All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 00/11] Add STM32 DFSDM support
@ 2017-03-17 14:08 ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Hello,

I kept it as RFC as i reworked the global design (but also because not full validated...).
Regarding Mark and Jonathan's comments, I decided to  try an implementation 
with DMA support in IIO instead of handling it in ASOC.
Solution seems more straightforward but as DMA cyclic mode and associated buffer
block API is not part of IIO today, I still need some hacks...
To be honest, I tried to integrate DMA cyclic management and associated buffer block 
Consumer API. but no enough expertise on IIO to success... :(

Advantage vs V2:
	- No more data processing need in ASoC , done in IIO
        - ASoC uses IIO consumer interface ( except for buffer callback)
Drawback :
	- Need to create a "SPI bus" trigger to allo to use Buffer consumer API
        	=> Need to find a solution to set this trigger by default in IIO audio driver
        - No standard buffer management
        	- Implement DMA cyclic in DFSDM IIO driver.
                - Define specific API to handle  buffer callback per period instead of 
                  callback per samples generated by iio_push_to_buffers_with_timestamp.
 
V2-V3 delta:
	- New patches to support ASoC DMA codec in DT 
  		- ASoC: Add Bindins for DMIC codec driver.
  		- ASoC: codec: add DT support in dmic codec.
        - New patches to allow in-kernel set of IIO buffer size and watermark   
  		- IIO: consumer: allow to set buffer sizes.
	- IIO DFSDM drivers
        	- Split audio and ADC support in 2 drivers.
                - Implement DMA cyclic mode.
                - Add SPI bus Trigger for buffer management.
                
	- IIO sigma delta adc drivers
        	-  Suppress "simple and rename driver.
         
         - ASoC driver
         	- Suppress DMA engine.
                - Suppress copy ops.
                - Use IIO consmumer interface to enable/Disable DFSDM.        

Regards,

Arnaud

Arnaud Pouliquen (11):
  iio: Add hardware consumer support
  IIO: Add DT bindings for sigma delta adc modulator
  IIO: ADC: add sigma delta modulator support
  IIO: add DT bindings for stm32 DFSDM filter
  IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  IIO: ADC: add stm32 DFSDM support for PDM microphone
  IIO: consumer: allow to set buffer sizes
  ASoC: Add Bindins for DMIC codec driver
  ASoC: codec: add DT support in dmic codec
  ASoC: add bindings for stm32 DFSDM filter
  ASoC: stm32: add DFSDM DAI support

 .../bindings/iio/adc/sigma-delta-modulator.txt     |  13 +
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 ++++
 Documentation/devicetree/bindings/sound/dmic.txt   |  11 +
 .../devicetree/bindings/sound/st,stm32-adfsdm.txt  |  41 ++
 drivers/iio/Kconfig                                |   6 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/adc/Kconfig                            |  50 ++
 drivers/iio/adc/Makefile                           |   4 +
 drivers/iio/adc/sd_adc_modulator.c                 |  98 +++
 drivers/iio/adc/stm32-dfsdm-adc.c                  | 419 ++++++++++++
 drivers/iio/adc/stm32-dfsdm-audio.c                | 720 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c                 | 658 +++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h                      | 372 +++++++++++
 drivers/iio/buffer/industrialio-buffer-cb.c        |  12 +
 drivers/iio/hw_consumer.c                          | 156 +++++
 include/linux/iio/adc/stm32-dfsdm-audio.h          |  41 ++
 include/linux/iio/consumer.h                       |  13 +
 include/linux/iio/hw_consumer.h                    |  12 +
 sound/soc/Kconfig                                  |   1 +
 sound/soc/Makefile                                 |   1 +
 sound/soc/codecs/dmic.c                            |   8 +
 sound/soc/stm/Kconfig                              |  10 +
 sound/soc/stm/Makefile                             |   2 +
 sound/soc/stm/stm32_adfsdm.c                       | 362 +++++++++++
 24 files changed, 3131 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
 create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
 create mode 100644 drivers/iio/adc/sd_adc_modulator.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm.h
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
 create mode 100644 include/linux/iio/hw_consumer.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

V2:

Patch-set associated to this RFC proposes an implementation of the
DFSDM features shared between ASoC and IIO frameworks.

Patch-set is only a Skeleton of the drivers, so a base to discuss and validate a design. 
It contains minimum code to allow probing (with DT) and to expose the ASoC and IIO ABI.
Hope that is sufficent in a first step to allow to focus on APIs.

In this patch-set there are two new APIs used:
	- IIO in-kern API: based on hw_customer API proposed by Lars
 	- ASOC <-> IIO API inspired by API defined for hdmi-codec for ASoC/DRM interconnect. 
   	  API is dedicated to DFSDM only.

Notice also that this design is based on following assumption:
	- Audio stream ABI interface is ASoC, no access to data through IIO ABI for PDM. 
	- ASoC DMA should be used for audio transfer as designed for real time stream.
	- Need some runtime parameters exchange between ASoC and IIO
	  due to the correlation between the sample frequency, the DFSDM decimation 
          factor and the associated scaling.

- "ASoC: dmaengine_pcm: add copy support" patch:
 I added a patch in ASoC that allows to implement a copy function to process data 
 after DMA transfer. Requested, as DFSDM samples captured contain channel ID 
 on 8-LSB bits and need also a potential rescale to present DAT on 24-bits.  	

- "IIO: ADC: add sigma delta modulator support" patch:
Simple dummy driver created to support external Sigma delta modulator. 
It is binded to DFSDM driver through hw_customer API.


- 
1.9.1

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

* [PATCH v3 00/11] Add STM32 DFSDM support
@ 2017-03-17 14:08 ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Hello,

I kept it as RFC as i reworked the global design (but also because not full validated...).
Regarding Mark and Jonathan's comments, I decided to  try an implementation 
with DMA support in IIO instead of handling it in ASOC.
Solution seems more straightforward but as DMA cyclic mode and associated buffer
block API is not part of IIO today, I still need some hacks...
To be honest, I tried to integrate DMA cyclic management and associated buffer block 
Consumer API. but no enough expertise on IIO to success... :(

Advantage vs V2:
	- No more data processing need in ASoC , done in IIO
        - ASoC uses IIO consumer interface ( except for buffer callback)
Drawback :
	- Need to create a "SPI bus" trigger to allo to use Buffer consumer API
        	=> Need to find a solution to set this trigger by default in IIO audio driver
        - No standard buffer management
        	- Implement DMA cyclic in DFSDM IIO driver.
                - Define specific API to handle  buffer callback per period instead of 
                  callback per samples generated by iio_push_to_buffers_with_timestamp.
 
V2-V3 delta:
	- New patches to support ASoC DMA codec in DT 
  		- ASoC: Add Bindins for DMIC codec driver.
  		- ASoC: codec: add DT support in dmic codec.
        - New patches to allow in-kernel set of IIO buffer size and watermark   
  		- IIO: consumer: allow to set buffer sizes.
	- IIO DFSDM drivers
        	- Split audio and ADC support in 2 drivers.
                - Implement DMA cyclic mode.
                - Add SPI bus Trigger for buffer management.
                
	- IIO sigma delta adc drivers
        	-  Suppress "simple and rename driver.
         
         - ASoC driver
         	- Suppress DMA engine.
                - Suppress copy ops.
                - Use IIO consmumer interface to enable/Disable DFSDM.        

Regards,

Arnaud

Arnaud Pouliquen (11):
  iio: Add hardware consumer support
  IIO: Add DT bindings for sigma delta adc modulator
  IIO: ADC: add sigma delta modulator support
  IIO: add DT bindings for stm32 DFSDM filter
  IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  IIO: ADC: add stm32 DFSDM support for PDM microphone
  IIO: consumer: allow to set buffer sizes
  ASoC: Add Bindins for DMIC codec driver
  ASoC: codec: add DT support in dmic codec
  ASoC: add bindings for stm32 DFSDM filter
  ASoC: stm32: add DFSDM DAI support

 .../bindings/iio/adc/sigma-delta-modulator.txt     |  13 +
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 ++++
 Documentation/devicetree/bindings/sound/dmic.txt   |  11 +
 .../devicetree/bindings/sound/st,stm32-adfsdm.txt  |  41 ++
 drivers/iio/Kconfig                                |   6 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/adc/Kconfig                            |  50 ++
 drivers/iio/adc/Makefile                           |   4 +
 drivers/iio/adc/sd_adc_modulator.c                 |  98 +++
 drivers/iio/adc/stm32-dfsdm-adc.c                  | 419 ++++++++++++
 drivers/iio/adc/stm32-dfsdm-audio.c                | 720 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c                 | 658 +++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h                      | 372 +++++++++++
 drivers/iio/buffer/industrialio-buffer-cb.c        |  12 +
 drivers/iio/hw_consumer.c                          | 156 +++++
 include/linux/iio/adc/stm32-dfsdm-audio.h          |  41 ++
 include/linux/iio/consumer.h                       |  13 +
 include/linux/iio/hw_consumer.h                    |  12 +
 sound/soc/Kconfig                                  |   1 +
 sound/soc/Makefile                                 |   1 +
 sound/soc/codecs/dmic.c                            |   8 +
 sound/soc/stm/Kconfig                              |  10 +
 sound/soc/stm/Makefile                             |   2 +
 sound/soc/stm/stm32_adfsdm.c                       | 362 +++++++++++
 24 files changed, 3131 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
 create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
 create mode 100644 drivers/iio/adc/sd_adc_modulator.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm.h
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
 create mode 100644 include/linux/iio/hw_consumer.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

V2:

Patch-set associated to this RFC proposes an implementation of the
DFSDM features shared between ASoC and IIO frameworks.

Patch-set is only a Skeleton of the drivers, so a base to discuss and validate a design. 
It contains minimum code to allow probing (with DT) and to expose the ASoC and IIO ABI.
Hope that is sufficent in a first step to allow to focus on APIs.

In this patch-set there are two new APIs used:
	- IIO in-kern API: based on hw_customer API proposed by Lars
 	- ASOC <-> IIO API inspired by API defined for hdmi-codec for ASoC/DRM interconnect. 
   	  API is dedicated to DFSDM only.

Notice also that this design is based on following assumption:
	- Audio stream ABI interface is ASoC, no access to data through IIO ABI for PDM. 
	- ASoC DMA should be used for audio transfer as designed for real time stream.
	- Need some runtime parameters exchange between ASoC and IIO
	  due to the correlation between the sample frequency, the DFSDM decimation 
          factor and the associated scaling.

- "ASoC: dmaengine_pcm: add copy support" patch:
 I added a patch in ASoC that allows to implement a copy function to process data 
 after DMA transfer. Requested, as DFSDM samples captured contain channel ID 
 on 8-LSB bits and need also a potential rescale to present DAT on 24-bits.  	

- "IIO: ADC: add sigma delta modulator support" patch:
Simple dummy driver created to support external Sigma delta modulator. 
It is binded to DFSDM driver through hw_customer API.


- 
1.9.1


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

* [PATCH v3 00/11] Add STM32 DFSDM support
@ 2017-03-17 14:08 ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

I kept it as RFC as i reworked the global design (but also because not full validated...).
Regarding Mark and Jonathan's comments, I decided to  try an implementation 
with DMA support in IIO instead of handling it in ASOC.
Solution seems more straightforward but as DMA cyclic mode and associated buffer
block API is not part of IIO today, I still need some hacks...
To be honest, I tried to integrate DMA cyclic management and associated buffer block 
Consumer API. but no enough expertise on IIO to success... :(

Advantage vs V2:
	- No more data processing need in ASoC , done in IIO
        - ASoC uses IIO consumer interface ( except for buffer callback)
Drawback :
	- Need to create a "SPI bus" trigger to allo to use Buffer consumer API
        	=> Need to find a solution to set this trigger by default in IIO audio driver
        - No standard buffer management
        	- Implement DMA cyclic in DFSDM IIO driver.
                - Define specific API to handle  buffer callback per period instead of 
                  callback per samples generated by iio_push_to_buffers_with_timestamp.
 
V2-V3 delta:
	- New patches to support ASoC DMA codec in DT 
  		- ASoC: Add Bindins for DMIC codec driver.
  		- ASoC: codec: add DT support in dmic codec.
        - New patches to allow in-kernel set of IIO buffer size and watermark   
  		- IIO: consumer: allow to set buffer sizes.
	- IIO DFSDM drivers
        	- Split audio and ADC support in 2 drivers.
                - Implement DMA cyclic mode.
                - Add SPI bus Trigger for buffer management.
                
	- IIO sigma delta adc drivers
        	-  Suppress "simple and rename driver.
         
         - ASoC driver
         	- Suppress DMA engine.
                - Suppress copy ops.
                - Use IIO consmumer interface to enable/Disable DFSDM.        

Regards,

Arnaud

Arnaud Pouliquen (11):
  iio: Add hardware consumer support
  IIO: Add DT bindings for sigma delta adc modulator
  IIO: ADC: add sigma delta modulator support
  IIO: add DT bindings for stm32 DFSDM filter
  IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  IIO: ADC: add stm32 DFSDM support for PDM microphone
  IIO: consumer: allow to set buffer sizes
  ASoC: Add Bindins for DMIC codec driver
  ASoC: codec: add DT support in dmic codec
  ASoC: add bindings for stm32 DFSDM filter
  ASoC: stm32: add DFSDM DAI support

 .../bindings/iio/adc/sigma-delta-modulator.txt     |  13 +
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 ++++
 Documentation/devicetree/bindings/sound/dmic.txt   |  11 +
 .../devicetree/bindings/sound/st,stm32-adfsdm.txt  |  41 ++
 drivers/iio/Kconfig                                |   6 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/adc/Kconfig                            |  50 ++
 drivers/iio/adc/Makefile                           |   4 +
 drivers/iio/adc/sd_adc_modulator.c                 |  98 +++
 drivers/iio/adc/stm32-dfsdm-adc.c                  | 419 ++++++++++++
 drivers/iio/adc/stm32-dfsdm-audio.c                | 720 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c                 | 658 +++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h                      | 372 +++++++++++
 drivers/iio/buffer/industrialio-buffer-cb.c        |  12 +
 drivers/iio/hw_consumer.c                          | 156 +++++
 include/linux/iio/adc/stm32-dfsdm-audio.h          |  41 ++
 include/linux/iio/consumer.h                       |  13 +
 include/linux/iio/hw_consumer.h                    |  12 +
 sound/soc/Kconfig                                  |   1 +
 sound/soc/Makefile                                 |   1 +
 sound/soc/codecs/dmic.c                            |   8 +
 sound/soc/stm/Kconfig                              |  10 +
 sound/soc/stm/Makefile                             |   2 +
 sound/soc/stm/stm32_adfsdm.c                       | 362 +++++++++++
 24 files changed, 3131 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
 create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
 create mode 100644 drivers/iio/adc/sd_adc_modulator.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm.h
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
 create mode 100644 include/linux/iio/hw_consumer.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

V2:

Patch-set associated to this RFC proposes an implementation of the
DFSDM features shared between ASoC and IIO frameworks.

Patch-set is only a Skeleton of the drivers, so a base to discuss and validate a design. 
It contains minimum code to allow probing (with DT) and to expose the ASoC and IIO ABI.
Hope that is sufficent in a first step to allow to focus on APIs.

In this patch-set there are two new APIs used:
	- IIO in-kern API: based on hw_customer API proposed by Lars
 	- ASOC <-> IIO API inspired by API defined for hdmi-codec for ASoC/DRM interconnect. 
   	  API is dedicated to DFSDM only.

Notice also that this design is based on following assumption:
	- Audio stream ABI interface is ASoC, no access to data through IIO ABI for PDM. 
	- ASoC DMA should be used for audio transfer as designed for real time stream.
	- Need some runtime parameters exchange between ASoC and IIO
	  due to the correlation between the sample frequency, the DFSDM decimation 
          factor and the associated scaling.

- "ASoC: dmaengine_pcm: add copy support" patch:
 I added a patch in ASoC that allows to implement a copy function to process data 
 after DMA transfer. Requested, as DFSDM samples captured contain channel ID 
 on 8-LSB bits and need also a potential rescale to present DAT on 24-bits.  	

- "IIO: ADC: add sigma delta modulator support" patch:
Simple dummy driver created to support external Sigma delta modulator. 
It is binded to DFSDM driver through hw_customer API.


- 
1.9.1

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

* [PATCH v3 01/11] iio: Add hardware consumer support
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

From: Lars-Peter Clausen <lars@metafoo.de>

Hardware consumer's can be used when one IIO device has a direct connection
to another device in hardware.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3:
	 Just Adding sign off.  No update but Jonathan comment.
         Need to be taken into account (in agreement with Lars)
         when transforming rfc in patch.
---
 drivers/iio/Kconfig             |   6 ++
 drivers/iio/Makefile            |   1 +
 drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/hw_consumer.h |  12 ++++
 4 files changed, 175 insertions(+)
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/hw_consumer.h

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6743b18..956dd18 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -30,6 +30,12 @@ config IIO_CONFIGFS
 	  (e.g. software triggers). For more info see
 	  Documentation/iio/iio_configfs.txt.
 
+config IIO_HW_CONSUMER
+	tristate
+	help
+	  Hardware consumer buffer
+
+
 config IIO_TRIGGER
 	bool "Enable triggered sampling support"
 	help
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 87e4c43..8472b97 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
 industrialio-y := industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
+obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
 
 obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
 obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
new file mode 100644
index 0000000..66f0732
--- /dev/null
+++ b/drivers/iio/hw_consumer.c
@@ -0,0 +1,156 @@
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include "iio_core.h"
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/buffer.h>
+
+struct iio_hw_consumer {
+	struct list_head buffers;
+	struct iio_channel *channels;
+};
+
+struct hw_consumer_buffer {
+	struct list_head head;
+	struct iio_dev *indio_dev;
+	struct iio_buffer buffer;
+};
+
+static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
+	struct iio_buffer *buffer)
+{
+	return container_of(buffer, struct hw_consumer_buffer, buffer);
+}
+
+static void iio_hw_buf_release(struct iio_buffer *buffer)
+{
+	struct hw_consumer_buffer *hw_buf =
+		iio_buffer_to_hw_consumer_buffer(buffer);
+	kfree(hw_buf->buffer.scan_mask);
+	kfree(hw_buf);
+}
+
+static const struct iio_buffer_access_funcs iio_hw_buf_access = {
+	.release = &iio_hw_buf_release,
+	.modes = INDIO_BUFFER_HARDWARE,
+};
+
+static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
+	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
+{
+	struct hw_consumer_buffer *buf;
+
+	list_for_each_entry(buf, &hwc->buffers, head) {
+		if (buf->indio_dev == indio_dev)
+			return buf;
+	}
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	buf->buffer.access = &iio_hw_buf_access;
+	buf->indio_dev = indio_dev;
+	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
+		sizeof(long), GFP_KERNEL);
+	if (!buf->buffer.scan_mask)
+		goto err_free_buf;
+
+	iio_buffer_init(&buf->buffer);
+	list_add_tail(&buf->head, &hwc->buffers);
+
+	return buf;
+
+err_free_buf:
+	kfree(buf);
+	return NULL;
+}
+
+struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
+{
+	struct hw_consumer_buffer *buf;
+	struct iio_hw_consumer *hwc;
+	struct iio_channel *chan;
+	int ret;
+
+	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
+	if (!hwc)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&hwc->buffers);
+
+	hwc->channels = iio_channel_get_all(dev);
+	if (IS_ERR(hwc->channels)) {
+		ret = PTR_ERR(hwc->channels);
+		goto err_free_hwc;
+	}
+
+	chan = &hwc->channels[0];
+	while (chan->indio_dev) {
+		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
+		if (buf == NULL) {
+			ret = -ENOMEM;
+			goto err_put_buffers;
+		}
+		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
+		chan++;
+	}
+
+	return hwc;
+
+err_put_buffers:
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_buffer_put(&buf->buffer);
+	iio_channel_release_all(hwc->channels);
+err_free_hwc:
+	kfree(hwc);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
+
+void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+
+	iio_channel_release_all(hwc->channels);
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_buffer_put(&buf->buffer);
+	kfree(hwc);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
+
+int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+	int ret;
+
+	list_for_each_entry(buf, &hwc->buffers, head) {
+		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
+		if (ret)
+			goto err_disable_buffers;
+	}
+
+	return 0;
+
+err_disable_buffers:
+	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
+		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
+
+void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
new file mode 100644
index 0000000..f12653d
--- /dev/null
+++ b/include/linux/iio/hw_consumer.h
@@ -0,0 +1,12 @@
+#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
+#define LINUX_IIO_HW_CONSUMER_BUFFER_H
+
+struct device;
+struct iio_hw_consumer;
+
+struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
+void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
+int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
+void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
+
+#endif
-- 
1.9.1

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

* [PATCH v3 01/11] iio: Add hardware consumer support
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

From: Lars-Peter Clausen <lars@metafoo.de>

Hardware consumer's can be used when one IIO device has a direct connection
to another device in hardware.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3:
	 Just Adding sign off.  No update but Jonathan comment.
         Need to be taken into account (in agreement with Lars)
         when transforming rfc in patch.
---
 drivers/iio/Kconfig             |   6 ++
 drivers/iio/Makefile            |   1 +
 drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/hw_consumer.h |  12 ++++
 4 files changed, 175 insertions(+)
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/hw_consumer.h

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6743b18..956dd18 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -30,6 +30,12 @@ config IIO_CONFIGFS
 	  (e.g. software triggers). For more info see
 	  Documentation/iio/iio_configfs.txt.
 
+config IIO_HW_CONSUMER
+	tristate
+	help
+	  Hardware consumer buffer
+
+
 config IIO_TRIGGER
 	bool "Enable triggered sampling support"
 	help
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 87e4c43..8472b97 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
 industrialio-y := industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
+obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
 
 obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
 obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
new file mode 100644
index 0000000..66f0732
--- /dev/null
+++ b/drivers/iio/hw_consumer.c
@@ -0,0 +1,156 @@
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include "iio_core.h"
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/buffer.h>
+
+struct iio_hw_consumer {
+	struct list_head buffers;
+	struct iio_channel *channels;
+};
+
+struct hw_consumer_buffer {
+	struct list_head head;
+	struct iio_dev *indio_dev;
+	struct iio_buffer buffer;
+};
+
+static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
+	struct iio_buffer *buffer)
+{
+	return container_of(buffer, struct hw_consumer_buffer, buffer);
+}
+
+static void iio_hw_buf_release(struct iio_buffer *buffer)
+{
+	struct hw_consumer_buffer *hw_buf =
+		iio_buffer_to_hw_consumer_buffer(buffer);
+	kfree(hw_buf->buffer.scan_mask);
+	kfree(hw_buf);
+}
+
+static const struct iio_buffer_access_funcs iio_hw_buf_access = {
+	.release = &iio_hw_buf_release,
+	.modes = INDIO_BUFFER_HARDWARE,
+};
+
+static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
+	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
+{
+	struct hw_consumer_buffer *buf;
+
+	list_for_each_entry(buf, &hwc->buffers, head) {
+		if (buf->indio_dev == indio_dev)
+			return buf;
+	}
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	buf->buffer.access = &iio_hw_buf_access;
+	buf->indio_dev = indio_dev;
+	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
+		sizeof(long), GFP_KERNEL);
+	if (!buf->buffer.scan_mask)
+		goto err_free_buf;
+
+	iio_buffer_init(&buf->buffer);
+	list_add_tail(&buf->head, &hwc->buffers);
+
+	return buf;
+
+err_free_buf:
+	kfree(buf);
+	return NULL;
+}
+
+struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
+{
+	struct hw_consumer_buffer *buf;
+	struct iio_hw_consumer *hwc;
+	struct iio_channel *chan;
+	int ret;
+
+	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
+	if (!hwc)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&hwc->buffers);
+
+	hwc->channels = iio_channel_get_all(dev);
+	if (IS_ERR(hwc->channels)) {
+		ret = PTR_ERR(hwc->channels);
+		goto err_free_hwc;
+	}
+
+	chan = &hwc->channels[0];
+	while (chan->indio_dev) {
+		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
+		if (buf == NULL) {
+			ret = -ENOMEM;
+			goto err_put_buffers;
+		}
+		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
+		chan++;
+	}
+
+	return hwc;
+
+err_put_buffers:
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_buffer_put(&buf->buffer);
+	iio_channel_release_all(hwc->channels);
+err_free_hwc:
+	kfree(hwc);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
+
+void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+
+	iio_channel_release_all(hwc->channels);
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_buffer_put(&buf->buffer);
+	kfree(hwc);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
+
+int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+	int ret;
+
+	list_for_each_entry(buf, &hwc->buffers, head) {
+		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
+		if (ret)
+			goto err_disable_buffers;
+	}
+
+	return 0;
+
+err_disable_buffers:
+	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
+		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
+
+void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
new file mode 100644
index 0000000..f12653d
--- /dev/null
+++ b/include/linux/iio/hw_consumer.h
@@ -0,0 +1,12 @@
+#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
+#define LINUX_IIO_HW_CONSUMER_BUFFER_H
+
+struct device;
+struct iio_hw_consumer;
+
+struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
+void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
+int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
+void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
+
+#endif
-- 
1.9.1


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

* [PATCH v3 01/11] iio: Add hardware consumer support
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

From: Lars-Peter Clausen <lars@metafoo.de>

Hardware consumer's can be used when one IIO device has a direct connection
to another device in hardware.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3:
	 Just Adding sign off.  No update but Jonathan comment.
         Need to be taken into account (in agreement with Lars)
         when transforming rfc in patch.
---
 drivers/iio/Kconfig             |   6 ++
 drivers/iio/Makefile            |   1 +
 drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
 include/linux/iio/hw_consumer.h |  12 ++++
 4 files changed, 175 insertions(+)
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/hw_consumer.h

diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6743b18..956dd18 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -30,6 +30,12 @@ config IIO_CONFIGFS
 	  (e.g. software triggers). For more info see
 	  Documentation/iio/iio_configfs.txt.
 
+config IIO_HW_CONSUMER
+	tristate
+	help
+	  Hardware consumer buffer
+
+
 config IIO_TRIGGER
 	bool "Enable triggered sampling support"
 	help
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 87e4c43..8472b97 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
 industrialio-y := industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
+obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
 
 obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
 obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
new file mode 100644
index 0000000..66f0732
--- /dev/null
+++ b/drivers/iio/hw_consumer.c
@@ -0,0 +1,156 @@
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include "iio_core.h"
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/buffer.h>
+
+struct iio_hw_consumer {
+	struct list_head buffers;
+	struct iio_channel *channels;
+};
+
+struct hw_consumer_buffer {
+	struct list_head head;
+	struct iio_dev *indio_dev;
+	struct iio_buffer buffer;
+};
+
+static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
+	struct iio_buffer *buffer)
+{
+	return container_of(buffer, struct hw_consumer_buffer, buffer);
+}
+
+static void iio_hw_buf_release(struct iio_buffer *buffer)
+{
+	struct hw_consumer_buffer *hw_buf =
+		iio_buffer_to_hw_consumer_buffer(buffer);
+	kfree(hw_buf->buffer.scan_mask);
+	kfree(hw_buf);
+}
+
+static const struct iio_buffer_access_funcs iio_hw_buf_access = {
+	.release = &iio_hw_buf_release,
+	.modes = INDIO_BUFFER_HARDWARE,
+};
+
+static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
+	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
+{
+	struct hw_consumer_buffer *buf;
+
+	list_for_each_entry(buf, &hwc->buffers, head) {
+		if (buf->indio_dev == indio_dev)
+			return buf;
+	}
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	buf->buffer.access = &iio_hw_buf_access;
+	buf->indio_dev = indio_dev;
+	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
+		sizeof(long), GFP_KERNEL);
+	if (!buf->buffer.scan_mask)
+		goto err_free_buf;
+
+	iio_buffer_init(&buf->buffer);
+	list_add_tail(&buf->head, &hwc->buffers);
+
+	return buf;
+
+err_free_buf:
+	kfree(buf);
+	return NULL;
+}
+
+struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
+{
+	struct hw_consumer_buffer *buf;
+	struct iio_hw_consumer *hwc;
+	struct iio_channel *chan;
+	int ret;
+
+	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
+	if (!hwc)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&hwc->buffers);
+
+	hwc->channels = iio_channel_get_all(dev);
+	if (IS_ERR(hwc->channels)) {
+		ret = PTR_ERR(hwc->channels);
+		goto err_free_hwc;
+	}
+
+	chan = &hwc->channels[0];
+	while (chan->indio_dev) {
+		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
+		if (buf == NULL) {
+			ret = -ENOMEM;
+			goto err_put_buffers;
+		}
+		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
+		chan++;
+	}
+
+	return hwc;
+
+err_put_buffers:
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_buffer_put(&buf->buffer);
+	iio_channel_release_all(hwc->channels);
+err_free_hwc:
+	kfree(hwc);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
+
+void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+
+	iio_channel_release_all(hwc->channels);
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_buffer_put(&buf->buffer);
+	kfree(hwc);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
+
+int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+	int ret;
+
+	list_for_each_entry(buf, &hwc->buffers, head) {
+		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
+		if (ret)
+			goto err_disable_buffers;
+	}
+
+	return 0;
+
+err_disable_buffers:
+	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
+		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
+
+void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
+{
+	struct hw_consumer_buffer *buf;
+
+	list_for_each_entry(buf, &hwc->buffers, head)
+		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
+}
+EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
new file mode 100644
index 0000000..f12653d
--- /dev/null
+++ b/include/linux/iio/hw_consumer.h
@@ -0,0 +1,12 @@
+#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
+#define LINUX_IIO_HW_CONSUMER_BUFFER_H
+
+struct device;
+struct iio_hw_consumer;
+
+struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
+void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
+int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
+void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
+
+#endif
-- 
1.9.1

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

* [PATCH v3 02/11] IIO: Add DT bindings for sigma delta adc modulator
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add documentation of device tree bindings to support
sigma delta modulator in IIO framework.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	-Rename to suppress "simple"
        - add "ads1201" compatibility
 
 .../devicetree/bindings/iio/adc/sigma-delta-modulator.txt   | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
new file mode 100644
index 0000000..27f04a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
@@ -0,0 +1,13 @@
+Device-Tree bindings for sigma delta modulator
+
+Required properties:
+- compatible: should be "ads1201", "sd-modulator". "sd-modulator" can be use
+	as a generic SD modulator if modulator not specified in compatible list.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+
+Example node:
+
+	ads1202: simple_sd_adc@0 {
+		compatible = "sd-modulator";
+		#io-channel-cells = <1>;
+	};
-- 
1.9.1

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

* [PATCH v3 02/11] IIO: Add DT bindings for sigma delta adc modulator
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add documentation of device tree bindings to support
sigma delta modulator in IIO framework.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	-Rename to suppress "simple"
        - add "ads1201" compatibility
 
 .../devicetree/bindings/iio/adc/sigma-delta-modulator.txt   | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
new file mode 100644
index 0000000..27f04a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
@@ -0,0 +1,13 @@
+Device-Tree bindings for sigma delta modulator
+
+Required properties:
+- compatible: should be "ads1201", "sd-modulator". "sd-modulator" can be use
+	as a generic SD modulator if modulator not specified in compatible list.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+
+Example node:
+
+	ads1202: simple_sd_adc@0 {
+		compatible = "sd-modulator";
+		#io-channel-cells = <1>;
+	};
-- 
1.9.1


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

* [PATCH v3 02/11] IIO: Add DT bindings for sigma delta adc modulator
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add documentation of device tree bindings to support
sigma delta modulator in IIO framework.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	-Rename to suppress "simple"
        - add "ads1201" compatibility
 
 .../devicetree/bindings/iio/adc/sigma-delta-modulator.txt   | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
new file mode 100644
index 0000000..27f04a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
@@ -0,0 +1,13 @@
+Device-Tree bindings for sigma delta modulator
+
+Required properties:
+- compatible: should be "ads1201", "sd-modulator". "sd-modulator" can be use
+	as a generic SD modulator if modulator not specified in compatible list.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+
+Example node:
+
+	ads1202: simple_sd_adc at 0 {
+		compatible = "sd-modulator";
+		#io-channel-cells = <1>;
+	};
-- 
1.9.1

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

* [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add generic driver to support sigma delta modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	-Rename to suppress "simple"
        - add "ads1201" compatibility
 
 drivers/iio/adc/Kconfig            | 11 +++++
 drivers/iio/adc/Makefile           |  1 +
 drivers/iio/adc/sd_adc_modulator.c | 98 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 110 insertions(+)
 create mode 100644 drivers/iio/adc/sd_adc_modulator.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..d411d66 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -419,6 +419,17 @@ config ROCKCHIP_SARADC
 	  To compile this driver as a module, choose M here: the
 	  module will be called rockchip_saradc.
 
+config SD_ADC_MODULATOR
+	tristate "Basic sigma delta modulator"
+	depends on OF
+        select IIO_BUFFER
+        select IIO_TRIGGERED_BUFFER
+	help
+	  Select this option to enables generic sigma delta modulator.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called simple-sd-adc.
+
 config STM32_ADC_CORE
 	tristate "STMicroelectronics STM32 adc core"
 	depends on ARCH_STM32 || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8e02a94..c68819c 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o
 obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
 xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
 obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
+obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
new file mode 100644
index 0000000..4a25642
--- /dev/null
+++ b/drivers/iio/adc/sd_adc_modulator.c
@@ -0,0 +1,98 @@
+/*
+ * Basic sigma delta modulator 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/iio/iio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <linux/iio/triggered_buffer.h>
+
+static int iio_sd_mod_of_xlate(struct iio_dev *iio,
+			       const struct of_phandle_args *iiospec)
+{
+	dev_dbg(&iio->dev, "%s:\n", __func__);
+	if (iiospec->args[0] != 0) {
+		dev_err(&iio->dev, "Only one channel supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info iio_sd_mod_iio_info = {
+	.of_xlate = iio_sd_mod_of_xlate,
+};
+
+static const struct iio_chan_spec stm32_dfsdm_ch = {
+	.type = IIO_VOLTAGE,
+	.indexed = 1,
+	.scan_index = 0,
+	.scan_type = {
+		.sign = 'u',
+		.realbits = 1,
+		.shift = 0,
+	},
+};
+
+static int iio_sd_mod_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *iio;
+
+	dev_dbg(&pdev->dev, "%s:\n", __func__);
+	iio = devm_iio_device_alloc(dev, 0);
+	if (!iio)
+		return -ENOMEM;
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = dev->of_node;
+	iio->name = dev_name(dev);
+	iio->info = &iio_sd_mod_iio_info;
+	iio->modes = INDIO_BUFFER_HARDWARE;
+
+	iio->num_channels = 1;
+	iio->channels = &stm32_dfsdm_ch;
+
+	platform_set_drvdata(pdev, iio);
+
+	return devm_iio_device_register(&pdev->dev, iio);
+}
+
+static const struct of_device_id sd_adc_of_match[] = {
+	{ .compatible = "sd-modulator" },
+	{ .compatible = "ads1201" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adc081c_of_match);
+
+static struct platform_driver iio_sd_mod_adc = {
+	.driver = {
+		.name = "iio_sd_adc_mod",
+		.of_match_table = of_match_ptr(sd_adc_of_match),
+	},
+	.probe = iio_sd_mod_probe,
+};
+
+module_platform_driver(iio_sd_mod_adc);
+
+MODULE_DESCRIPTION("Basic sigma delta modulator");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add generic driver to support sigma delta modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	-Rename to suppress "simple"
        - add "ads1201" compatibility
 
 drivers/iio/adc/Kconfig            | 11 +++++
 drivers/iio/adc/Makefile           |  1 +
 drivers/iio/adc/sd_adc_modulator.c | 98 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 110 insertions(+)
 create mode 100644 drivers/iio/adc/sd_adc_modulator.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..d411d66 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -419,6 +419,17 @@ config ROCKCHIP_SARADC
 	  To compile this driver as a module, choose M here: the
 	  module will be called rockchip_saradc.
 
+config SD_ADC_MODULATOR
+	tristate "Basic sigma delta modulator"
+	depends on OF
+        select IIO_BUFFER
+        select IIO_TRIGGERED_BUFFER
+	help
+	  Select this option to enables generic sigma delta modulator.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called simple-sd-adc.
+
 config STM32_ADC_CORE
 	tristate "STMicroelectronics STM32 adc core"
 	depends on ARCH_STM32 || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8e02a94..c68819c 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o
 obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
 xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
 obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
+obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
new file mode 100644
index 0000000..4a25642
--- /dev/null
+++ b/drivers/iio/adc/sd_adc_modulator.c
@@ -0,0 +1,98 @@
+/*
+ * Basic sigma delta modulator 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/iio/iio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <linux/iio/triggered_buffer.h>
+
+static int iio_sd_mod_of_xlate(struct iio_dev *iio,
+			       const struct of_phandle_args *iiospec)
+{
+	dev_dbg(&iio->dev, "%s:\n", __func__);
+	if (iiospec->args[0] != 0) {
+		dev_err(&iio->dev, "Only one channel supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info iio_sd_mod_iio_info = {
+	.of_xlate = iio_sd_mod_of_xlate,
+};
+
+static const struct iio_chan_spec stm32_dfsdm_ch = {
+	.type = IIO_VOLTAGE,
+	.indexed = 1,
+	.scan_index = 0,
+	.scan_type = {
+		.sign = 'u',
+		.realbits = 1,
+		.shift = 0,
+	},
+};
+
+static int iio_sd_mod_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *iio;
+
+	dev_dbg(&pdev->dev, "%s:\n", __func__);
+	iio = devm_iio_device_alloc(dev, 0);
+	if (!iio)
+		return -ENOMEM;
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = dev->of_node;
+	iio->name = dev_name(dev);
+	iio->info = &iio_sd_mod_iio_info;
+	iio->modes = INDIO_BUFFER_HARDWARE;
+
+	iio->num_channels = 1;
+	iio->channels = &stm32_dfsdm_ch;
+
+	platform_set_drvdata(pdev, iio);
+
+	return devm_iio_device_register(&pdev->dev, iio);
+}
+
+static const struct of_device_id sd_adc_of_match[] = {
+	{ .compatible = "sd-modulator" },
+	{ .compatible = "ads1201" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adc081c_of_match);
+
+static struct platform_driver iio_sd_mod_adc = {
+	.driver = {
+		.name = "iio_sd_adc_mod",
+		.of_match_table = of_match_ptr(sd_adc_of_match),
+	},
+	.probe = iio_sd_mod_probe,
+};
+
+module_platform_driver(iio_sd_mod_adc);
+
+MODULE_DESCRIPTION("Basic sigma delta modulator");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


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

* [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add generic driver to support sigma delta modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	-Rename to suppress "simple"
        - add "ads1201" compatibility
 
 drivers/iio/adc/Kconfig            | 11 +++++
 drivers/iio/adc/Makefile           |  1 +
 drivers/iio/adc/sd_adc_modulator.c | 98 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 110 insertions(+)
 create mode 100644 drivers/iio/adc/sd_adc_modulator.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..d411d66 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -419,6 +419,17 @@ config ROCKCHIP_SARADC
 	  To compile this driver as a module, choose M here: the
 	  module will be called rockchip_saradc.
 
+config SD_ADC_MODULATOR
+	tristate "Basic sigma delta modulator"
+	depends on OF
+        select IIO_BUFFER
+        select IIO_TRIGGERED_BUFFER
+	help
+	  Select this option to enables generic sigma delta modulator.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called simple-sd-adc.
+
 config STM32_ADC_CORE
 	tristate "STMicroelectronics STM32 adc core"
 	depends on ARCH_STM32 || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8e02a94..c68819c 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o
 obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
 xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
 obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
+obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
new file mode 100644
index 0000000..4a25642
--- /dev/null
+++ b/drivers/iio/adc/sd_adc_modulator.c
@@ -0,0 +1,98 @@
+/*
+ * Basic sigma delta modulator 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/iio/iio.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <linux/iio/triggered_buffer.h>
+
+static int iio_sd_mod_of_xlate(struct iio_dev *iio,
+			       const struct of_phandle_args *iiospec)
+{
+	dev_dbg(&iio->dev, "%s:\n", __func__);
+	if (iiospec->args[0] != 0) {
+		dev_err(&iio->dev, "Only one channel supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info iio_sd_mod_iio_info = {
+	.of_xlate = iio_sd_mod_of_xlate,
+};
+
+static const struct iio_chan_spec stm32_dfsdm_ch = {
+	.type = IIO_VOLTAGE,
+	.indexed = 1,
+	.scan_index = 0,
+	.scan_type = {
+		.sign = 'u',
+		.realbits = 1,
+		.shift = 0,
+	},
+};
+
+static int iio_sd_mod_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *iio;
+
+	dev_dbg(&pdev->dev, "%s:\n", __func__);
+	iio = devm_iio_device_alloc(dev, 0);
+	if (!iio)
+		return -ENOMEM;
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = dev->of_node;
+	iio->name = dev_name(dev);
+	iio->info = &iio_sd_mod_iio_info;
+	iio->modes = INDIO_BUFFER_HARDWARE;
+
+	iio->num_channels = 1;
+	iio->channels = &stm32_dfsdm_ch;
+
+	platform_set_drvdata(pdev, iio);
+
+	return devm_iio_device_register(&pdev->dev, iio);
+}
+
+static const struct of_device_id sd_adc_of_match[] = {
+	{ .compatible = "sd-modulator" },
+	{ .compatible = "ads1201" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adc081c_of_match);
+
+static struct platform_driver iio_sd_mod_adc = {
+	.driver = {
+		.name = "iio_sd_adc_mod",
+		.of_match_table = of_match_ptr(sd_adc_of_match),
+	},
+	.probe = iio_sd_mod_probe,
+};
+
+module_platform_driver(iio_sd_mod_adc);
+
+MODULE_DESCRIPTION("Basic sigma delta modulator");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* [PATCH v3 04/11] IIO: add DT bindings for stm32 DFSDM filter
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add bindings that describes Digital Filter for Sigma Delta
Modulators. DFSDM allows to connect sigma delta
modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
   Fixes based on V2 comments

 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 +++++++++++++++++++++
 1 file changed, 120 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..d7828bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
@@ -0,0 +1,120 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicated to
+interface external sigma delta modulators to STM32 micro controllers.
+It is mainly targeted for:
+- Sigma delta modulators (motor control, metering...)
+- PDM microphones (audio digital microphone)
+
+It features up to 8 serial digital interfaces (SPI or Manchester) and
+up to 4 filters.
+
+Each instance of the sub-drivers uses one filter instance.
+
+Contents of a STM32 DFSDM root node:
+------------------------------------
+Required properties:
+- compatible: Should be "st,stm32-dfsdm".
+- reg: Offset and length of the DFSDM block register set.
+- clocks: IP and serial interfaces clocking. Should be set according
+		to rcc clock ID and "clock-names".
+- clock-names: Input clock name "dfsdm" must be defined,
+		"audio" is optional. If defined CLKOUT is based on the audio
+		clock, else "dfsdm" is used.
+
+Optional properties:
+- spi-max-frequency: Requested only for SPI master mode.
+		  SPI clock OUT frequency (Hz). This clock must be set according
+		  to "clock" property. Frequency must be a multiple of the rcc
+		  clock frequency. If not, SPI CLKOUT frequency will not be
+		  accurate.
+
+Contents of a STM32 DFSDM child nodes:
+--------------------------------------
+
+Required properties:
+- compatible: Must be:
+	"st,stm32-dfsdm-adc" for sigma delta ADCs
+	"st,stm32-dfsdm-audio" for audio digital microphone.
+- reg: Specifies the DFSDM filter instance used.
+- interrupts: IRQ lines connected to each DFSDM filter instance.
+- st,adc-channels:	List of single-ended channels muxed for this ADC.
+- st,adc-channel-names:	List of single-ended channel names.
+- st,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.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+
+Required properties for "st,stm32-dfsdm-adc" compatibility:
+- io-channels: From common IIO binding. Used to pipe external sigma delta
+		modulator or internal ADC output to DFSDM channel.
+		This is not required for "st,stm32-dfsdm-pdm" compatibility as
+		PDM microphone is binded in Audio DT node.
+
+Required properties for "st,stm32-dfsdm-pdm" compatibility:
+- #sound-dai-cells: Must be set to 0.
+- dma: DMA controller phandle and DMA request line associated to the
+		filter instance (specified by the field "reg")
+- dma-names: Must be "rx"
+
+Optional properties:
+- st,adc-channel-types:	Single-ended channel input type.
+			- "SPI_R": SPI with data on rising edge (default)
+			- "SPI_F": SPI with data on falling edge
+			- "MANCH_R": manchester codec, rising edge = logic 0
+			- "MANCH_F": manchester codec, falling edge = logic 1
+- st,adc-channel-clk-src: Conversion clock source.
+			  - "CLKIN": external SPI clock (CLKIN x)
+			  - "CLKOUT": internal SPI clock (CLKOUT) (default)
+			  - "CLKOUT_F": internal SPI clock divided by 2 (falling edge).
+			  - "CLKOUT_R": internal SPI clock divided by 2 (rising edge).
+
+- st,adc-alt-channel: Must be defined if two sigma delta modulator 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.
+
+- st,filter0-sync: Set to 1 to synchronize with DFSDM filter instance 0.
+		   Used for multi microphones synchronization.
+
+Example of a sigma delta adc connected on DFSDM SPI port 0
+and a pdm microphone connected on DFSDM SPI port 1:
+
+	ads1202: simple_sd_adc@0 {
+		compatible = "ads1202";
+		#io-channel-cells = <1>;
+	};
+
+	dfsdm: dfsdm@40017000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm";
+
+		dfsdm_adc0: dfsdm-adc0@0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			interrupts = <110>;
+			st,adc-channels = <0>;
+			st,adc-channel-names = "sd_adc0";
+			st,adc-channel-types = "SPI_F";
+			st,adc-channel-clk-src = "CLKOUT";
+			io-channels = <&ads1202 0>;
+			st,filter-order = <3>;
+
+		dfsdm_pdm1: dfsdm-pdm@0 {
+			compatible = "st,stm32-dfsdm-pdm";
+			reg = <1>;
+			interrupts = <111>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,adc-channels = <1>;
+			st,adc-channel-names = "pdm1";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+			st,filter-order = <5>;
+		};
+	}
-- 
1.9.1

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

* [PATCH v3 04/11] IIO: add DT bindings for stm32 DFSDM filter
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add bindings that describes Digital Filter for Sigma Delta
Modulators. DFSDM allows to connect sigma delta
modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
   Fixes based on V2 comments

 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 +++++++++++++++++++++
 1 file changed, 120 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..d7828bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
@@ -0,0 +1,120 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicated to
+interface external sigma delta modulators to STM32 micro controllers.
+It is mainly targeted for:
+- Sigma delta modulators (motor control, metering...)
+- PDM microphones (audio digital microphone)
+
+It features up to 8 serial digital interfaces (SPI or Manchester) and
+up to 4 filters.
+
+Each instance of the sub-drivers uses one filter instance.
+
+Contents of a STM32 DFSDM root node:
+------------------------------------
+Required properties:
+- compatible: Should be "st,stm32-dfsdm".
+- reg: Offset and length of the DFSDM block register set.
+- clocks: IP and serial interfaces clocking. Should be set according
+		to rcc clock ID and "clock-names".
+- clock-names: Input clock name "dfsdm" must be defined,
+		"audio" is optional. If defined CLKOUT is based on the audio
+		clock, else "dfsdm" is used.
+
+Optional properties:
+- spi-max-frequency: Requested only for SPI master mode.
+		  SPI clock OUT frequency (Hz). This clock must be set according
+		  to "clock" property. Frequency must be a multiple of the rcc
+		  clock frequency. If not, SPI CLKOUT frequency will not be
+		  accurate.
+
+Contents of a STM32 DFSDM child nodes:
+--------------------------------------
+
+Required properties:
+- compatible: Must be:
+	"st,stm32-dfsdm-adc" for sigma delta ADCs
+	"st,stm32-dfsdm-audio" for audio digital microphone.
+- reg: Specifies the DFSDM filter instance used.
+- interrupts: IRQ lines connected to each DFSDM filter instance.
+- st,adc-channels:	List of single-ended channels muxed for this ADC.
+- st,adc-channel-names:	List of single-ended channel names.
+- st,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.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+
+Required properties for "st,stm32-dfsdm-adc" compatibility:
+- io-channels: From common IIO binding. Used to pipe external sigma delta
+		modulator or internal ADC output to DFSDM channel.
+		This is not required for "st,stm32-dfsdm-pdm" compatibility as
+		PDM microphone is binded in Audio DT node.
+
+Required properties for "st,stm32-dfsdm-pdm" compatibility:
+- #sound-dai-cells: Must be set to 0.
+- dma: DMA controller phandle and DMA request line associated to the
+		filter instance (specified by the field "reg")
+- dma-names: Must be "rx"
+
+Optional properties:
+- st,adc-channel-types:	Single-ended channel input type.
+			- "SPI_R": SPI with data on rising edge (default)
+			- "SPI_F": SPI with data on falling edge
+			- "MANCH_R": manchester codec, rising edge = logic 0
+			- "MANCH_F": manchester codec, falling edge = logic 1
+- st,adc-channel-clk-src: Conversion clock source.
+			  - "CLKIN": external SPI clock (CLKIN x)
+			  - "CLKOUT": internal SPI clock (CLKOUT) (default)
+			  - "CLKOUT_F": internal SPI clock divided by 2 (falling edge).
+			  - "CLKOUT_R": internal SPI clock divided by 2 (rising edge).
+
+- st,adc-alt-channel: Must be defined if two sigma delta modulator 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.
+
+- st,filter0-sync: Set to 1 to synchronize with DFSDM filter instance 0.
+		   Used for multi microphones synchronization.
+
+Example of a sigma delta adc connected on DFSDM SPI port 0
+and a pdm microphone connected on DFSDM SPI port 1:
+
+	ads1202: simple_sd_adc@0 {
+		compatible = "ads1202";
+		#io-channel-cells = <1>;
+	};
+
+	dfsdm: dfsdm@40017000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm";
+
+		dfsdm_adc0: dfsdm-adc0@0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			interrupts = <110>;
+			st,adc-channels = <0>;
+			st,adc-channel-names = "sd_adc0";
+			st,adc-channel-types = "SPI_F";
+			st,adc-channel-clk-src = "CLKOUT";
+			io-channels = <&ads1202 0>;
+			st,filter-order = <3>;
+
+		dfsdm_pdm1: dfsdm-pdm@0 {
+			compatible = "st,stm32-dfsdm-pdm";
+			reg = <1>;
+			interrupts = <111>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,adc-channels = <1>;
+			st,adc-channel-names = "pdm1";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+			st,filter-order = <5>;
+		};
+	}
-- 
1.9.1


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

* [PATCH v3 04/11] IIO: add DT bindings for stm32 DFSDM filter
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add bindings that describes Digital Filter for Sigma Delta
Modulators. DFSDM allows to connect sigma delta
modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
   Fixes based on V2 comments

 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 +++++++++++++++++++++
 1 file changed, 120 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..d7828bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
@@ -0,0 +1,120 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicated to
+interface external sigma delta modulators to STM32 micro controllers.
+It is mainly targeted for:
+- Sigma delta modulators (motor control, metering...)
+- PDM microphones (audio digital microphone)
+
+It features up to 8 serial digital interfaces (SPI or Manchester) and
+up to 4 filters.
+
+Each instance of the sub-drivers uses one filter instance.
+
+Contents of a STM32 DFSDM root node:
+------------------------------------
+Required properties:
+- compatible: Should be "st,stm32-dfsdm".
+- reg: Offset and length of the DFSDM block register set.
+- clocks: IP and serial interfaces clocking. Should be set according
+		to rcc clock ID and "clock-names".
+- clock-names: Input clock name "dfsdm" must be defined,
+		"audio" is optional. If defined CLKOUT is based on the audio
+		clock, else "dfsdm" is used.
+
+Optional properties:
+- spi-max-frequency: Requested only for SPI master mode.
+		  SPI clock OUT frequency (Hz). This clock must be set according
+		  to "clock" property. Frequency must be a multiple of the rcc
+		  clock frequency. If not, SPI CLKOUT frequency will not be
+		  accurate.
+
+Contents of a STM32 DFSDM child nodes:
+--------------------------------------
+
+Required properties:
+- compatible: Must be:
+	"st,stm32-dfsdm-adc" for sigma delta ADCs
+	"st,stm32-dfsdm-audio" for audio digital microphone.
+- reg: Specifies the DFSDM filter instance used.
+- interrupts: IRQ lines connected to each DFSDM filter instance.
+- st,adc-channels:	List of single-ended channels muxed for this ADC.
+- st,adc-channel-names:	List of single-ended channel names.
+- st,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.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+
+Required properties for "st,stm32-dfsdm-adc" compatibility:
+- io-channels: From common IIO binding. Used to pipe external sigma delta
+		modulator or internal ADC output to DFSDM channel.
+		This is not required for "st,stm32-dfsdm-pdm" compatibility as
+		PDM microphone is binded in Audio DT node.
+
+Required properties for "st,stm32-dfsdm-pdm" compatibility:
+- #sound-dai-cells: Must be set to 0.
+- dma: DMA controller phandle and DMA request line associated to the
+		filter instance (specified by the field "reg")
+- dma-names: Must be "rx"
+
+Optional properties:
+- st,adc-channel-types:	Single-ended channel input type.
+			- "SPI_R": SPI with data on rising edge (default)
+			- "SPI_F": SPI with data on falling edge
+			- "MANCH_R": manchester codec, rising edge = logic 0
+			- "MANCH_F": manchester codec, falling edge = logic 1
+- st,adc-channel-clk-src: Conversion clock source.
+			  - "CLKIN": external SPI clock (CLKIN x)
+			  - "CLKOUT": internal SPI clock (CLKOUT) (default)
+			  - "CLKOUT_F": internal SPI clock divided by 2 (falling edge).
+			  - "CLKOUT_R": internal SPI clock divided by 2 (rising edge).
+
+- st,adc-alt-channel: Must be defined if two sigma delta modulator 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.
+
+- st,filter0-sync: Set to 1 to synchronize with DFSDM filter instance 0.
+		   Used for multi microphones synchronization.
+
+Example of a sigma delta adc connected on DFSDM SPI port 0
+and a pdm microphone connected on DFSDM SPI port 1:
+
+	ads1202: simple_sd_adc at 0 {
+		compatible = "ads1202";
+		#io-channel-cells = <1>;
+	};
+
+	dfsdm: dfsdm at 40017000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm";
+
+		dfsdm_adc0: dfsdm-adc0 at 0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			interrupts = <110>;
+			st,adc-channels = <0>;
+			st,adc-channel-names = "sd_adc0";
+			st,adc-channel-types = "SPI_F";
+			st,adc-channel-clk-src = "CLKOUT";
+			io-channels = <&ads1202 0>;
+			st,filter-order = <3>;
+
+		dfsdm_pdm1: dfsdm-pdm at 0 {
+			compatible = "st,stm32-dfsdm-pdm";
+			reg = <1>;
+			interrupts = <111>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,adc-channels = <1>;
+			st,adc-channel-names = "pdm1";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+			st,filter-order = <5>;
+		};
+	}
-- 
1.9.1

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

* [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
in n bit samples through a low pass filter and an integrator.
stm32-dfsdm-adc driver allows to handle sigma delta ADC.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	- Split audio and ADC support in 2 drivers
	- Implement DMA cyclic mode
	- Add SPI bus Trigger for buffer management

 drivers/iio/adc/Kconfig            |  26 ++
 drivers/iio/adc/Makefile           |   2 +
 drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
 5 files changed, 1477 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index d411d66..3e0eb11 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -452,6 +452,32 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_CORE
+	tristate "STMicroelectronics STM32 dfsdm core"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select REGMAP
+	select REGMAP_MMIO
+	help
+	  Select this option to enable the  driver for STMicroelectronics
+	  STM32 digital filter for sigma delta converter.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-dfsdm-core.
+
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 dfsdm adc"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select STM32_DFSDM_CORE
+	select REGMAP_MMIO
+	select IIO_BUFFER_DMAENGINE
+	select IIO_HW_CONSUMER
+	help
+	  Select this option to support ADCSigma delta modulator for
+	  STMicroelectronics STM32 digital filter for sigma delta converter.
+
+	  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 c68819c..161f271 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,419 @@
+/*
+ * This file is the ADC part of of the STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, 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/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "stm32-dfsdm.h"
+
+#define DFSDM_TIMEOUT_US 100000
+#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
+
+struct stm32_dfsdm_adc {
+	struct stm32_dfsdm *dfsdm;
+	unsigned int fl_id;
+	unsigned int ch_id;
+
+	unsigned int oversamp;
+
+	struct completion completion;
+
+	u32 *buffer;
+
+	/* Hardware consumer structure for Front End IIO */
+	struct iio_hw_consumer *hwc;
+};
+
+static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
+{
+	int ret;
+
+	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
+	if (ret < 0)
+		goto stop_dfsdm;
+
+	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	return 0;
+
+stop_channels:
+	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
+stop_dfsdm:
+	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+
+	return ret;
+}
+
+static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
+{
+	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
+
+	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
+
+	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+}
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan, int *res)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	long timeout;
+	int ret;
+
+	reinit_completion(&adc->completion);
+
+	adc->buffer = res;
+
+	/* Unmask IRQ for regular conversion achievement*/
+	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_conv(adc);
+	if (ret < 0)
+		return ret;
+
+	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+							    DFSDM_TIMEOUT);
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+
+	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", *res);
+		ret = IIO_VAL_INT;
+	}
+
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+
+	stm32_dfsdm_stop_conv(adc);
+
+	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+	int ret = -EINVAL;
+
+	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
+		ret = stm32_dfsdm_set_osrs(fl, 0, val);
+		if (!ret)
+			adc->oversamp = val;
+	}
+	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);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_hw_consumer_enable(adc->hwc);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"%s: IIO enable failed (channel %d)\n",
+				__func__, chan->channel);
+			return ret;
+		}
+		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"%s: Conversion failed (channel %d)\n",
+				__func__, chan->channel);
+			return ret;
+		}
+
+		iio_hw_consumer_disable(adc->hwc);
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*val = adc->oversamp;
+
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info stm32_dfsdm_info_adc = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct stm32_dfsdm_adc *adc = arg;
+	struct regmap *regmap = adc->dfsdm->regmap;
+	unsigned int status;
+
+	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
+
+	if (status & DFSDM_ISR_REOCF_MASK) {
+		/* read the data register clean the IRQ status */
+		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
+		complete(&adc->completion);
+	}
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
+				   DFSDM_ICR_CLRROVRF_MASK,
+				   DFSDM_ICR_CLRROVRF_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return stm32_dfsdm_start_conv(adc);
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	stm32_dfsdm_stop_conv(adc);
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
+	.postenable = &stm32_dfsdm_postenable,
+	.predisable = &stm32_dfsdm_predisable,
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int ch_idx)
+{
+	struct iio_chan_spec *ch = &chan[ch_idx];
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = ch_idx;
+
+	/*
+	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
+	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
+	 */
+	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+	adc->ch_id = ch->channel;
+
+	return stm32_dfsdm_chan_configure(adc->dfsdm,
+					  &adc->dfsdm->ch_list[ch->channel]);
+}
+
+static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
+{
+	struct iio_chan_spec *channels;
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	unsigned int num_ch;
+	int ret, chan_idx;
+
+	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
+					     "st,adc-channels");
+	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
+		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+		return num_ch < 0 ? num_ch : -EINVAL;
+	}
+
+	/*
+	 * Number of channel per filter is temporary limited to 1.
+	 * Restriction should be cleaned with scan mode
+	 */
+	if (num_ch > 1) {
+		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
+		return -EINVAL;
+	}
+
+	/* Bind to SD modulator IIO device */
+	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
+	if (IS_ERR(adc->hwc))
+		return -EPROBE_DEFER;
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		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 free_hwc;
+	}
+
+	indio_dev->num_channels = num_ch;
+	indio_dev->channels = channels;
+
+	return 0;
+
+free_hwc:
+	iio_hw_consumer_free(adc->hwc);
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc"},
+	{}
+};
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	struct device_node *np = dev->of_node;
+	struct iio_dev *iio;
+	char *name;
+	int ret, irq, val;
+
+	iio = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
+		return PTR_ERR(iio);
+	}
+
+	adc = iio_priv(iio);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
+		return PTR_ERR(adc);
+	}
+	adc->dfsdm = dev_get_drvdata(dev->parent);
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = &stm32_dfsdm_info_adc;
+	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+
+	platform_set_drvdata(pdev, adc);
+
+	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
+	if (ret != 0) {
+		dev_err(dev, "Missing reg property\n");
+		return -EINVAL;
+	}
+
+	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
+	iio->name = name;
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 */
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
+			       0, pdev->name, adc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request IRQ\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set filter order\n");
+		return ret;
+	}
+	adc->dfsdm->fl_list[adc->fl_id].ford = val;
+
+	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
+	if (!ret)
+		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
+
+	ret = stm32_dfsdm_adc_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	init_completion(&adc->completion);
+
+	return iio_device_register(iio);
+}
+
+static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
+{
+	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+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");
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
new file mode 100644
index 0000000..488e456
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -0,0 +1,658 @@
+/*
+ * This file is part the core part STM32 DFSDM 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/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/trigger.h>
+#include <linux/iio/sysfs.h>
+
+#include "stm32-dfsdm.h"
+
+struct stm32_dfsdm_dev_data {
+	unsigned int num_filters;
+	unsigned int num_channels;
+	const struct regmap_config *regmap_cfg;
+};
+
+#define STM32H7_DFSDM_NUM_FILTERS	4
+#define STM32H7_DFSDM_NUM_CHANNELS	8
+
+#define DFSDM_MAX_INT_OVERSAMPLING 256
+
+#define DFSDM_MAX_FL_OVERSAMPLING 1024
+
+#define DFSDM_MAX_RES BIT(31)
+#define DFSDM_DATA_RES BIT(23)
+
+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 = 0x2B8,
+	.volatile_reg = stm32_dfsdm_volatile_reg,
+	.fast_io = true,
+};
+
+static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
+	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
+	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
+	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
+};
+
+struct dfsdm_priv {
+	struct platform_device *pdev; /* platform device*/
+
+	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
+
+	unsigned int spi_clk_out_div; /* SPI clkout divider value */
+	atomic_t n_active_ch;	/* number of current active channels */
+
+	/* Clock */
+	struct clk *clk; /* DFSDM clock */
+	struct clk *aclk; /* audio clock */
+};
+
+/**
+ * stm32_dfsdm_set_osrs - compute filter parameters.
+ *
+ * Enable interface if n_active_ch is not null.
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fast: Fast mode enabled or disabled
+ * @oversamp: Expected oversampling between filtered sample and SD input stream
+ */
+int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
+			 unsigned int oversamp)
+{
+	unsigned int i, d, fosr, iosr;
+	u64 res;
+	s64 delta;
+	unsigned int m = 1;	/* multiplication factor */
+	unsigned int p = fl->ford;	/* filter order (ford) */
+
+	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
+	/*
+	 * This function tries to compute filter oversampling and integrator
+	 * oversampling, base on oversampling ratio requested by user.
+	 *
+	 * Decimation d depends on the filter order and the oversampling ratios.
+	 * ford: filter order
+	 * fosr: filter over sampling ratio
+	 * iosr: integrator over sampling ratio
+	 */
+	if (fl->ford == DFSDM_FASTSINC_ORDER) {
+		m = 2;
+		p = 2;
+	}
+
+	/*
+	 * Looks for filter and integrator oversampling ratios which allows
+	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
+				d = fosr * (iosr + 3) + 2;
+			else
+				d = fosr * (iosr - 1 + p) + p;
+
+			if (d > oversamp)
+				break;
+			else if (d != oversamp)
+				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 > DFSDM_MAX_RES)
+					break;
+			}
+			if (res > DFSDM_MAX_RES)
+				continue;
+			res = res * (u64)m * (u64)iosr;
+			if (res > DFSDM_MAX_RES)
+				continue;
+
+			delta = res - DFSDM_DATA_RES;
+
+			if (res >= fl->res) {
+				fl->res = res;
+				fl->fosr = fosr;
+				fl->iosr = iosr;
+				fl->fast = fast;
+				pr_debug("%s: fosr = %d, iosr = %d\n",
+					 __func__, fl->fosr, fl->iosr);
+			}
+
+			if (!delta)
+				return 0;
+		}
+	}
+
+	if (!fl->fosr)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
+ *
+ * Enable interface if n_active_ch is not null.
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ */
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	struct device *dev = &priv->pdev->dev;
+	unsigned int clk_div = priv->spi_clk_out_div;
+	int ret;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		/* Enable clocks */
+		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");
+				goto disable_clk;
+			}
+		}
+
+		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
+		if (ret < 0)
+			goto disable_aclk;
+
+		/* Global enable of DFSDM interface */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(1));
+		if (ret < 0)
+			goto disable_aclk;
+	}
+
+	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+
+disable_aclk:
+	clk_disable_unprepare(priv->aclk);
+disable_clk:
+	clk_disable_unprepare(priv->clk);
+
+	return ret;
+}
+
+/**
+ * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
+ *
+ * Disable interface if n_active_ch is null
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ */
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	int ret;
+
+	if (atomic_dec_and_test(&priv->n_active_ch)) {
+		/* Global disable of DFSDM interface */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(0));
+		if (ret < 0)
+			return ret;
+
+		/* Stop SPI CLKOUT */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(0));
+		if (ret < 0)
+			return ret;
+
+		/* Disable clocks */
+		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));
+
+	return 0;
+}
+
+/**
+ * stm32_dfsdm_start_channel
+ *	Start DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: Channel index.
+ */
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
+				  DFSDM_CHCFGR1_CHEN_MASK,
+				  DFSDM_CHCFGR1_CHEN(1));
+}
+
+/**
+ * stm32_dfsdm_stop_channel
+ *	Stop DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: Channel index.
+ */
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
+			   DFSDM_CHCFGR1_CHEN_MASK,
+			   DFSDM_CHCFGR1_CHEN(0));
+}
+
+/**
+ * stm32_dfsdm_chan_configure
+ *	Configure DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: channel index.
+ */
+int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
+			       struct stm32_dfsdm_channel *ch)
+{
+	unsigned int id = ch->id;
+	struct regmap *regmap = dfsdm->regmap;
+	int ret;
+
+	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				 DFSDM_CHCFGR1_SITP_MASK,
+				 DFSDM_CHCFGR1_SITP(ch->type));
+	if (ret < 0)
+		return ret;
+	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				 DFSDM_CHCFGR1_SPICKSEL_MASK,
+				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
+	if (ret < 0)
+		return ret;
+	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				  DFSDM_CHCFGR1_CHINSEL_MASK,
+				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
+}
+
+/**
+ * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter index.
+ */
+int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	int ret;
+
+	/* Enable filter */
+	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
+	if (ret < 0)
+		return ret;
+
+	/* Start conversion */
+	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_RSWSTART_MASK,
+				  DFSDM_CR1_RSWSTART(1));
+}
+
+/**
+ * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter index.
+ */
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+	/* Disable conversion */
+	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
+}
+
+/**
+ * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
+ *	to channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: channel index.
+ * @fl_id: Filter index.
+ */
+int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 unsigned int ch_id)
+{
+	struct regmap *regmap = dfsdm->regmap;
+	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
+	int ret;
+
+	/* Average integrator oversampling */
+	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
+				 DFSDM_FCR_IOSR(fl->iosr));
+
+	/* Filter order and Oversampling */
+	if (!ret)
+		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
+					 DFSDM_FCR_FOSR_MASK,
+					 DFSDM_FCR_FOSR(fl->fosr));
+
+	if (!ret)
+		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
+					 DFSDM_FCR_FORD_MASK,
+					 DFSDM_FCR_FORD(fl->ford));
+
+	/* If only one channel no scan mode supported for the moment */
+	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+				 DFSDM_CR1_RCH_MASK,
+				 DFSDM_CR1_RCH(ch_id));
+
+	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+					 DFSDM_CR1_RSYNC_MASK,
+			  DFSDM_CR1_RSYNC(fl->sync_mode));
+}
+
+static const struct iio_trigger_ops dfsdm_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
+					 struct stm32_dfsdm *dfsdm)
+{
+	/*
+	 * To be able to use buffer consumer interface a trigger is needed.
+	 * As conversion are trigged by PDM samples from SPI bus, that makes
+	 * sense to define the serial interface ( SPI or manchester) as
+	 * trigger source.
+	 */
+
+	struct iio_trigger *trig;
+	int ret;
+
+	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
+	if (!trig)
+		return -ENOMEM;
+
+	trig->dev.parent = pdev->dev.parent;
+	trig->ops = &dfsdm_trigger_ops;
+
+	iio_trigger_set_drvdata(trig, dfsdm);
+
+	ret = devm_iio_trigger_register(&pdev->dev, trig);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
+				 struct iio_dev *indio_dev,
+				 struct iio_chan_spec *chan, int chan_idx)
+{
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	struct stm32_dfsdm_channel *df_ch;
+	const char *of_str;
+	int ret, val;
+
+	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;
+	}
+
+	df_ch =  &dfsdm->ch_list[ch->channel];
+	df_ch->id = ch->channel;
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-types", chan_idx,
+					    &of_str);
+	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
+	if (ret < 0 || val < 0)
+		df_ch->type = 0;
+	else
+		df_ch->type = val;
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-clk-src", chan_idx,
+					    &of_str);
+	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
+	if (ret < 0 || val < 0)
+		df_ch->src = 0;
+	else
+		df_ch->src = val;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-alt-channel", chan_idx,
+					 &df_ch->alt_si);
+	if (ret < 0)
+		df_ch->alt_si = 0;
+
+	return 0;
+}
+
+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;
+	unsigned long clk_freq;
+	unsigned int spi_freq, rem;
+	int ret;
+
+	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->dfsdm.phys_base = res->start;
+	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
+
+	/* Source clock */
+	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
+	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");
+	if (IS_ERR(priv->aclk))
+		priv->aclk = NULL;
+
+	if (priv->aclk)
+		clk_freq = clk_get_rate(priv->aclk);
+	else
+		clk_freq = clk_get_rate(priv->clk);
+
+	/* SPI clock freq */
+	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
+				   &spi_freq);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
+		return ret;
+	}
+
+	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
+	priv->dfsdm.spi_master_freq = spi_freq;
+
+	if (rem) {
+		dev_warn(&pdev->dev, "SPI clock not accurate\n");
+		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
+			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
+	}
+
+	return 0;
+};
+
+static const struct of_device_id stm32_dfsdm_of_match[] = {
+	{
+		.compatible = "st,stm32h7-dfsdm",
+		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
+	struct stm32_dfsdm *dfsdm;
+	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_dfsdm_dev_data *)of_id->data;
+	dfsdm = &priv->dfsdm;
+	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
+				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
+	if (!dfsdm->fl_list)
+		return -ENOMEM;
+
+	dfsdm->num_fls = dev_data->num_filters;
+	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
+				      sizeof(*dfsdm->ch_list),
+				      GFP_KERNEL);
+	if (!dfsdm->ch_list)
+		return -ENOMEM;
+	dfsdm->num_chs = dev_data->num_channels;
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
+					    &stm32h7_dfsdm_regmap_cfg);
+	if (IS_ERR(dfsdm->regmap)) {
+		ret = PTR_ERR(dfsdm->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
+		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
+
+		fl->id = i;
+	}
+
+	platform_set_drvdata(pdev, dfsdm);
+
+	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
+	if (ret < 0)
+		return ret;
+
+	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
+}
+
+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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
new file mode 100644
index 0000000..bb7d74f
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -0,0 +1,371 @@
+/*
+ * This file is part of STM32 DFSDM 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__H
+#define MDF_STM32_DFSDM__H
+
+#include <linux/bitfield.h>
+
+#include <linux/iio/iio.h>
+/*
+ * STM32 DFSDM - global register map
+ * ________________________________________________________
+ * | Offset |                 Registers block             |
+ * --------------------------------------------------------
+ * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
+ * --------------------------------------------------------
+ * | 0x020  |                CHANNEL 1                    |
+ * --------------------------------------------------------
+ * | ...    |                .....                        |
+ * --------------------------------------------------------
+ * | 0x0E0  |                CHANNEL 7                    |
+ * --------------------------------------------------------
+ * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
+ * --------------------------------------------------------
+ * | 0x200  |                FILTER  1                    |
+ * --------------------------------------------------------
+ * | 0x300  |                FILTER  2                    |
+ * --------------------------------------------------------
+ * | 0x400  |                FILTER  3                    |
+ * --------------------------------------------------------
+ */
+
+/*
+ * Channels register 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 register 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)
+
+/* 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,
+};
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_filter {
+	unsigned int id;
+	unsigned int iosr; /* integrator oversampling */
+	unsigned int fosr; /* filter oversampling */
+	enum stm32_dfsdm_sinc_order ford;
+	u64 res; /* output sample resolution */
+	unsigned int sync_mode; /* filter suynchronized with filter0 */
+	unsigned int fast; /* filter fast mode */
+};
+
+/**
+ * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;   /* id of the channel */
+	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
+	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
+	unsigned int alt_si; /* use alternative serial input interface */
+};
+
+/**
+ * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
+ * @base:	control registers base cpu addr
+ * @phys_base:	DFSDM IP register physical address.
+ * @fl_list:	filter resources list
+ * @num_fl:	number of filter resources available
+ * @ch_list:	channel resources list
+ * @num_chs:	number of channel resources available
+ */
+struct stm32_dfsdm {
+	void __iomem	*base;
+	phys_addr_t	phys_base;
+	struct regmap *regmap;
+	struct stm32_dfsdm_filter *fl_list;
+	unsigned int num_fls;
+	struct stm32_dfsdm_channel *ch_list;
+	unsigned int num_chs;
+	unsigned int spi_master_freq;
+};
+
+struct stm32_dfsdm_str2field {
+	const char	*name;
+	unsigned int	val;
+};
+
+/* DFSDM channel serial interface type */
+static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
+	{ "SPI_R", 0 }, /* SPI with data on rising edge */
+	{ "SPI_F", 1 }, /* SPI with data on falling edge */
+	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
+	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
+	{ 0, 0},
+};
+
+/* DFSDM channel serial spi clock source */
+enum stm32_dfsdm_spi_clk_src {
+	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
+};
+
+/* DFSDM channel clock source */
+static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
+	/* External SPI clock (CLKIN x) */
+	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
+	/* Internal SPI clock (CLKOUT) */
+	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
+	/* Internal SPI clock divided by 2 (falling edge) */
+	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
+	/* Internal SPI clock divided by 2 (falling edge) */
+	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
+	{ 0, 0 },
+};
+
+/* DFSDM Serial interface trigger name  */
+#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
+
+static inline int stm32_dfsdm_str2val(const char *str,
+				      const struct stm32_dfsdm_str2field *list)
+{
+	const struct stm32_dfsdm_str2field *p = list;
+
+	for (p = list; p && p->name; p++) {
+		if (!strcmp(p->name, str))
+			return p->val;
+	}
+	return -EINVAL;
+}
+
+int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
+			 unsigned int oversamp);
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
+
+int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 unsigned int ch_id);
+int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
+			       struct stm32_dfsdm_channel *ch);
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
+				 struct iio_dev *indio_dev,
+				 struct iio_chan_spec *chan, int chan_idx);
+
+#endif
-- 
1.9.1

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

* [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
in n bit samples through a low pass filter and an integrator.
stm32-dfsdm-adc driver allows to handle sigma delta ADC.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	- Split audio and ADC support in 2 drivers
	- Implement DMA cyclic mode
	- Add SPI bus Trigger for buffer management

 drivers/iio/adc/Kconfig            |  26 ++
 drivers/iio/adc/Makefile           |   2 +
 drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
 5 files changed, 1477 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index d411d66..3e0eb11 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -452,6 +452,32 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_CORE
+	tristate "STMicroelectronics STM32 dfsdm core"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select REGMAP
+	select REGMAP_MMIO
+	help
+	  Select this option to enable the  driver for STMicroelectronics
+	  STM32 digital filter for sigma delta converter.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-dfsdm-core.
+
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 dfsdm adc"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select STM32_DFSDM_CORE
+	select REGMAP_MMIO
+	select IIO_BUFFER_DMAENGINE
+	select IIO_HW_CONSUMER
+	help
+	  Select this option to support ADCSigma delta modulator for
+	  STMicroelectronics STM32 digital filter for sigma delta converter.
+
+	  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 c68819c..161f271 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,419 @@
+/*
+ * This file is the ADC part of of the STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, 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/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "stm32-dfsdm.h"
+
+#define DFSDM_TIMEOUT_US 100000
+#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
+
+struct stm32_dfsdm_adc {
+	struct stm32_dfsdm *dfsdm;
+	unsigned int fl_id;
+	unsigned int ch_id;
+
+	unsigned int oversamp;
+
+	struct completion completion;
+
+	u32 *buffer;
+
+	/* Hardware consumer structure for Front End IIO */
+	struct iio_hw_consumer *hwc;
+};
+
+static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
+{
+	int ret;
+
+	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
+	if (ret < 0)
+		goto stop_dfsdm;
+
+	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	return 0;
+
+stop_channels:
+	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
+stop_dfsdm:
+	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+
+	return ret;
+}
+
+static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
+{
+	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
+
+	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
+
+	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+}
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan, int *res)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	long timeout;
+	int ret;
+
+	reinit_completion(&adc->completion);
+
+	adc->buffer = res;
+
+	/* Unmask IRQ for regular conversion achievement*/
+	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_conv(adc);
+	if (ret < 0)
+		return ret;
+
+	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+							    DFSDM_TIMEOUT);
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+
+	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", *res);
+		ret = IIO_VAL_INT;
+	}
+
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+
+	stm32_dfsdm_stop_conv(adc);
+
+	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+	int ret = -EINVAL;
+
+	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
+		ret = stm32_dfsdm_set_osrs(fl, 0, val);
+		if (!ret)
+			adc->oversamp = val;
+	}
+	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);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_hw_consumer_enable(adc->hwc);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"%s: IIO enable failed (channel %d)\n",
+				__func__, chan->channel);
+			return ret;
+		}
+		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"%s: Conversion failed (channel %d)\n",
+				__func__, chan->channel);
+			return ret;
+		}
+
+		iio_hw_consumer_disable(adc->hwc);
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*val = adc->oversamp;
+
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info stm32_dfsdm_info_adc = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct stm32_dfsdm_adc *adc = arg;
+	struct regmap *regmap = adc->dfsdm->regmap;
+	unsigned int status;
+
+	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
+
+	if (status & DFSDM_ISR_REOCF_MASK) {
+		/* read the data register clean the IRQ status */
+		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
+		complete(&adc->completion);
+	}
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
+				   DFSDM_ICR_CLRROVRF_MASK,
+				   DFSDM_ICR_CLRROVRF_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return stm32_dfsdm_start_conv(adc);
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	stm32_dfsdm_stop_conv(adc);
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
+	.postenable = &stm32_dfsdm_postenable,
+	.predisable = &stm32_dfsdm_predisable,
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int ch_idx)
+{
+	struct iio_chan_spec *ch = &chan[ch_idx];
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = ch_idx;
+
+	/*
+	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
+	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
+	 */
+	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+	adc->ch_id = ch->channel;
+
+	return stm32_dfsdm_chan_configure(adc->dfsdm,
+					  &adc->dfsdm->ch_list[ch->channel]);
+}
+
+static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
+{
+	struct iio_chan_spec *channels;
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	unsigned int num_ch;
+	int ret, chan_idx;
+
+	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
+					     "st,adc-channels");
+	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
+		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+		return num_ch < 0 ? num_ch : -EINVAL;
+	}
+
+	/*
+	 * Number of channel per filter is temporary limited to 1.
+	 * Restriction should be cleaned with scan mode
+	 */
+	if (num_ch > 1) {
+		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
+		return -EINVAL;
+	}
+
+	/* Bind to SD modulator IIO device */
+	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
+	if (IS_ERR(adc->hwc))
+		return -EPROBE_DEFER;
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		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 free_hwc;
+	}
+
+	indio_dev->num_channels = num_ch;
+	indio_dev->channels = channels;
+
+	return 0;
+
+free_hwc:
+	iio_hw_consumer_free(adc->hwc);
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc"},
+	{}
+};
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	struct device_node *np = dev->of_node;
+	struct iio_dev *iio;
+	char *name;
+	int ret, irq, val;
+
+	iio = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
+		return PTR_ERR(iio);
+	}
+
+	adc = iio_priv(iio);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
+		return PTR_ERR(adc);
+	}
+	adc->dfsdm = dev_get_drvdata(dev->parent);
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = &stm32_dfsdm_info_adc;
+	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+
+	platform_set_drvdata(pdev, adc);
+
+	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
+	if (ret != 0) {
+		dev_err(dev, "Missing reg property\n");
+		return -EINVAL;
+	}
+
+	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
+	iio->name = name;
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 */
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
+			       0, pdev->name, adc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request IRQ\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set filter order\n");
+		return ret;
+	}
+	adc->dfsdm->fl_list[adc->fl_id].ford = val;
+
+	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
+	if (!ret)
+		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
+
+	ret = stm32_dfsdm_adc_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	init_completion(&adc->completion);
+
+	return iio_device_register(iio);
+}
+
+static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
+{
+	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+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");
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
new file mode 100644
index 0000000..488e456
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -0,0 +1,658 @@
+/*
+ * This file is part the core part STM32 DFSDM 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/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/trigger.h>
+#include <linux/iio/sysfs.h>
+
+#include "stm32-dfsdm.h"
+
+struct stm32_dfsdm_dev_data {
+	unsigned int num_filters;
+	unsigned int num_channels;
+	const struct regmap_config *regmap_cfg;
+};
+
+#define STM32H7_DFSDM_NUM_FILTERS	4
+#define STM32H7_DFSDM_NUM_CHANNELS	8
+
+#define DFSDM_MAX_INT_OVERSAMPLING 256
+
+#define DFSDM_MAX_FL_OVERSAMPLING 1024
+
+#define DFSDM_MAX_RES BIT(31)
+#define DFSDM_DATA_RES BIT(23)
+
+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 = 0x2B8,
+	.volatile_reg = stm32_dfsdm_volatile_reg,
+	.fast_io = true,
+};
+
+static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
+	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
+	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
+	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
+};
+
+struct dfsdm_priv {
+	struct platform_device *pdev; /* platform device*/
+
+	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
+
+	unsigned int spi_clk_out_div; /* SPI clkout divider value */
+	atomic_t n_active_ch;	/* number of current active channels */
+
+	/* Clock */
+	struct clk *clk; /* DFSDM clock */
+	struct clk *aclk; /* audio clock */
+};
+
+/**
+ * stm32_dfsdm_set_osrs - compute filter parameters.
+ *
+ * Enable interface if n_active_ch is not null.
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fast: Fast mode enabled or disabled
+ * @oversamp: Expected oversampling between filtered sample and SD input stream
+ */
+int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
+			 unsigned int oversamp)
+{
+	unsigned int i, d, fosr, iosr;
+	u64 res;
+	s64 delta;
+	unsigned int m = 1;	/* multiplication factor */
+	unsigned int p = fl->ford;	/* filter order (ford) */
+
+	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
+	/*
+	 * This function tries to compute filter oversampling and integrator
+	 * oversampling, base on oversampling ratio requested by user.
+	 *
+	 * Decimation d depends on the filter order and the oversampling ratios.
+	 * ford: filter order
+	 * fosr: filter over sampling ratio
+	 * iosr: integrator over sampling ratio
+	 */
+	if (fl->ford == DFSDM_FASTSINC_ORDER) {
+		m = 2;
+		p = 2;
+	}
+
+	/*
+	 * Looks for filter and integrator oversampling ratios which allows
+	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
+				d = fosr * (iosr + 3) + 2;
+			else
+				d = fosr * (iosr - 1 + p) + p;
+
+			if (d > oversamp)
+				break;
+			else if (d != oversamp)
+				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 > DFSDM_MAX_RES)
+					break;
+			}
+			if (res > DFSDM_MAX_RES)
+				continue;
+			res = res * (u64)m * (u64)iosr;
+			if (res > DFSDM_MAX_RES)
+				continue;
+
+			delta = res - DFSDM_DATA_RES;
+
+			if (res >= fl->res) {
+				fl->res = res;
+				fl->fosr = fosr;
+				fl->iosr = iosr;
+				fl->fast = fast;
+				pr_debug("%s: fosr = %d, iosr = %d\n",
+					 __func__, fl->fosr, fl->iosr);
+			}
+
+			if (!delta)
+				return 0;
+		}
+	}
+
+	if (!fl->fosr)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
+ *
+ * Enable interface if n_active_ch is not null.
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ */
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	struct device *dev = &priv->pdev->dev;
+	unsigned int clk_div = priv->spi_clk_out_div;
+	int ret;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		/* Enable clocks */
+		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");
+				goto disable_clk;
+			}
+		}
+
+		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
+		if (ret < 0)
+			goto disable_aclk;
+
+		/* Global enable of DFSDM interface */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(1));
+		if (ret < 0)
+			goto disable_aclk;
+	}
+
+	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+
+disable_aclk:
+	clk_disable_unprepare(priv->aclk);
+disable_clk:
+	clk_disable_unprepare(priv->clk);
+
+	return ret;
+}
+
+/**
+ * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
+ *
+ * Disable interface if n_active_ch is null
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ */
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	int ret;
+
+	if (atomic_dec_and_test(&priv->n_active_ch)) {
+		/* Global disable of DFSDM interface */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(0));
+		if (ret < 0)
+			return ret;
+
+		/* Stop SPI CLKOUT */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(0));
+		if (ret < 0)
+			return ret;
+
+		/* Disable clocks */
+		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));
+
+	return 0;
+}
+
+/**
+ * stm32_dfsdm_start_channel
+ *	Start DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: Channel index.
+ */
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
+				  DFSDM_CHCFGR1_CHEN_MASK,
+				  DFSDM_CHCFGR1_CHEN(1));
+}
+
+/**
+ * stm32_dfsdm_stop_channel
+ *	Stop DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: Channel index.
+ */
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
+			   DFSDM_CHCFGR1_CHEN_MASK,
+			   DFSDM_CHCFGR1_CHEN(0));
+}
+
+/**
+ * stm32_dfsdm_chan_configure
+ *	Configure DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: channel index.
+ */
+int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
+			       struct stm32_dfsdm_channel *ch)
+{
+	unsigned int id = ch->id;
+	struct regmap *regmap = dfsdm->regmap;
+	int ret;
+
+	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				 DFSDM_CHCFGR1_SITP_MASK,
+				 DFSDM_CHCFGR1_SITP(ch->type));
+	if (ret < 0)
+		return ret;
+	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				 DFSDM_CHCFGR1_SPICKSEL_MASK,
+				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
+	if (ret < 0)
+		return ret;
+	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				  DFSDM_CHCFGR1_CHINSEL_MASK,
+				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
+}
+
+/**
+ * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter index.
+ */
+int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	int ret;
+
+	/* Enable filter */
+	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
+	if (ret < 0)
+		return ret;
+
+	/* Start conversion */
+	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_RSWSTART_MASK,
+				  DFSDM_CR1_RSWSTART(1));
+}
+
+/**
+ * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter index.
+ */
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+	/* Disable conversion */
+	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
+}
+
+/**
+ * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
+ *	to channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: channel index.
+ * @fl_id: Filter index.
+ */
+int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 unsigned int ch_id)
+{
+	struct regmap *regmap = dfsdm->regmap;
+	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
+	int ret;
+
+	/* Average integrator oversampling */
+	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
+				 DFSDM_FCR_IOSR(fl->iosr));
+
+	/* Filter order and Oversampling */
+	if (!ret)
+		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
+					 DFSDM_FCR_FOSR_MASK,
+					 DFSDM_FCR_FOSR(fl->fosr));
+
+	if (!ret)
+		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
+					 DFSDM_FCR_FORD_MASK,
+					 DFSDM_FCR_FORD(fl->ford));
+
+	/* If only one channel no scan mode supported for the moment */
+	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+				 DFSDM_CR1_RCH_MASK,
+				 DFSDM_CR1_RCH(ch_id));
+
+	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+					 DFSDM_CR1_RSYNC_MASK,
+			  DFSDM_CR1_RSYNC(fl->sync_mode));
+}
+
+static const struct iio_trigger_ops dfsdm_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
+					 struct stm32_dfsdm *dfsdm)
+{
+	/*
+	 * To be able to use buffer consumer interface a trigger is needed.
+	 * As conversion are trigged by PDM samples from SPI bus, that makes
+	 * sense to define the serial interface ( SPI or manchester) as
+	 * trigger source.
+	 */
+
+	struct iio_trigger *trig;
+	int ret;
+
+	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
+	if (!trig)
+		return -ENOMEM;
+
+	trig->dev.parent = pdev->dev.parent;
+	trig->ops = &dfsdm_trigger_ops;
+
+	iio_trigger_set_drvdata(trig, dfsdm);
+
+	ret = devm_iio_trigger_register(&pdev->dev, trig);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
+				 struct iio_dev *indio_dev,
+				 struct iio_chan_spec *chan, int chan_idx)
+{
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	struct stm32_dfsdm_channel *df_ch;
+	const char *of_str;
+	int ret, val;
+
+	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;
+	}
+
+	df_ch =  &dfsdm->ch_list[ch->channel];
+	df_ch->id = ch->channel;
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-types", chan_idx,
+					    &of_str);
+	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
+	if (ret < 0 || val < 0)
+		df_ch->type = 0;
+	else
+		df_ch->type = val;
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-clk-src", chan_idx,
+					    &of_str);
+	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
+	if (ret < 0 || val < 0)
+		df_ch->src = 0;
+	else
+		df_ch->src = val;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-alt-channel", chan_idx,
+					 &df_ch->alt_si);
+	if (ret < 0)
+		df_ch->alt_si = 0;
+
+	return 0;
+}
+
+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;
+	unsigned long clk_freq;
+	unsigned int spi_freq, rem;
+	int ret;
+
+	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->dfsdm.phys_base = res->start;
+	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
+
+	/* Source clock */
+	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
+	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");
+	if (IS_ERR(priv->aclk))
+		priv->aclk = NULL;
+
+	if (priv->aclk)
+		clk_freq = clk_get_rate(priv->aclk);
+	else
+		clk_freq = clk_get_rate(priv->clk);
+
+	/* SPI clock freq */
+	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
+				   &spi_freq);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
+		return ret;
+	}
+
+	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
+	priv->dfsdm.spi_master_freq = spi_freq;
+
+	if (rem) {
+		dev_warn(&pdev->dev, "SPI clock not accurate\n");
+		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
+			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
+	}
+
+	return 0;
+};
+
+static const struct of_device_id stm32_dfsdm_of_match[] = {
+	{
+		.compatible = "st,stm32h7-dfsdm",
+		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
+	struct stm32_dfsdm *dfsdm;
+	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_dfsdm_dev_data *)of_id->data;
+	dfsdm = &priv->dfsdm;
+	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
+				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
+	if (!dfsdm->fl_list)
+		return -ENOMEM;
+
+	dfsdm->num_fls = dev_data->num_filters;
+	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
+				      sizeof(*dfsdm->ch_list),
+				      GFP_KERNEL);
+	if (!dfsdm->ch_list)
+		return -ENOMEM;
+	dfsdm->num_chs = dev_data->num_channels;
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
+					    &stm32h7_dfsdm_regmap_cfg);
+	if (IS_ERR(dfsdm->regmap)) {
+		ret = PTR_ERR(dfsdm->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
+		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
+
+		fl->id = i;
+	}
+
+	platform_set_drvdata(pdev, dfsdm);
+
+	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
+	if (ret < 0)
+		return ret;
+
+	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
+}
+
+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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
new file mode 100644
index 0000000..bb7d74f
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -0,0 +1,371 @@
+/*
+ * This file is part of STM32 DFSDM 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__H
+#define MDF_STM32_DFSDM__H
+
+#include <linux/bitfield.h>
+
+#include <linux/iio/iio.h>
+/*
+ * STM32 DFSDM - global register map
+ * ________________________________________________________
+ * | Offset |                 Registers block             |
+ * --------------------------------------------------------
+ * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
+ * --------------------------------------------------------
+ * | 0x020  |                CHANNEL 1                    |
+ * --------------------------------------------------------
+ * | ...    |                .....                        |
+ * --------------------------------------------------------
+ * | 0x0E0  |                CHANNEL 7                    |
+ * --------------------------------------------------------
+ * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
+ * --------------------------------------------------------
+ * | 0x200  |                FILTER  1                    |
+ * --------------------------------------------------------
+ * | 0x300  |                FILTER  2                    |
+ * --------------------------------------------------------
+ * | 0x400  |                FILTER  3                    |
+ * --------------------------------------------------------
+ */
+
+/*
+ * Channels register 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 register 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)
+
+/* 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,
+};
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_filter {
+	unsigned int id;
+	unsigned int iosr; /* integrator oversampling */
+	unsigned int fosr; /* filter oversampling */
+	enum stm32_dfsdm_sinc_order ford;
+	u64 res; /* output sample resolution */
+	unsigned int sync_mode; /* filter suynchronized with filter0 */
+	unsigned int fast; /* filter fast mode */
+};
+
+/**
+ * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;   /* id of the channel */
+	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
+	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
+	unsigned int alt_si; /* use alternative serial input interface */
+};
+
+/**
+ * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
+ * @base:	control registers base cpu addr
+ * @phys_base:	DFSDM IP register physical address.
+ * @fl_list:	filter resources list
+ * @num_fl:	number of filter resources available
+ * @ch_list:	channel resources list
+ * @num_chs:	number of channel resources available
+ */
+struct stm32_dfsdm {
+	void __iomem	*base;
+	phys_addr_t	phys_base;
+	struct regmap *regmap;
+	struct stm32_dfsdm_filter *fl_list;
+	unsigned int num_fls;
+	struct stm32_dfsdm_channel *ch_list;
+	unsigned int num_chs;
+	unsigned int spi_master_freq;
+};
+
+struct stm32_dfsdm_str2field {
+	const char	*name;
+	unsigned int	val;
+};
+
+/* DFSDM channel serial interface type */
+static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
+	{ "SPI_R", 0 }, /* SPI with data on rising edge */
+	{ "SPI_F", 1 }, /* SPI with data on falling edge */
+	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
+	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
+	{ 0, 0},
+};
+
+/* DFSDM channel serial spi clock source */
+enum stm32_dfsdm_spi_clk_src {
+	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
+};
+
+/* DFSDM channel clock source */
+static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
+	/* External SPI clock (CLKIN x) */
+	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
+	/* Internal SPI clock (CLKOUT) */
+	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
+	/* Internal SPI clock divided by 2 (falling edge) */
+	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
+	/* Internal SPI clock divided by 2 (falling edge) */
+	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
+	{ 0, 0 },
+};
+
+/* DFSDM Serial interface trigger name  */
+#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
+
+static inline int stm32_dfsdm_str2val(const char *str,
+				      const struct stm32_dfsdm_str2field *list)
+{
+	const struct stm32_dfsdm_str2field *p = list;
+
+	for (p = list; p && p->name; p++) {
+		if (!strcmp(p->name, str))
+			return p->val;
+	}
+	return -EINVAL;
+}
+
+int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
+			 unsigned int oversamp);
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
+
+int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 unsigned int ch_id);
+int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
+			       struct stm32_dfsdm_channel *ch);
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
+				 struct iio_dev *indio_dev,
+				 struct iio_chan_spec *chan, int chan_idx);
+
+#endif
-- 
1.9.1


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

* [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
in n bit samples through a low pass filter and an integrator.
stm32-dfsdm-adc driver allows to handle sigma delta ADC.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2 -> V3 :
	- Split audio and ADC support in 2 drivers
	- Implement DMA cyclic mode
	- Add SPI bus Trigger for buffer management

 drivers/iio/adc/Kconfig            |  26 ++
 drivers/iio/adc/Makefile           |   2 +
 drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
 5 files changed, 1477 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
 create mode 100644 drivers/iio/adc/stm32-dfsdm.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index d411d66..3e0eb11 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -452,6 +452,32 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_CORE
+	tristate "STMicroelectronics STM32 dfsdm core"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select REGMAP
+	select REGMAP_MMIO
+	help
+	  Select this option to enable the  driver for STMicroelectronics
+	  STM32 digital filter for sigma delta converter.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-dfsdm-core.
+
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 dfsdm adc"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select STM32_DFSDM_CORE
+	select REGMAP_MMIO
+	select IIO_BUFFER_DMAENGINE
+	select IIO_HW_CONSUMER
+	help
+	  Select this option to support ADCSigma delta modulator for
+	  STMicroelectronics STM32 digital filter for sigma delta converter.
+
+	  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 c68819c..161f271 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,419 @@
+/*
+ * This file is the ADC part of of the STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, 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/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "stm32-dfsdm.h"
+
+#define DFSDM_TIMEOUT_US 100000
+#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
+
+struct stm32_dfsdm_adc {
+	struct stm32_dfsdm *dfsdm;
+	unsigned int fl_id;
+	unsigned int ch_id;
+
+	unsigned int oversamp;
+
+	struct completion completion;
+
+	u32 *buffer;
+
+	/* Hardware consumer structure for Front End IIO */
+	struct iio_hw_consumer *hwc;
+};
+
+static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
+{
+	int ret;
+
+	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
+	if (ret < 0)
+		goto stop_dfsdm;
+
+	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	return 0;
+
+stop_channels:
+	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
+stop_dfsdm:
+	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+
+	return ret;
+}
+
+static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
+{
+	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
+
+	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
+
+	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+}
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan, int *res)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	long timeout;
+	int ret;
+
+	reinit_completion(&adc->completion);
+
+	adc->buffer = res;
+
+	/* Unmask IRQ for regular conversion achievement*/
+	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_conv(adc);
+	if (ret < 0)
+		return ret;
+
+	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+							    DFSDM_TIMEOUT);
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+
+	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", *res);
+		ret = IIO_VAL_INT;
+	}
+
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+
+	stm32_dfsdm_stop_conv(adc);
+
+	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+	int ret = -EINVAL;
+
+	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
+		ret = stm32_dfsdm_set_osrs(fl, 0, val);
+		if (!ret)
+			adc->oversamp = val;
+	}
+	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);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_hw_consumer_enable(adc->hwc);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"%s: IIO enable failed (channel %d)\n",
+				__func__, chan->channel);
+			return ret;
+		}
+		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"%s: Conversion failed (channel %d)\n",
+				__func__, chan->channel);
+			return ret;
+		}
+
+		iio_hw_consumer_disable(adc->hwc);
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*val = adc->oversamp;
+
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info stm32_dfsdm_info_adc = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct stm32_dfsdm_adc *adc = arg;
+	struct regmap *regmap = adc->dfsdm->regmap;
+	unsigned int status;
+
+	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
+
+	if (status & DFSDM_ISR_REOCF_MASK) {
+		/* read the data register clean the IRQ status */
+		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
+		complete(&adc->completion);
+	}
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
+				   DFSDM_ICR_CLRROVRF_MASK,
+				   DFSDM_ICR_CLRROVRF_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return stm32_dfsdm_start_conv(adc);
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	stm32_dfsdm_stop_conv(adc);
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
+	.postenable = &stm32_dfsdm_postenable,
+	.predisable = &stm32_dfsdm_predisable,
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int ch_idx)
+{
+	struct iio_chan_spec *ch = &chan[ch_idx];
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = ch_idx;
+
+	/*
+	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
+	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
+	 */
+	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+	adc->ch_id = ch->channel;
+
+	return stm32_dfsdm_chan_configure(adc->dfsdm,
+					  &adc->dfsdm->ch_list[ch->channel]);
+}
+
+static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
+{
+	struct iio_chan_spec *channels;
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	unsigned int num_ch;
+	int ret, chan_idx;
+
+	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
+					     "st,adc-channels");
+	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
+		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+		return num_ch < 0 ? num_ch : -EINVAL;
+	}
+
+	/*
+	 * Number of channel per filter is temporary limited to 1.
+	 * Restriction should be cleaned with scan mode
+	 */
+	if (num_ch > 1) {
+		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
+		return -EINVAL;
+	}
+
+	/* Bind to SD modulator IIO device */
+	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
+	if (IS_ERR(adc->hwc))
+		return -EPROBE_DEFER;
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		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 free_hwc;
+	}
+
+	indio_dev->num_channels = num_ch;
+	indio_dev->channels = channels;
+
+	return 0;
+
+free_hwc:
+	iio_hw_consumer_free(adc->hwc);
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc"},
+	{}
+};
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	struct device_node *np = dev->of_node;
+	struct iio_dev *iio;
+	char *name;
+	int ret, irq, val;
+
+	iio = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
+		return PTR_ERR(iio);
+	}
+
+	adc = iio_priv(iio);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
+		return PTR_ERR(adc);
+	}
+	adc->dfsdm = dev_get_drvdata(dev->parent);
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = &stm32_dfsdm_info_adc;
+	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+
+	platform_set_drvdata(pdev, adc);
+
+	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
+	if (ret != 0) {
+		dev_err(dev, "Missing reg property\n");
+		return -EINVAL;
+	}
+
+	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
+	iio->name = name;
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 */
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
+			       0, pdev->name, adc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request IRQ\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set filter order\n");
+		return ret;
+	}
+	adc->dfsdm->fl_list[adc->fl_id].ford = val;
+
+	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
+	if (!ret)
+		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
+
+	ret = stm32_dfsdm_adc_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	init_completion(&adc->completion);
+
+	return iio_device_register(iio);
+}
+
+static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
+{
+	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+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");
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
new file mode 100644
index 0000000..488e456
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -0,0 +1,658 @@
+/*
+ * This file is part the core part STM32 DFSDM 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/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/trigger.h>
+#include <linux/iio/sysfs.h>
+
+#include "stm32-dfsdm.h"
+
+struct stm32_dfsdm_dev_data {
+	unsigned int num_filters;
+	unsigned int num_channels;
+	const struct regmap_config *regmap_cfg;
+};
+
+#define STM32H7_DFSDM_NUM_FILTERS	4
+#define STM32H7_DFSDM_NUM_CHANNELS	8
+
+#define DFSDM_MAX_INT_OVERSAMPLING 256
+
+#define DFSDM_MAX_FL_OVERSAMPLING 1024
+
+#define DFSDM_MAX_RES BIT(31)
+#define DFSDM_DATA_RES BIT(23)
+
+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 = 0x2B8,
+	.volatile_reg = stm32_dfsdm_volatile_reg,
+	.fast_io = true,
+};
+
+static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
+	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
+	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
+	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
+};
+
+struct dfsdm_priv {
+	struct platform_device *pdev; /* platform device*/
+
+	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
+
+	unsigned int spi_clk_out_div; /* SPI clkout divider value */
+	atomic_t n_active_ch;	/* number of current active channels */
+
+	/* Clock */
+	struct clk *clk; /* DFSDM clock */
+	struct clk *aclk; /* audio clock */
+};
+
+/**
+ * stm32_dfsdm_set_osrs - compute filter parameters.
+ *
+ * Enable interface if n_active_ch is not null.
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fast: Fast mode enabled or disabled
+ * @oversamp: Expected oversampling between filtered sample and SD input stream
+ */
+int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
+			 unsigned int oversamp)
+{
+	unsigned int i, d, fosr, iosr;
+	u64 res;
+	s64 delta;
+	unsigned int m = 1;	/* multiplication factor */
+	unsigned int p = fl->ford;	/* filter order (ford) */
+
+	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
+	/*
+	 * This function tries to compute filter oversampling and integrator
+	 * oversampling, base on oversampling ratio requested by user.
+	 *
+	 * Decimation d depends on the filter order and the oversampling ratios.
+	 * ford: filter order
+	 * fosr: filter over sampling ratio
+	 * iosr: integrator over sampling ratio
+	 */
+	if (fl->ford == DFSDM_FASTSINC_ORDER) {
+		m = 2;
+		p = 2;
+	}
+
+	/*
+	 * Looks for filter and integrator oversampling ratios which allows
+	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
+				d = fosr * (iosr + 3) + 2;
+			else
+				d = fosr * (iosr - 1 + p) + p;
+
+			if (d > oversamp)
+				break;
+			else if (d != oversamp)
+				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 > DFSDM_MAX_RES)
+					break;
+			}
+			if (res > DFSDM_MAX_RES)
+				continue;
+			res = res * (u64)m * (u64)iosr;
+			if (res > DFSDM_MAX_RES)
+				continue;
+
+			delta = res - DFSDM_DATA_RES;
+
+			if (res >= fl->res) {
+				fl->res = res;
+				fl->fosr = fosr;
+				fl->iosr = iosr;
+				fl->fast = fast;
+				pr_debug("%s: fosr = %d, iosr = %d\n",
+					 __func__, fl->fosr, fl->iosr);
+			}
+
+			if (!delta)
+				return 0;
+		}
+	}
+
+	if (!fl->fosr)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
+ *
+ * Enable interface if n_active_ch is not null.
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ */
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	struct device *dev = &priv->pdev->dev;
+	unsigned int clk_div = priv->spi_clk_out_div;
+	int ret;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		/* Enable clocks */
+		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");
+				goto disable_clk;
+			}
+		}
+
+		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
+		if (ret < 0)
+			goto disable_aclk;
+
+		/* Global enable of DFSDM interface */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(1));
+		if (ret < 0)
+			goto disable_aclk;
+	}
+
+	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+
+disable_aclk:
+	clk_disable_unprepare(priv->aclk);
+disable_clk:
+	clk_disable_unprepare(priv->clk);
+
+	return ret;
+}
+
+/**
+ * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
+ *
+ * Disable interface if n_active_ch is null
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ */
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	int ret;
+
+	if (atomic_dec_and_test(&priv->n_active_ch)) {
+		/* Global disable of DFSDM interface */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(0));
+		if (ret < 0)
+			return ret;
+
+		/* Stop SPI CLKOUT */
+		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(0));
+		if (ret < 0)
+			return ret;
+
+		/* Disable clocks */
+		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));
+
+	return 0;
+}
+
+/**
+ * stm32_dfsdm_start_channel
+ *	Start DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: Channel index.
+ */
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
+				  DFSDM_CHCFGR1_CHEN_MASK,
+				  DFSDM_CHCFGR1_CHEN(1));
+}
+
+/**
+ * stm32_dfsdm_stop_channel
+ *	Stop DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: Channel index.
+ */
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
+			   DFSDM_CHCFGR1_CHEN_MASK,
+			   DFSDM_CHCFGR1_CHEN(0));
+}
+
+/**
+ * stm32_dfsdm_chan_configure
+ *	Configure DFSDM IP channels and associated interface.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: channel index.
+ */
+int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
+			       struct stm32_dfsdm_channel *ch)
+{
+	unsigned int id = ch->id;
+	struct regmap *regmap = dfsdm->regmap;
+	int ret;
+
+	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				 DFSDM_CHCFGR1_SITP_MASK,
+				 DFSDM_CHCFGR1_SITP(ch->type));
+	if (ret < 0)
+		return ret;
+	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				 DFSDM_CHCFGR1_SPICKSEL_MASK,
+				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
+	if (ret < 0)
+		return ret;
+	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+				  DFSDM_CHCFGR1_CHINSEL_MASK,
+				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
+}
+
+/**
+ * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter index.
+ */
+int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	int ret;
+
+	/* Enable filter */
+	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
+	if (ret < 0)
+		return ret;
+
+	/* Start conversion */
+	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_RSWSTART_MASK,
+				  DFSDM_CR1_RSWSTART(1));
+}
+
+/**
+ * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter index.
+ */
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	/* Mask IRQ for regular conversion achievement*/
+	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
+			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+	/* Disable conversion */
+	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
+}
+
+/**
+ * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
+ *	to channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: channel index.
+ * @fl_id: Filter index.
+ */
+int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 unsigned int ch_id)
+{
+	struct regmap *regmap = dfsdm->regmap;
+	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
+	int ret;
+
+	/* Average integrator oversampling */
+	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
+				 DFSDM_FCR_IOSR(fl->iosr));
+
+	/* Filter order and Oversampling */
+	if (!ret)
+		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
+					 DFSDM_FCR_FOSR_MASK,
+					 DFSDM_FCR_FOSR(fl->fosr));
+
+	if (!ret)
+		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
+					 DFSDM_FCR_FORD_MASK,
+					 DFSDM_FCR_FORD(fl->ford));
+
+	/* If only one channel no scan mode supported for the moment */
+	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+				 DFSDM_CR1_RCH_MASK,
+				 DFSDM_CR1_RCH(ch_id));
+
+	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+					 DFSDM_CR1_RSYNC_MASK,
+			  DFSDM_CR1_RSYNC(fl->sync_mode));
+}
+
+static const struct iio_trigger_ops dfsdm_trigger_ops = {
+	.owner = THIS_MODULE,
+};
+
+static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
+					 struct stm32_dfsdm *dfsdm)
+{
+	/*
+	 * To be able to use buffer consumer interface a trigger is needed.
+	 * As conversion are trigged by PDM samples from SPI bus, that makes
+	 * sense to define the serial interface ( SPI or manchester) as
+	 * trigger source.
+	 */
+
+	struct iio_trigger *trig;
+	int ret;
+
+	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
+	if (!trig)
+		return -ENOMEM;
+
+	trig->dev.parent = pdev->dev.parent;
+	trig->ops = &dfsdm_trigger_ops;
+
+	iio_trigger_set_drvdata(trig, dfsdm);
+
+	ret = devm_iio_trigger_register(&pdev->dev, trig);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
+				 struct iio_dev *indio_dev,
+				 struct iio_chan_spec *chan, int chan_idx)
+{
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	struct stm32_dfsdm_channel *df_ch;
+	const char *of_str;
+	int ret, val;
+
+	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;
+	}
+
+	df_ch =  &dfsdm->ch_list[ch->channel];
+	df_ch->id = ch->channel;
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-types", chan_idx,
+					    &of_str);
+	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
+	if (ret < 0 || val < 0)
+		df_ch->type = 0;
+	else
+		df_ch->type = val;
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-clk-src", chan_idx,
+					    &of_str);
+	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
+	if (ret < 0 || val < 0)
+		df_ch->src = 0;
+	else
+		df_ch->src = val;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-alt-channel", chan_idx,
+					 &df_ch->alt_si);
+	if (ret < 0)
+		df_ch->alt_si = 0;
+
+	return 0;
+}
+
+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;
+	unsigned long clk_freq;
+	unsigned int spi_freq, rem;
+	int ret;
+
+	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->dfsdm.phys_base = res->start;
+	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
+
+	/* Source clock */
+	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
+	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");
+	if (IS_ERR(priv->aclk))
+		priv->aclk = NULL;
+
+	if (priv->aclk)
+		clk_freq = clk_get_rate(priv->aclk);
+	else
+		clk_freq = clk_get_rate(priv->clk);
+
+	/* SPI clock freq */
+	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
+				   &spi_freq);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
+		return ret;
+	}
+
+	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
+	priv->dfsdm.spi_master_freq = spi_freq;
+
+	if (rem) {
+		dev_warn(&pdev->dev, "SPI clock not accurate\n");
+		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
+			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
+	}
+
+	return 0;
+};
+
+static const struct of_device_id stm32_dfsdm_of_match[] = {
+	{
+		.compatible = "st,stm32h7-dfsdm",
+		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
+	struct stm32_dfsdm *dfsdm;
+	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_dfsdm_dev_data *)of_id->data;
+	dfsdm = &priv->dfsdm;
+	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
+				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
+	if (!dfsdm->fl_list)
+		return -ENOMEM;
+
+	dfsdm->num_fls = dev_data->num_filters;
+	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
+				      sizeof(*dfsdm->ch_list),
+				      GFP_KERNEL);
+	if (!dfsdm->ch_list)
+		return -ENOMEM;
+	dfsdm->num_chs = dev_data->num_channels;
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
+					    &stm32h7_dfsdm_regmap_cfg);
+	if (IS_ERR(dfsdm->regmap)) {
+		ret = PTR_ERR(dfsdm->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
+		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
+
+		fl->id = i;
+	}
+
+	platform_set_drvdata(pdev, dfsdm);
+
+	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
+	if (ret < 0)
+		return ret;
+
+	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
+}
+
+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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
new file mode 100644
index 0000000..bb7d74f
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -0,0 +1,371 @@
+/*
+ * This file is part of STM32 DFSDM 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__H
+#define MDF_STM32_DFSDM__H
+
+#include <linux/bitfield.h>
+
+#include <linux/iio/iio.h>
+/*
+ * STM32 DFSDM - global register map
+ * ________________________________________________________
+ * | Offset |                 Registers block             |
+ * --------------------------------------------------------
+ * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
+ * --------------------------------------------------------
+ * | 0x020  |                CHANNEL 1                    |
+ * --------------------------------------------------------
+ * | ...    |                .....                        |
+ * --------------------------------------------------------
+ * | 0x0E0  |                CHANNEL 7                    |
+ * --------------------------------------------------------
+ * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
+ * --------------------------------------------------------
+ * | 0x200  |                FILTER  1                    |
+ * --------------------------------------------------------
+ * | 0x300  |                FILTER  2                    |
+ * --------------------------------------------------------
+ * | 0x400  |                FILTER  3                    |
+ * --------------------------------------------------------
+ */
+
+/*
+ * Channels register 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 register 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)
+
+/* 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,
+};
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_filter {
+	unsigned int id;
+	unsigned int iosr; /* integrator oversampling */
+	unsigned int fosr; /* filter oversampling */
+	enum stm32_dfsdm_sinc_order ford;
+	u64 res; /* output sample resolution */
+	unsigned int sync_mode; /* filter suynchronized with filter0 */
+	unsigned int fast; /* filter fast mode */
+};
+
+/**
+ * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;   /* id of the channel */
+	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
+	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
+	unsigned int alt_si; /* use alternative serial input interface */
+};
+
+/**
+ * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
+ * @base:	control registers base cpu addr
+ * @phys_base:	DFSDM IP register physical address.
+ * @fl_list:	filter resources list
+ * @num_fl:	number of filter resources available
+ * @ch_list:	channel resources list
+ * @num_chs:	number of channel resources available
+ */
+struct stm32_dfsdm {
+	void __iomem	*base;
+	phys_addr_t	phys_base;
+	struct regmap *regmap;
+	struct stm32_dfsdm_filter *fl_list;
+	unsigned int num_fls;
+	struct stm32_dfsdm_channel *ch_list;
+	unsigned int num_chs;
+	unsigned int spi_master_freq;
+};
+
+struct stm32_dfsdm_str2field {
+	const char	*name;
+	unsigned int	val;
+};
+
+/* DFSDM channel serial interface type */
+static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
+	{ "SPI_R", 0 }, /* SPI with data on rising edge */
+	{ "SPI_F", 1 }, /* SPI with data on falling edge */
+	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
+	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
+	{ 0, 0},
+};
+
+/* DFSDM channel serial spi clock source */
+enum stm32_dfsdm_spi_clk_src {
+	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
+};
+
+/* DFSDM channel clock source */
+static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
+	/* External SPI clock (CLKIN x) */
+	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
+	/* Internal SPI clock (CLKOUT) */
+	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
+	/* Internal SPI clock divided by 2 (falling edge) */
+	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
+	/* Internal SPI clock divided by 2 (falling edge) */
+	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
+	{ 0, 0 },
+};
+
+/* DFSDM Serial interface trigger name  */
+#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
+
+static inline int stm32_dfsdm_str2val(const char *str,
+				      const struct stm32_dfsdm_str2field *list)
+{
+	const struct stm32_dfsdm_str2field *p = list;
+
+	for (p = list; p && p->name; p++) {
+		if (!strcmp(p->name, str))
+			return p->val;
+	}
+	return -EINVAL;
+}
+
+int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
+			 unsigned int oversamp);
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
+
+int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 unsigned int ch_id);
+int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
+			       struct stm32_dfsdm_channel *ch);
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
+				 struct iio_dev *indio_dev,
+				 struct iio_chan_spec *chan, int chan_idx);
+
+#endif
-- 
1.9.1

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

* [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add DFSDM driver to handle PDM audio microphones.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig                   |  13 +
 drivers/iio/adc/Makefile                  |   1 +
 drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
 include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
 4 files changed, 775 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
 create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3e0eb11..c108933 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-dfsdm-adc.
 
+config STM32_DFSDM_AUDIO
+	tristate "STMicroelectronics STM32 dfsdm audio
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select STM32_DFSDM_CORE
+	select REGMAP_MMIO
+	select IIO_BUFFER_DMAENGINE
+	help
+	  Select this option to support Audio PDM micophone for
+	  STMicroelectronics  STM32 digital filter for sigma delta converter.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-dfsdm-audio.
+
 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 161f271..79f975d 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
 obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
new file mode 100644
index 0000000..115ef8f
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-audio.c
@@ -0,0 +1,720 @@
+/*
+ * This file is the ADC part of of the STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, 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/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "stm32-dfsdm.h"
+
+#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
+
+struct stm32_dfsdm_audio {
+	struct stm32_dfsdm *dfsdm;
+	unsigned int fl_id;
+	unsigned int ch_id;
+	unsigned int spi_freq;  /* SPI bus clock frequency */
+	unsigned int sample_freq; /* Sample frequency after filter decimation */
+
+	u8 *rx_buf;
+	unsigned int bufi; /* Buffer current position */
+	unsigned int buf_sz; /* Buffer size */
+
+	struct dma_chan	*dma_chan;
+	dma_addr_t dma_buf;
+
+	int (*cb)(const void *data, size_t size, void *cb_priv);
+	void *cb_priv;
+};
+
+const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
+
+static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
+				    const struct iio_chan_spec *chan, char *buf)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
+}
+
+static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
+				    const struct iio_chan_spec *chan,
+				    const char *buf, size_t len)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
+	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
+	unsigned int spi_freq = pdmc->spi_freq;
+	unsigned int sample_freq;
+	int ret;
+
+	ret = kstrtoint(buf, 0, &sample_freq);
+	if (ret)
+		return ret;
+
+	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
+	if (!sample_freq)
+		return -EINVAL;
+
+	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
+		spi_freq = pdmc->dfsdm->spi_master_freq;
+
+	if (spi_freq % sample_freq)
+		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
+			 spi_freq / (spi_freq / sample_freq));
+
+	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			"Not able to find filter parameter that match!\n");
+		return ret;
+	}
+	pdmc->sample_freq = sample_freq;
+
+	return len;
+}
+
+static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      char *buf)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
+}
+
+static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      const char *buf, size_t len)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
+	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
+	unsigned int sample_freq = pdmc->sample_freq;
+	unsigned int spi_freq;
+	int ret;
+
+	/* If DFSDM is master on SPI, SPI freq can not be updated */
+	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
+		return -EPERM;
+
+	ret = kstrtoint(buf, 0, &spi_freq);
+	if (ret)
+		return ret;
+
+	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
+	if (!spi_freq)
+		return -EINVAL;
+
+	if (sample_freq) {
+		if (spi_freq % sample_freq)
+			dev_warn(&indio_dev->dev,
+				 "Sampling rate not accurate (%d)\n",
+				 spi_freq / (spi_freq / sample_freq));
+
+		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"No filter parameters that match!\n");
+			return ret;
+		}
+	}
+	pdmc->spi_freq = spi_freq;
+
+	return len;
+}
+
+/*
+ * Define external info for SPI Frequency and audio sampling rate that can be
+ * configured by ASoC driver through consumer.h API
+ */
+static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
+	/* filter oversampling: Post filter oversampling ratio */
+	{
+		.name = "audio_sampling_rate",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = dfsdm_audio_get_rate,
+		.write = dfsdm_audio_set_rate,
+	},
+	/* data_right_bit_shift : Filter output data shifting */
+	{
+		.name = "spi_clk_freq",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = dfsdm_audio_get_spiclk,
+		.write = dfsdm_audio_set_spiclk,
+	},
+	{},
+};
+
+static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
+{
+	struct regmap *regmap = pdmc->dfsdm->regmap;
+	int ret;
+
+	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
+	if (ret < 0)
+		goto stop_dfsdm;
+
+	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
+					   pdmc->ch_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	/* Enable DMA transfer*/
+	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
+				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
+	if (ret < 0)
+		return ret;
+
+	/* Enable conversion triggered by SPI clock*/
+	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
+				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	return 0;
+
+stop_channels:
+	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
+stop_dfsdm:
+	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
+
+	return ret;
+}
+
+static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
+{
+	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
+
+	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
+
+	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
+}
+
+static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
+				     unsigned int val)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
+
+	/*
+	 * DMA cyclic transfers are used, buffer is split into two periods.
+	 * There should be :
+	 * - always one buffer (period) DMA is working on
+	 * - one buffer (period) driver pushed to ASoC side ().
+	 */
+	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
+	pdmc->buf_sz = watermark * 2;
+
+	return 0;
+}
+
+int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
+				 struct iio_trigger *trig)
+{
+	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
+		return 0;
+
+	return -EINVAL;
+}
+
+static const struct iio_info stm32_dfsdm_info_pdmc = {
+	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
+	.driver_module = THIS_MODULE,
+	.validate_trigger = stm32_dfsdm_validate_trigger,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct stm32_dfsdm_audio *pdmc = arg;
+	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
+	struct regmap *regmap = pdmc->dfsdm->regmap;
+	unsigned int status;
+
+	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
+
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
+		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
+				   DFSDM_ICR_CLRROVRF_MASK,
+				   DFSDM_ICR_CLRROVRF_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
+{
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	status = dmaengine_tx_status(pdmc->dma_chan,
+				     pdmc->dma_chan->cookie,
+				     &state);
+	if (status == DMA_IN_PROGRESS) {
+		/* Residue is size in bytes from end of buffer */
+		unsigned int i = pdmc->buf_sz - state.residue;
+		unsigned int size;
+
+		/* Return available bytes */
+		if (i >= pdmc->bufi)
+			size = i - pdmc->bufi;
+		else
+			size = pdmc->buf_sz + i - pdmc->bufi;
+
+		return size;
+	}
+
+	return 0;
+}
+
+static void stm32_dfsdm_audio_dma_buffer_done(void *data)
+{
+	struct iio_dev *indio_dev = data;
+
+	iio_trigger_poll_chained(indio_dev->trig);
+}
+
+static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct dma_async_tx_descriptor *desc;
+	dma_cookie_t cookie;
+	int ret;
+
+	if (!pdmc->dma_chan)
+		return -EINVAL;
+
+	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
+		pdmc->buf_sz, pdmc->buf_sz / 2);
+
+	/* Prepare a DMA cyclic transaction */
+	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
+					 pdmc->dma_buf,
+					 pdmc->buf_sz, pdmc->buf_sz / 2,
+					 DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT);
+	if (!desc)
+		return -EBUSY;
+
+	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
+	desc->callback_param = indio_dev;
+
+	cookie = dmaengine_submit(desc);
+	ret = dma_submit_error(cookie);
+	if (ret) {
+		dmaengine_terminate_all(pdmc->dma_chan);
+		return ret;
+	}
+
+	/* Issue pending DMA requests */
+	dma_async_issue_pending(pdmc->dma_chan);
+
+	return 0;
+}
+
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	/* Reset pdmc buffer index */
+	pdmc->bufi = 0;
+
+	ret = stm32_dfsdm_start_conv(pdmc, false);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Can't start conversion\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_audio_dma_start(indio_dev);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Can't start DMA\n");
+		goto err_stop_conv;
+	}
+
+	ret = iio_triggered_buffer_postenable(indio_dev);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
+		goto err_stop_dma;
+	}
+
+	return 0;
+
+err_stop_dma:
+	if (pdmc->dma_chan)
+		dmaengine_terminate_all(pdmc->dma_chan);
+err_stop_conv:
+	stm32_dfsdm_stop_conv(pdmc);
+
+	return ret;
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	ret = iio_triggered_buffer_predisable(indio_dev);
+	if (ret < 0)
+		dev_err(&indio_dev->dev, "Predisable failed\n");
+
+	if (pdmc->dma_chan)
+		dmaengine_terminate_all(pdmc->dma_chan);
+
+	stm32_dfsdm_stop_conv(pdmc);
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
+	.postenable = &stm32_dfsdm_postenable,
+	.predisable = &stm32_dfsdm_predisable,
+};
+
+static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	size_t old_pos;
+	int available = stm32_dfsdm_audio_avail_data(pdmc);
+
+	/*
+	 * Buffer interface is not support cyclic DMA buffer,and offer only
+	 * an interface to push data samples per samples.
+	 * For this reason iio_push_to_buffers_with_timestamp in not used
+	 * and interface is hacked using a private callback registered by ASoC.
+	 * This should be a temporary solution waiting a cyclic DMA engine
+	 * support in IIO.
+	 */
+
+	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
+		pdmc->bufi, available);
+	old_pos = pdmc->bufi;
+	while (available >= indio_dev->scan_bytes) {
+		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
+
+		/* Mask 8 LSB that contains the channel ID */
+		*buffer &= 0xFFFFFF00;
+		available -= indio_dev->scan_bytes;
+		pdmc->bufi += indio_dev->scan_bytes;
+		if (pdmc->bufi >= pdmc->buf_sz) {
+			if (pdmc->cb)
+				pdmc->cb(&pdmc->rx_buf[old_pos],
+					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
+			pdmc->bufi = 0;
+			old_pos = 0;
+		}
+	}
+	if (pdmc->cb)
+		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
+				pdmc->cb_priv);
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * stm32_dfsdm_get_buff_cb - register a callback
+ *	that will be called when DMA transfer period is achieved.
+ *
+ * @iio_dev: Handle to IIO device.
+ * @cb: pointer to callback function.
+ *	@data: pointer to data buffer
+ *	@size: size in byte of the data buffer
+ *	@private: pointer to consumer private structure
+ * @private: pointer to consumer private structure
+ */
+int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
+			    int (*cb)(const void *data, size_t size,
+				      void *private),
+			    void *private)
+{
+	struct stm32_dfsdm_audio *pdmc;
+
+	if (!iio_dev)
+		return -EINVAL;
+	pdmc = iio_priv(iio_dev);
+
+	if (iio_dev !=  iio_priv_to_dev(pdmc))
+		return -EINVAL;
+
+	pdmc->cb = cb;
+	pdmc->cb_priv = private;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
+
+/**
+ * stm32_dfsdm_release_buff_cb - unregister buffer callback
+ *
+ * @iio_dev: Handle to IIO device.
+ */
+int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc;
+
+	if (!iio_dev)
+		return -EINVAL;
+	pdmc = iio_priv(iio_dev);
+
+	if (iio_dev !=  iio_priv_to_dev(pdmc))
+		return -EINVAL;
+	pdmc->cb = NULL;
+	pdmc->cb_priv = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
+
+static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
+{
+	struct iio_chan_spec *ch;
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
+	if (!ch)
+		return -ENOMEM;
+
+	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
+	if (ret < 0)
+		return ret;
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = 0;
+	ch->ext_info = dfsdm_adc_ext_info;
+
+	ch->scan_type.sign = 's';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+
+	pdmc->ch_id = ch->channel;
+	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
+					 &pdmc->dfsdm->ch_list[ch->channel]);
+
+	indio_dev->num_channels = 1;
+	indio_dev->channels = ch;
+
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_audio_match[] = {
+	{ .compatible = "st,stm32-dfsdm-audio"},
+	{}
+};
+
+static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct dma_slave_config config;
+	int ret;
+
+	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
+	if (!pdmc->dma_chan)
+		return -EINVAL;
+
+	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
+					 DFSDM_DMA_BUFFER_SIZE,
+					 &pdmc->dma_buf, GFP_KERNEL);
+	if (!pdmc->rx_buf) {
+		ret = -ENOMEM;
+		goto err_release;
+	}
+
+	/* Configure DMA channel to read data register */
+	memset(&config, 0, sizeof(config));
+	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
+	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
+	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
+			  pdmc->rx_buf, pdmc->dma_buf);
+err_release:
+	dma_release_channel(pdmc->dma_chan);
+
+	return ret;
+}
+
+static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_audio *pdmc;
+	struct device_node *np = dev->of_node;
+	struct iio_dev *iio;
+	char *name;
+	int ret, irq, val;
+
+	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
+		return PTR_ERR(iio);
+	}
+
+	pdmc = iio_priv(iio);
+	if (IS_ERR(pdmc)) {
+		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
+		return PTR_ERR(pdmc);
+	}
+	pdmc->dfsdm = dev_get_drvdata(dev->parent);
+
+	iio->name = np->name;
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = &stm32_dfsdm_info_pdmc;
+	iio->modes = INDIO_DIRECT_MODE;
+
+	platform_set_drvdata(pdev, pdmc);
+
+	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
+	if (ret != 0) {
+		dev_err(dev, "Missing reg property\n");
+		return -EINVAL;
+	}
+
+	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
+	iio->name = name;
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 */
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
+			       0, pdev->name, pdmc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request IRQ\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set filter order\n");
+		return ret;
+	}
+	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
+
+	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
+	if (!ret)
+		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
+
+	ret = stm32_dfsdm_audio_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_audio_dma_request(iio);
+	if (ret) {
+		dev_err(&pdev->dev, "DMA request failed\n");
+		return ret;
+	}
+
+	iio->modes |= INDIO_BUFFER_SOFTWARE;
+
+	ret = iio_triggered_buffer_setup(iio,
+					 &iio_pollfunc_store_time,
+					 &stm32_dfsdm_audio_trigger_handler,
+					 &stm32_dfsdm_buffer_setup_ops);
+	if (ret) {
+		dev_err(&pdev->dev, "Buffer setup failed\n");
+		goto err_dma_disable;
+	}
+
+	ret = iio_device_register(iio);
+	if (ret) {
+		dev_err(&pdev->dev, "IIO dev register failed\n");
+		goto err_buffer_cleanup;
+	}
+
+	return 0;
+
+err_buffer_cleanup:
+	iio_triggered_buffer_cleanup(iio);
+
+err_dma_disable:
+	if (pdmc->dma_chan) {
+		dma_free_coherent(pdmc->dma_chan->device->dev,
+				  DFSDM_DMA_BUFFER_SIZE,
+				  pdmc->rx_buf, pdmc->dma_buf);
+		dma_release_channel(pdmc->dma_chan);
+	}
+
+	return ret;
+}
+
+static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
+{
+	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
+	struct iio_dev *iio = iio_priv_to_dev(pdmc);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+static struct platform_driver stm32_dfsdm_audio_driver = {
+	.driver = {
+		.name = "stm32-dfsdm-audio",
+		.of_match_table = stm32_dfsdm_audio_match,
+	},
+	.probe = stm32_dfsdm_audio_probe,
+	.remove = stm32_dfsdm_audio_remove,
+};
+module_platform_driver(stm32_dfsdm_audio_driver);
+
+MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
new file mode 100644
index 0000000..9b41b28
--- /dev/null
+++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
@@ -0,0 +1,41 @@
+/*
+ * This file discribe the STM32 DFSDM IIO driver API for audio part
+ *
+ * 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 STM32_DFSDM_AUDIO_H
+#define STM32_DFSDM_AUDIO_H
+
+/**
+ * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
+ * @dev:	Pointer to client device.
+ * @indio_dev:	Device on which the channel exists.
+ * @cb:		Callback function.
+ *		@data:  pointer to data buffer
+ *		@size: size of the data buffer in bytes
+ * @private:	Private data passed to callback.
+ *
+ */
+int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
+			    int (*cb)(const void *data, size_t size,
+				      void *private),
+			    void *private);
+/**
+ * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
+ * @indio_dev:	Device on which the channel exists.
+ */
+int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
+
+#endif
-- 
1.9.1

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

* [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add DFSDM driver to handle PDM audio microphones.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig                   |  13 +
 drivers/iio/adc/Makefile                  |   1 +
 drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
 include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
 4 files changed, 775 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
 create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3e0eb11..c108933 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-dfsdm-adc.
 
+config STM32_DFSDM_AUDIO
+	tristate "STMicroelectronics STM32 dfsdm audio
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select STM32_DFSDM_CORE
+	select REGMAP_MMIO
+	select IIO_BUFFER_DMAENGINE
+	help
+	  Select this option to support Audio PDM micophone for
+	  STMicroelectronics  STM32 digital filter for sigma delta converter.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-dfsdm-audio.
+
 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 161f271..79f975d 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
 obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
new file mode 100644
index 0000000..115ef8f
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-audio.c
@@ -0,0 +1,720 @@
+/*
+ * This file is the ADC part of of the STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, 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/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "stm32-dfsdm.h"
+
+#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
+
+struct stm32_dfsdm_audio {
+	struct stm32_dfsdm *dfsdm;
+	unsigned int fl_id;
+	unsigned int ch_id;
+	unsigned int spi_freq;  /* SPI bus clock frequency */
+	unsigned int sample_freq; /* Sample frequency after filter decimation */
+
+	u8 *rx_buf;
+	unsigned int bufi; /* Buffer current position */
+	unsigned int buf_sz; /* Buffer size */
+
+	struct dma_chan	*dma_chan;
+	dma_addr_t dma_buf;
+
+	int (*cb)(const void *data, size_t size, void *cb_priv);
+	void *cb_priv;
+};
+
+const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
+
+static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
+				    const struct iio_chan_spec *chan, char *buf)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
+}
+
+static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
+				    const struct iio_chan_spec *chan,
+				    const char *buf, size_t len)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
+	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
+	unsigned int spi_freq = pdmc->spi_freq;
+	unsigned int sample_freq;
+	int ret;
+
+	ret = kstrtoint(buf, 0, &sample_freq);
+	if (ret)
+		return ret;
+
+	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
+	if (!sample_freq)
+		return -EINVAL;
+
+	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
+		spi_freq = pdmc->dfsdm->spi_master_freq;
+
+	if (spi_freq % sample_freq)
+		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
+			 spi_freq / (spi_freq / sample_freq));
+
+	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			"Not able to find filter parameter that match!\n");
+		return ret;
+	}
+	pdmc->sample_freq = sample_freq;
+
+	return len;
+}
+
+static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      char *buf)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
+}
+
+static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      const char *buf, size_t len)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
+	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
+	unsigned int sample_freq = pdmc->sample_freq;
+	unsigned int spi_freq;
+	int ret;
+
+	/* If DFSDM is master on SPI, SPI freq can not be updated */
+	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
+		return -EPERM;
+
+	ret = kstrtoint(buf, 0, &spi_freq);
+	if (ret)
+		return ret;
+
+	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
+	if (!spi_freq)
+		return -EINVAL;
+
+	if (sample_freq) {
+		if (spi_freq % sample_freq)
+			dev_warn(&indio_dev->dev,
+				 "Sampling rate not accurate (%d)\n",
+				 spi_freq / (spi_freq / sample_freq));
+
+		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"No filter parameters that match!\n");
+			return ret;
+		}
+	}
+	pdmc->spi_freq = spi_freq;
+
+	return len;
+}
+
+/*
+ * Define external info for SPI Frequency and audio sampling rate that can be
+ * configured by ASoC driver through consumer.h API
+ */
+static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
+	/* filter oversampling: Post filter oversampling ratio */
+	{
+		.name = "audio_sampling_rate",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = dfsdm_audio_get_rate,
+		.write = dfsdm_audio_set_rate,
+	},
+	/* data_right_bit_shift : Filter output data shifting */
+	{
+		.name = "spi_clk_freq",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = dfsdm_audio_get_spiclk,
+		.write = dfsdm_audio_set_spiclk,
+	},
+	{},
+};
+
+static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
+{
+	struct regmap *regmap = pdmc->dfsdm->regmap;
+	int ret;
+
+	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
+	if (ret < 0)
+		goto stop_dfsdm;
+
+	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
+					   pdmc->ch_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	/* Enable DMA transfer*/
+	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
+				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
+	if (ret < 0)
+		return ret;
+
+	/* Enable conversion triggered by SPI clock*/
+	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
+				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	return 0;
+
+stop_channels:
+	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
+stop_dfsdm:
+	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
+
+	return ret;
+}
+
+static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
+{
+	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
+
+	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
+
+	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
+}
+
+static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
+				     unsigned int val)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
+
+	/*
+	 * DMA cyclic transfers are used, buffer is split into two periods.
+	 * There should be :
+	 * - always one buffer (period) DMA is working on
+	 * - one buffer (period) driver pushed to ASoC side ().
+	 */
+	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
+	pdmc->buf_sz = watermark * 2;
+
+	return 0;
+}
+
+int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
+				 struct iio_trigger *trig)
+{
+	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
+		return 0;
+
+	return -EINVAL;
+}
+
+static const struct iio_info stm32_dfsdm_info_pdmc = {
+	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
+	.driver_module = THIS_MODULE,
+	.validate_trigger = stm32_dfsdm_validate_trigger,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct stm32_dfsdm_audio *pdmc = arg;
+	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
+	struct regmap *regmap = pdmc->dfsdm->regmap;
+	unsigned int status;
+
+	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
+
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
+		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
+				   DFSDM_ICR_CLRROVRF_MASK,
+				   DFSDM_ICR_CLRROVRF_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
+{
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	status = dmaengine_tx_status(pdmc->dma_chan,
+				     pdmc->dma_chan->cookie,
+				     &state);
+	if (status == DMA_IN_PROGRESS) {
+		/* Residue is size in bytes from end of buffer */
+		unsigned int i = pdmc->buf_sz - state.residue;
+		unsigned int size;
+
+		/* Return available bytes */
+		if (i >= pdmc->bufi)
+			size = i - pdmc->bufi;
+		else
+			size = pdmc->buf_sz + i - pdmc->bufi;
+
+		return size;
+	}
+
+	return 0;
+}
+
+static void stm32_dfsdm_audio_dma_buffer_done(void *data)
+{
+	struct iio_dev *indio_dev = data;
+
+	iio_trigger_poll_chained(indio_dev->trig);
+}
+
+static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct dma_async_tx_descriptor *desc;
+	dma_cookie_t cookie;
+	int ret;
+
+	if (!pdmc->dma_chan)
+		return -EINVAL;
+
+	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
+		pdmc->buf_sz, pdmc->buf_sz / 2);
+
+	/* Prepare a DMA cyclic transaction */
+	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
+					 pdmc->dma_buf,
+					 pdmc->buf_sz, pdmc->buf_sz / 2,
+					 DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT);
+	if (!desc)
+		return -EBUSY;
+
+	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
+	desc->callback_param = indio_dev;
+
+	cookie = dmaengine_submit(desc);
+	ret = dma_submit_error(cookie);
+	if (ret) {
+		dmaengine_terminate_all(pdmc->dma_chan);
+		return ret;
+	}
+
+	/* Issue pending DMA requests */
+	dma_async_issue_pending(pdmc->dma_chan);
+
+	return 0;
+}
+
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	/* Reset pdmc buffer index */
+	pdmc->bufi = 0;
+
+	ret = stm32_dfsdm_start_conv(pdmc, false);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Can't start conversion\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_audio_dma_start(indio_dev);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Can't start DMA\n");
+		goto err_stop_conv;
+	}
+
+	ret = iio_triggered_buffer_postenable(indio_dev);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
+		goto err_stop_dma;
+	}
+
+	return 0;
+
+err_stop_dma:
+	if (pdmc->dma_chan)
+		dmaengine_terminate_all(pdmc->dma_chan);
+err_stop_conv:
+	stm32_dfsdm_stop_conv(pdmc);
+
+	return ret;
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	ret = iio_triggered_buffer_predisable(indio_dev);
+	if (ret < 0)
+		dev_err(&indio_dev->dev, "Predisable failed\n");
+
+	if (pdmc->dma_chan)
+		dmaengine_terminate_all(pdmc->dma_chan);
+
+	stm32_dfsdm_stop_conv(pdmc);
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
+	.postenable = &stm32_dfsdm_postenable,
+	.predisable = &stm32_dfsdm_predisable,
+};
+
+static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	size_t old_pos;
+	int available = stm32_dfsdm_audio_avail_data(pdmc);
+
+	/*
+	 * Buffer interface is not support cyclic DMA buffer,and offer only
+	 * an interface to push data samples per samples.
+	 * For this reason iio_push_to_buffers_with_timestamp in not used
+	 * and interface is hacked using a private callback registered by ASoC.
+	 * This should be a temporary solution waiting a cyclic DMA engine
+	 * support in IIO.
+	 */
+
+	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
+		pdmc->bufi, available);
+	old_pos = pdmc->bufi;
+	while (available >= indio_dev->scan_bytes) {
+		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
+
+		/* Mask 8 LSB that contains the channel ID */
+		*buffer &= 0xFFFFFF00;
+		available -= indio_dev->scan_bytes;
+		pdmc->bufi += indio_dev->scan_bytes;
+		if (pdmc->bufi >= pdmc->buf_sz) {
+			if (pdmc->cb)
+				pdmc->cb(&pdmc->rx_buf[old_pos],
+					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
+			pdmc->bufi = 0;
+			old_pos = 0;
+		}
+	}
+	if (pdmc->cb)
+		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
+				pdmc->cb_priv);
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * stm32_dfsdm_get_buff_cb - register a callback
+ *	that will be called when DMA transfer period is achieved.
+ *
+ * @iio_dev: Handle to IIO device.
+ * @cb: pointer to callback function.
+ *	@data: pointer to data buffer
+ *	@size: size in byte of the data buffer
+ *	@private: pointer to consumer private structure
+ * @private: pointer to consumer private structure
+ */
+int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
+			    int (*cb)(const void *data, size_t size,
+				      void *private),
+			    void *private)
+{
+	struct stm32_dfsdm_audio *pdmc;
+
+	if (!iio_dev)
+		return -EINVAL;
+	pdmc = iio_priv(iio_dev);
+
+	if (iio_dev !=  iio_priv_to_dev(pdmc))
+		return -EINVAL;
+
+	pdmc->cb = cb;
+	pdmc->cb_priv = private;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
+
+/**
+ * stm32_dfsdm_release_buff_cb - unregister buffer callback
+ *
+ * @iio_dev: Handle to IIO device.
+ */
+int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc;
+
+	if (!iio_dev)
+		return -EINVAL;
+	pdmc = iio_priv(iio_dev);
+
+	if (iio_dev !=  iio_priv_to_dev(pdmc))
+		return -EINVAL;
+	pdmc->cb = NULL;
+	pdmc->cb_priv = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
+
+static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
+{
+	struct iio_chan_spec *ch;
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
+	if (!ch)
+		return -ENOMEM;
+
+	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
+	if (ret < 0)
+		return ret;
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = 0;
+	ch->ext_info = dfsdm_adc_ext_info;
+
+	ch->scan_type.sign = 's';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+
+	pdmc->ch_id = ch->channel;
+	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
+					 &pdmc->dfsdm->ch_list[ch->channel]);
+
+	indio_dev->num_channels = 1;
+	indio_dev->channels = ch;
+
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_audio_match[] = {
+	{ .compatible = "st,stm32-dfsdm-audio"},
+	{}
+};
+
+static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct dma_slave_config config;
+	int ret;
+
+	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
+	if (!pdmc->dma_chan)
+		return -EINVAL;
+
+	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
+					 DFSDM_DMA_BUFFER_SIZE,
+					 &pdmc->dma_buf, GFP_KERNEL);
+	if (!pdmc->rx_buf) {
+		ret = -ENOMEM;
+		goto err_release;
+	}
+
+	/* Configure DMA channel to read data register */
+	memset(&config, 0, sizeof(config));
+	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
+	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
+	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
+			  pdmc->rx_buf, pdmc->dma_buf);
+err_release:
+	dma_release_channel(pdmc->dma_chan);
+
+	return ret;
+}
+
+static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_audio *pdmc;
+	struct device_node *np = dev->of_node;
+	struct iio_dev *iio;
+	char *name;
+	int ret, irq, val;
+
+	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
+		return PTR_ERR(iio);
+	}
+
+	pdmc = iio_priv(iio);
+	if (IS_ERR(pdmc)) {
+		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
+		return PTR_ERR(pdmc);
+	}
+	pdmc->dfsdm = dev_get_drvdata(dev->parent);
+
+	iio->name = np->name;
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = &stm32_dfsdm_info_pdmc;
+	iio->modes = INDIO_DIRECT_MODE;
+
+	platform_set_drvdata(pdev, pdmc);
+
+	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
+	if (ret != 0) {
+		dev_err(dev, "Missing reg property\n");
+		return -EINVAL;
+	}
+
+	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
+	iio->name = name;
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 */
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
+			       0, pdev->name, pdmc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request IRQ\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set filter order\n");
+		return ret;
+	}
+	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
+
+	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
+	if (!ret)
+		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
+
+	ret = stm32_dfsdm_audio_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_audio_dma_request(iio);
+	if (ret) {
+		dev_err(&pdev->dev, "DMA request failed\n");
+		return ret;
+	}
+
+	iio->modes |= INDIO_BUFFER_SOFTWARE;
+
+	ret = iio_triggered_buffer_setup(iio,
+					 &iio_pollfunc_store_time,
+					 &stm32_dfsdm_audio_trigger_handler,
+					 &stm32_dfsdm_buffer_setup_ops);
+	if (ret) {
+		dev_err(&pdev->dev, "Buffer setup failed\n");
+		goto err_dma_disable;
+	}
+
+	ret = iio_device_register(iio);
+	if (ret) {
+		dev_err(&pdev->dev, "IIO dev register failed\n");
+		goto err_buffer_cleanup;
+	}
+
+	return 0;
+
+err_buffer_cleanup:
+	iio_triggered_buffer_cleanup(iio);
+
+err_dma_disable:
+	if (pdmc->dma_chan) {
+		dma_free_coherent(pdmc->dma_chan->device->dev,
+				  DFSDM_DMA_BUFFER_SIZE,
+				  pdmc->rx_buf, pdmc->dma_buf);
+		dma_release_channel(pdmc->dma_chan);
+	}
+
+	return ret;
+}
+
+static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
+{
+	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
+	struct iio_dev *iio = iio_priv_to_dev(pdmc);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+static struct platform_driver stm32_dfsdm_audio_driver = {
+	.driver = {
+		.name = "stm32-dfsdm-audio",
+		.of_match_table = stm32_dfsdm_audio_match,
+	},
+	.probe = stm32_dfsdm_audio_probe,
+	.remove = stm32_dfsdm_audio_remove,
+};
+module_platform_driver(stm32_dfsdm_audio_driver);
+
+MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
new file mode 100644
index 0000000..9b41b28
--- /dev/null
+++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
@@ -0,0 +1,41 @@
+/*
+ * This file discribe the STM32 DFSDM IIO driver API for audio part
+ *
+ * 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 STM32_DFSDM_AUDIO_H
+#define STM32_DFSDM_AUDIO_H
+
+/**
+ * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
+ * @dev:	Pointer to client device.
+ * @indio_dev:	Device on which the channel exists.
+ * @cb:		Callback function.
+ *		@data:  pointer to data buffer
+ *		@size: size of the data buffer in bytes
+ * @private:	Private data passed to callback.
+ *
+ */
+int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
+			    int (*cb)(const void *data, size_t size,
+				      void *private),
+			    void *private);
+/**
+ * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
+ * @indio_dev:	Device on which the channel exists.
+ */
+int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
+
+#endif
-- 
1.9.1


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

* [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add DFSDM driver to handle PDM audio microphones.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig                   |  13 +
 drivers/iio/adc/Makefile                  |   1 +
 drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
 include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
 4 files changed, 775 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
 create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3e0eb11..c108933 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-dfsdm-adc.
 
+config STM32_DFSDM_AUDIO
+	tristate "STMicroelectronics STM32 dfsdm audio
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select STM32_DFSDM_CORE
+	select REGMAP_MMIO
+	select IIO_BUFFER_DMAENGINE
+	help
+	  Select this option to support Audio PDM micophone for
+	  STMicroelectronics  STM32 digital filter for sigma delta converter.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-dfsdm-audio.
+
 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 161f271..79f975d 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
 obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
new file mode 100644
index 0000000..115ef8f
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-audio.c
@@ -0,0 +1,720 @@
+/*
+ * This file is the ADC part of of the STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, 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/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "stm32-dfsdm.h"
+
+#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
+
+struct stm32_dfsdm_audio {
+	struct stm32_dfsdm *dfsdm;
+	unsigned int fl_id;
+	unsigned int ch_id;
+	unsigned int spi_freq;  /* SPI bus clock frequency */
+	unsigned int sample_freq; /* Sample frequency after filter decimation */
+
+	u8 *rx_buf;
+	unsigned int bufi; /* Buffer current position */
+	unsigned int buf_sz; /* Buffer size */
+
+	struct dma_chan	*dma_chan;
+	dma_addr_t dma_buf;
+
+	int (*cb)(const void *data, size_t size, void *cb_priv);
+	void *cb_priv;
+};
+
+const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
+
+static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
+				    const struct iio_chan_spec *chan, char *buf)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
+}
+
+static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
+				    const struct iio_chan_spec *chan,
+				    const char *buf, size_t len)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
+	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
+	unsigned int spi_freq = pdmc->spi_freq;
+	unsigned int sample_freq;
+	int ret;
+
+	ret = kstrtoint(buf, 0, &sample_freq);
+	if (ret)
+		return ret;
+
+	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
+	if (!sample_freq)
+		return -EINVAL;
+
+	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
+		spi_freq = pdmc->dfsdm->spi_master_freq;
+
+	if (spi_freq % sample_freq)
+		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
+			 spi_freq / (spi_freq / sample_freq));
+
+	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			"Not able to find filter parameter that match!\n");
+		return ret;
+	}
+	pdmc->sample_freq = sample_freq;
+
+	return len;
+}
+
+static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      char *buf)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
+}
+
+static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
+				      const struct iio_chan_spec *chan,
+				      const char *buf, size_t len)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
+	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
+	unsigned int sample_freq = pdmc->sample_freq;
+	unsigned int spi_freq;
+	int ret;
+
+	/* If DFSDM is master on SPI, SPI freq can not be updated */
+	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
+		return -EPERM;
+
+	ret = kstrtoint(buf, 0, &spi_freq);
+	if (ret)
+		return ret;
+
+	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
+	if (!spi_freq)
+		return -EINVAL;
+
+	if (sample_freq) {
+		if (spi_freq % sample_freq)
+			dev_warn(&indio_dev->dev,
+				 "Sampling rate not accurate (%d)\n",
+				 spi_freq / (spi_freq / sample_freq));
+
+		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
+		if (ret < 0) {
+			dev_err(&indio_dev->dev,
+				"No filter parameters that match!\n");
+			return ret;
+		}
+	}
+	pdmc->spi_freq = spi_freq;
+
+	return len;
+}
+
+/*
+ * Define external info for SPI Frequency and audio sampling rate that can be
+ * configured by ASoC driver through consumer.h API
+ */
+static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
+	/* filter oversampling: Post filter oversampling ratio */
+	{
+		.name = "audio_sampling_rate",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = dfsdm_audio_get_rate,
+		.write = dfsdm_audio_set_rate,
+	},
+	/* data_right_bit_shift : Filter output data shifting */
+	{
+		.name = "spi_clk_freq",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = dfsdm_audio_get_spiclk,
+		.write = dfsdm_audio_set_spiclk,
+	},
+	{},
+};
+
+static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
+{
+	struct regmap *regmap = pdmc->dfsdm->regmap;
+	int ret;
+
+	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
+	if (ret < 0)
+		goto stop_dfsdm;
+
+	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
+					   pdmc->ch_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	/* Enable DMA transfer*/
+	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
+				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
+	if (ret < 0)
+		return ret;
+
+	/* Enable conversion triggered by SPI clock*/
+	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
+				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
+	if (ret < 0)
+		goto stop_channels;
+
+	return 0;
+
+stop_channels:
+	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
+stop_dfsdm:
+	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
+
+	return ret;
+}
+
+static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
+{
+	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
+
+	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
+
+	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
+}
+
+static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
+				     unsigned int val)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
+
+	/*
+	 * DMA cyclic transfers are used, buffer is split into two periods.
+	 * There should be :
+	 * - always one buffer (period) DMA is working on
+	 * - one buffer (period) driver pushed to ASoC side ().
+	 */
+	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
+	pdmc->buf_sz = watermark * 2;
+
+	return 0;
+}
+
+int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
+				 struct iio_trigger *trig)
+{
+	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
+		return 0;
+
+	return -EINVAL;
+}
+
+static const struct iio_info stm32_dfsdm_info_pdmc = {
+	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
+	.driver_module = THIS_MODULE,
+	.validate_trigger = stm32_dfsdm_validate_trigger,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct stm32_dfsdm_audio *pdmc = arg;
+	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
+	struct regmap *regmap = pdmc->dfsdm->regmap;
+	unsigned int status;
+
+	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
+
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
+		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
+				   DFSDM_ICR_CLRROVRF_MASK,
+				   DFSDM_ICR_CLRROVRF_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
+{
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	status = dmaengine_tx_status(pdmc->dma_chan,
+				     pdmc->dma_chan->cookie,
+				     &state);
+	if (status == DMA_IN_PROGRESS) {
+		/* Residue is size in bytes from end of buffer */
+		unsigned int i = pdmc->buf_sz - state.residue;
+		unsigned int size;
+
+		/* Return available bytes */
+		if (i >= pdmc->bufi)
+			size = i - pdmc->bufi;
+		else
+			size = pdmc->buf_sz + i - pdmc->bufi;
+
+		return size;
+	}
+
+	return 0;
+}
+
+static void stm32_dfsdm_audio_dma_buffer_done(void *data)
+{
+	struct iio_dev *indio_dev = data;
+
+	iio_trigger_poll_chained(indio_dev->trig);
+}
+
+static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct dma_async_tx_descriptor *desc;
+	dma_cookie_t cookie;
+	int ret;
+
+	if (!pdmc->dma_chan)
+		return -EINVAL;
+
+	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
+		pdmc->buf_sz, pdmc->buf_sz / 2);
+
+	/* Prepare a DMA cyclic transaction */
+	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
+					 pdmc->dma_buf,
+					 pdmc->buf_sz, pdmc->buf_sz / 2,
+					 DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT);
+	if (!desc)
+		return -EBUSY;
+
+	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
+	desc->callback_param = indio_dev;
+
+	cookie = dmaengine_submit(desc);
+	ret = dma_submit_error(cookie);
+	if (ret) {
+		dmaengine_terminate_all(pdmc->dma_chan);
+		return ret;
+	}
+
+	/* Issue pending DMA requests */
+	dma_async_issue_pending(pdmc->dma_chan);
+
+	return 0;
+}
+
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	/* Reset pdmc buffer index */
+	pdmc->bufi = 0;
+
+	ret = stm32_dfsdm_start_conv(pdmc, false);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Can't start conversion\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_audio_dma_start(indio_dev);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Can't start DMA\n");
+		goto err_stop_conv;
+	}
+
+	ret = iio_triggered_buffer_postenable(indio_dev);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
+		goto err_stop_dma;
+	}
+
+	return 0;
+
+err_stop_dma:
+	if (pdmc->dma_chan)
+		dmaengine_terminate_all(pdmc->dma_chan);
+err_stop_conv:
+	stm32_dfsdm_stop_conv(pdmc);
+
+	return ret;
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	ret = iio_triggered_buffer_predisable(indio_dev);
+	if (ret < 0)
+		dev_err(&indio_dev->dev, "Predisable failed\n");
+
+	if (pdmc->dma_chan)
+		dmaengine_terminate_all(pdmc->dma_chan);
+
+	stm32_dfsdm_stop_conv(pdmc);
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
+	.postenable = &stm32_dfsdm_postenable,
+	.predisable = &stm32_dfsdm_predisable,
+};
+
+static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	size_t old_pos;
+	int available = stm32_dfsdm_audio_avail_data(pdmc);
+
+	/*
+	 * Buffer interface is not support cyclic DMA buffer,and offer only
+	 * an interface to push data samples per samples.
+	 * For this reason iio_push_to_buffers_with_timestamp in not used
+	 * and interface is hacked using a private callback registered by ASoC.
+	 * This should be a temporary solution waiting a cyclic DMA engine
+	 * support in IIO.
+	 */
+
+	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
+		pdmc->bufi, available);
+	old_pos = pdmc->bufi;
+	while (available >= indio_dev->scan_bytes) {
+		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
+
+		/* Mask 8 LSB that contains the channel ID */
+		*buffer &= 0xFFFFFF00;
+		available -= indio_dev->scan_bytes;
+		pdmc->bufi += indio_dev->scan_bytes;
+		if (pdmc->bufi >= pdmc->buf_sz) {
+			if (pdmc->cb)
+				pdmc->cb(&pdmc->rx_buf[old_pos],
+					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
+			pdmc->bufi = 0;
+			old_pos = 0;
+		}
+	}
+	if (pdmc->cb)
+		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
+				pdmc->cb_priv);
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * stm32_dfsdm_get_buff_cb - register a callback
+ *	that will be called when DMA transfer period is achieved.
+ *
+ * @iio_dev: Handle to IIO device.
+ * @cb: pointer to callback function.
+ *	@data: pointer to data buffer
+ *	@size: size in byte of the data buffer
+ *	@private: pointer to consumer private structure
+ * @private: pointer to consumer private structure
+ */
+int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
+			    int (*cb)(const void *data, size_t size,
+				      void *private),
+			    void *private)
+{
+	struct stm32_dfsdm_audio *pdmc;
+
+	if (!iio_dev)
+		return -EINVAL;
+	pdmc = iio_priv(iio_dev);
+
+	if (iio_dev !=  iio_priv_to_dev(pdmc))
+		return -EINVAL;
+
+	pdmc->cb = cb;
+	pdmc->cb_priv = private;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
+
+/**
+ * stm32_dfsdm_release_buff_cb - unregister buffer callback
+ *
+ * @iio_dev: Handle to IIO device.
+ */
+int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc;
+
+	if (!iio_dev)
+		return -EINVAL;
+	pdmc = iio_priv(iio_dev);
+
+	if (iio_dev !=  iio_priv_to_dev(pdmc))
+		return -EINVAL;
+	pdmc->cb = NULL;
+	pdmc->cb_priv = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
+
+static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
+{
+	struct iio_chan_spec *ch;
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	int ret;
+
+	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
+	if (!ch)
+		return -ENOMEM;
+
+	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
+	if (ret < 0)
+		return ret;
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = 0;
+	ch->ext_info = dfsdm_adc_ext_info;
+
+	ch->scan_type.sign = 's';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+
+	pdmc->ch_id = ch->channel;
+	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
+					 &pdmc->dfsdm->ch_list[ch->channel]);
+
+	indio_dev->num_channels = 1;
+	indio_dev->channels = ch;
+
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_audio_match[] = {
+	{ .compatible = "st,stm32-dfsdm-audio"},
+	{}
+};
+
+static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
+	struct dma_slave_config config;
+	int ret;
+
+	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
+	if (!pdmc->dma_chan)
+		return -EINVAL;
+
+	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
+					 DFSDM_DMA_BUFFER_SIZE,
+					 &pdmc->dma_buf, GFP_KERNEL);
+	if (!pdmc->rx_buf) {
+		ret = -ENOMEM;
+		goto err_release;
+	}
+
+	/* Configure DMA channel to read data register */
+	memset(&config, 0, sizeof(config));
+	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
+	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
+	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
+	if (ret)
+		goto err_free;
+
+	return 0;
+
+err_free:
+	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
+			  pdmc->rx_buf, pdmc->dma_buf);
+err_release:
+	dma_release_channel(pdmc->dma_chan);
+
+	return ret;
+}
+
+static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_audio *pdmc;
+	struct device_node *np = dev->of_node;
+	struct iio_dev *iio;
+	char *name;
+	int ret, irq, val;
+
+	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
+		return PTR_ERR(iio);
+	}
+
+	pdmc = iio_priv(iio);
+	if (IS_ERR(pdmc)) {
+		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
+		return PTR_ERR(pdmc);
+	}
+	pdmc->dfsdm = dev_get_drvdata(dev->parent);
+
+	iio->name = np->name;
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = &stm32_dfsdm_info_pdmc;
+	iio->modes = INDIO_DIRECT_MODE;
+
+	platform_set_drvdata(pdev, pdmc);
+
+	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
+	if (ret != 0) {
+		dev_err(dev, "Missing reg property\n");
+		return -EINVAL;
+	}
+
+	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
+	iio->name = name;
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 */
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
+			       0, pdev->name, pdmc);
+	if (ret < 0) {
+		dev_err(dev, "Failed to request IRQ\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set filter order\n");
+		return ret;
+	}
+	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
+
+	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
+	if (!ret)
+		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
+
+	ret = stm32_dfsdm_audio_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_audio_dma_request(iio);
+	if (ret) {
+		dev_err(&pdev->dev, "DMA request failed\n");
+		return ret;
+	}
+
+	iio->modes |= INDIO_BUFFER_SOFTWARE;
+
+	ret = iio_triggered_buffer_setup(iio,
+					 &iio_pollfunc_store_time,
+					 &stm32_dfsdm_audio_trigger_handler,
+					 &stm32_dfsdm_buffer_setup_ops);
+	if (ret) {
+		dev_err(&pdev->dev, "Buffer setup failed\n");
+		goto err_dma_disable;
+	}
+
+	ret = iio_device_register(iio);
+	if (ret) {
+		dev_err(&pdev->dev, "IIO dev register failed\n");
+		goto err_buffer_cleanup;
+	}
+
+	return 0;
+
+err_buffer_cleanup:
+	iio_triggered_buffer_cleanup(iio);
+
+err_dma_disable:
+	if (pdmc->dma_chan) {
+		dma_free_coherent(pdmc->dma_chan->device->dev,
+				  DFSDM_DMA_BUFFER_SIZE,
+				  pdmc->rx_buf, pdmc->dma_buf);
+		dma_release_channel(pdmc->dma_chan);
+	}
+
+	return ret;
+}
+
+static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
+{
+	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
+	struct iio_dev *iio = iio_priv_to_dev(pdmc);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+static struct platform_driver stm32_dfsdm_audio_driver = {
+	.driver = {
+		.name = "stm32-dfsdm-audio",
+		.of_match_table = stm32_dfsdm_audio_match,
+	},
+	.probe = stm32_dfsdm_audio_probe,
+	.remove = stm32_dfsdm_audio_remove,
+};
+module_platform_driver(stm32_dfsdm_audio_driver);
+
+MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
new file mode 100644
index 0000000..9b41b28
--- /dev/null
+++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
@@ -0,0 +1,41 @@
+/*
+ * This file discribe the STM32 DFSDM IIO driver API for audio part
+ *
+ * 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 STM32_DFSDM_AUDIO_H
+#define STM32_DFSDM_AUDIO_H
+
+/**
+ * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
+ * @dev:	Pointer to client device.
+ * @indio_dev:	Device on which the channel exists.
+ * @cb:		Callback function.
+ *		@data:  pointer to data buffer
+ *		@size: size of the data buffer in bytes
+ * @private:	Private data passed to callback.
+ *
+ */
+int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
+			    int (*cb)(const void *data, size_t size,
+				      void *private),
+			    void *private);
+/**
+ * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
+ * @indio_dev:	Device on which the channel exists.
+ */
+int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
+
+#endif
-- 
1.9.1

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

* [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add iio consumer API to set buffer size and watermark according
to sysfs API.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
 include/linux/iio/consumer.h                | 13 +++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
index b8f550e..43c066a 100644
--- a/drivers/iio/buffer/industrialio-buffer-cb.c
+++ b/drivers/iio/buffer/industrialio-buffer-cb.c
@@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
 
+int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
+				      size_t length, size_t watermark)
+{
+	if (!length || length < watermark)
+		return -EINVAL;
+	cb_buff->buffer.watermark = watermark;
+	cb_buff->buffer.length = length;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
+
 int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
 {
 	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index cb44771..0f6e94d 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
 						       void *private),
 					     void *private);
 /**
+ * iio_channel_cb_set_buffer_size() - set the buffer length.
+ * @cb_buffer:		The callback buffer from whom we want the channel
+ *			information.
+ * @length: buffer length in bytes
+ * @watermark: buffer watermark in bytes
+ *
+ * This function allows to configure the buffer length. The watermark if
+ * forced to half of the buffer.
+ */
+int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
+				     size_t length, size_t watermark);
+
+/**
  * iio_channel_release_all_cb() - release and unregister the callback.
  * @cb_buffer:		The callback buffer that was allocated.
  */
-- 
1.9.1

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

* [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add iio consumer API to set buffer size and watermark according
to sysfs API.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
 include/linux/iio/consumer.h                | 13 +++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
index b8f550e..43c066a 100644
--- a/drivers/iio/buffer/industrialio-buffer-cb.c
+++ b/drivers/iio/buffer/industrialio-buffer-cb.c
@@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
 
+int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
+				      size_t length, size_t watermark)
+{
+	if (!length || length < watermark)
+		return -EINVAL;
+	cb_buff->buffer.watermark = watermark;
+	cb_buff->buffer.length = length;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
+
 int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
 {
 	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index cb44771..0f6e94d 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
 						       void *private),
 					     void *private);
 /**
+ * iio_channel_cb_set_buffer_size() - set the buffer length.
+ * @cb_buffer:		The callback buffer from whom we want the channel
+ *			information.
+ * @length: buffer length in bytes
+ * @watermark: buffer watermark in bytes
+ *
+ * This function allows to configure the buffer length. The watermark if
+ * forced to half of the buffer.
+ */
+int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
+				     size_t length, size_t watermark);
+
+/**
  * iio_channel_release_all_cb() - release and unregister the callback.
  * @cb_buffer:		The callback buffer that was allocated.
  */
-- 
1.9.1


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

* [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add iio consumer API to set buffer size and watermark according
to sysfs API.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
 include/linux/iio/consumer.h                | 13 +++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
index b8f550e..43c066a 100644
--- a/drivers/iio/buffer/industrialio-buffer-cb.c
+++ b/drivers/iio/buffer/industrialio-buffer-cb.c
@@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
 
+int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
+				      size_t length, size_t watermark)
+{
+	if (!length || length < watermark)
+		return -EINVAL;
+	cb_buff->buffer.watermark = watermark;
+	cb_buff->buffer.length = length;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
+
 int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
 {
 	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index cb44771..0f6e94d 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
 						       void *private),
 					     void *private);
 /**
+ * iio_channel_cb_set_buffer_size() - set the buffer length.
+ * @cb_buffer:		The callback buffer from whom we want the channel
+ *			information.
+ * @length: buffer length in bytes
+ * @watermark: buffer watermark in bytes
+ *
+ * This function allows to configure the buffer length. The watermark if
+ * forced to half of the buffer.
+ */
+int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
+				     size_t length, size_t watermark);
+
+/**
  * iio_channel_release_all_cb() - release and unregister the callback.
  * @cb_buffer:		The callback buffer that was allocated.
  */
-- 
1.9.1

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

* [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

This patch adds documentation of device tree bindings for audio DMIC codec.

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

diff --git a/Documentation/devicetree/bindings/sound/dmic.txt b/Documentation/devicetree/bindings/sound/dmic.txt
new file mode 100644
index 0000000..eb2a9d4
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/dmic.txt
@@ -0,0 +1,11 @@
+Device-Tree bindings for dmic codec
+
+Required properties:
+	- compatible: should be "dmic-codec".
+
+Example node:
+
+	dmic_audio: dmic_audio@0 {
+		compatible = "dmic-codec";
+		status = "okay";
+	};
-- 
1.9.1

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

* [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

This patch adds documentation of device tree bindings for audio DMIC codec.

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

diff --git a/Documentation/devicetree/bindings/sound/dmic.txt b/Documentation/devicetree/bindings/sound/dmic.txt
new file mode 100644
index 0000000..eb2a9d4
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/dmic.txt
@@ -0,0 +1,11 @@
+Device-Tree bindings for dmic codec
+
+Required properties:
+	- compatible: should be "dmic-codec".
+
+Example node:
+
+	dmic_audio: dmic_audio@0 {
+		compatible = "dmic-codec";
+		status = "okay";
+	};
-- 
1.9.1


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

* [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds documentation of device tree bindings for audio DMIC codec.

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

diff --git a/Documentation/devicetree/bindings/sound/dmic.txt b/Documentation/devicetree/bindings/sound/dmic.txt
new file mode 100644
index 0000000..eb2a9d4
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/dmic.txt
@@ -0,0 +1,11 @@
+Device-Tree bindings for dmic codec
+
+Required properties:
+	- compatible: should be "dmic-codec".
+
+Example node:
+
+	dmic_audio: dmic_audio at 0 {
+		compatible = "dmic-codec";
+		status = "okay";
+	};
-- 
1.9.1

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

* [PATCH v3 09/11] ASoC: codec: add DT support in dmic codec
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add of_table to allows DT probing.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 sound/soc/codecs/dmic.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index c82b9dc..d04cac9 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -73,9 +73,17 @@ static int dmic_dev_remove(struct platform_device *pdev)
 
 MODULE_ALIAS("platform:dmic-codec");
 
+static const struct of_device_id dmic_dev_match[] = {
+	{
+		.compatible = "dmic-codec",
+	},
+	{},
+};
+
 static struct platform_driver dmic_driver = {
 	.driver = {
 		.name = "dmic-codec",
+		.of_match_table = dmic_dev_match,
 	},
 	.probe = dmic_dev_probe,
 	.remove = dmic_dev_remove,
-- 
1.9.1

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

* [PATCH v3 09/11] ASoC: codec: add DT support in dmic codec
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add of_table to allows DT probing.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 sound/soc/codecs/dmic.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index c82b9dc..d04cac9 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -73,9 +73,17 @@ static int dmic_dev_remove(struct platform_device *pdev)
 
 MODULE_ALIAS("platform:dmic-codec");
 
+static const struct of_device_id dmic_dev_match[] = {
+	{
+		.compatible = "dmic-codec",
+	},
+	{},
+};
+
 static struct platform_driver dmic_driver = {
 	.driver = {
 		.name = "dmic-codec",
+		.of_match_table = dmic_dev_match,
 	},
 	.probe = dmic_dev_probe,
 	.remove = dmic_dev_remove,
-- 
1.9.1


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

* [PATCH v3 09/11] ASoC: codec: add DT support in dmic codec
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add of_table to allows DT probing.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 sound/soc/codecs/dmic.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index c82b9dc..d04cac9 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -73,9 +73,17 @@ static int dmic_dev_remove(struct platform_device *pdev)
 
 MODULE_ALIAS("platform:dmic-codec");
 
+static const struct of_device_id dmic_dev_match[] = {
+	{
+		.compatible = "dmic-codec",
+	},
+	{},
+};
+
 static struct platform_driver dmic_driver = {
 	.driver = {
 		.name = "dmic-codec",
+		.of_match_table = dmic_dev_match,
 	},
 	.probe = dmic_dev_probe,
 	.remove = dmic_dev_remove,
-- 
1.9.1

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

* [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add bindings that describes audio settings to support
Digital Filter for pulse density modulation(PDM) microphone.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
   Fixes based on V2 comments

 .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt

diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
new file mode 100644
index 0000000..ab610bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
@@ -0,0 +1,40 @@
+STMicroelectronics audio DFSDM DT bindings
+
+This driver supports audio PDM microphone capture through Digital Filter format
+Sigma Delta modulators (DFSDM).
+
+Required properties:
+  - compatible: "st,stm32h7-adfsdm".
+
+  - #sound-dai-cells : Must be equal to 0
+
+  - io-channels : phandle to iio dfsdm instance node.
+
+
+Example of a simple sound card using audio DFSDM node.
+
+	dmic0: dmic_@0 {
+		compatible = "dmic-codec";
+		#sound-dai-cells = <0>;
+	};
+
+	asoc-pdm@0 {
+		compatible = "st,stm32h7-adfsdm";
+		#sound-dai-cells = <0>;
+		io-channels = <&dfsdm_adc0 0>;
+	};
+
+	sound_dfsdm_pdm {
+ 		compatible = "simple-audio-card";
+ 		simple-audio-card,name = "dfsdm_pdm";
+
+ 		dfsdm0_mic0: simple-audio-card,dai-link@0 {
+ 			format = "pdm";
+ 			cpu {
+ 				sound-dai = <&asoc_pdm1>;
+ 			};
+ 			dmic0_codec: codec {
+ 				sound-dai = <&dmic0>;
+ 			};
+ 		};
+	};
\ No newline at end of file
-- 
1.9.1

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

* [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add bindings that describes audio settings to support
Digital Filter for pulse density modulation(PDM) microphone.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
   Fixes based on V2 comments

 .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt

diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
new file mode 100644
index 0000000..ab610bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
@@ -0,0 +1,40 @@
+STMicroelectronics audio DFSDM DT bindings
+
+This driver supports audio PDM microphone capture through Digital Filter format
+Sigma Delta modulators (DFSDM).
+
+Required properties:
+  - compatible: "st,stm32h7-adfsdm".
+
+  - #sound-dai-cells : Must be equal to 0
+
+  - io-channels : phandle to iio dfsdm instance node.
+
+
+Example of a simple sound card using audio DFSDM node.
+
+	dmic0: dmic_@0 {
+		compatible = "dmic-codec";
+		#sound-dai-cells = <0>;
+	};
+
+	asoc-pdm@0 {
+		compatible = "st,stm32h7-adfsdm";
+		#sound-dai-cells = <0>;
+		io-channels = <&dfsdm_adc0 0>;
+	};
+
+	sound_dfsdm_pdm {
+ 		compatible = "simple-audio-card";
+ 		simple-audio-card,name = "dfsdm_pdm";
+
+ 		dfsdm0_mic0: simple-audio-card,dai-link@0 {
+ 			format = "pdm";
+ 			cpu {
+ 				sound-dai = <&asoc_pdm1>;
+ 			};
+ 			dmic0_codec: codec {
+ 				sound-dai = <&dmic0>;
+ 			};
+ 		};
+	};
\ No newline at end of file
-- 
1.9.1


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

* [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add bindings that describes audio settings to support
Digital Filter for pulse density modulation(PDM) microphone.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
   Fixes based on V2 comments

 .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt

diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
new file mode 100644
index 0000000..ab610bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
@@ -0,0 +1,40 @@
+STMicroelectronics audio DFSDM DT bindings
+
+This driver supports audio PDM microphone capture through Digital Filter format
+Sigma Delta modulators (DFSDM).
+
+Required properties:
+  - compatible: "st,stm32h7-adfsdm".
+
+  - #sound-dai-cells : Must be equal to 0
+
+  - io-channels : phandle to iio dfsdm instance node.
+
+
+Example of a simple sound card using audio DFSDM node.
+
+	dmic0: dmic_ at 0 {
+		compatible = "dmic-codec";
+		#sound-dai-cells = <0>;
+	};
+
+	asoc-pdm at 0 {
+		compatible = "st,stm32h7-adfsdm";
+		#sound-dai-cells = <0>;
+		io-channels = <&dfsdm_adc0 0>;
+	};
+
+	sound_dfsdm_pdm {
+ 		compatible = "simple-audio-card";
+ 		simple-audio-card,name = "dfsdm_pdm";
+
+ 		dfsdm0_mic0: simple-audio-card,dai-link at 0 {
+ 			format = "pdm";
+ 			cpu {
+ 				sound-dai = <&asoc_pdm1>;
+ 			};
+ 			dmic0_codec: codec {
+ 				sound-dai = <&dmic0>;
+ 			};
+ 		};
+	};
\ No newline at end of file
-- 
1.9.1

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

* [PATCH v3 11/11] ASoC: stm32: add DFSDM DAI support
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

Add driver to handle DAI interface for PDM microphones connected
to Digital Filter for Sigma Delta mModulators IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
       	- Suppress DMA engine
        - Suppress copy ops
        - Use IIO consmumer interface to enable/Disable DFSDM         
---
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 ++
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 362 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 376 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..041ddb9
--- /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 && STM32_DFSDM_ADC) || 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..05c4186
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,362 @@
+/*
+ * 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 <linux/iio/iio.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/adc/stm32-dfsdm-audio.h>
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define STM32_ADFSDM_DRV_NAME "stm32-adfsdm"
+
+#define DFSDM_MAX_PERIOD_SIZE	(PAGE_SIZE / 2)
+#define DFSDM_MAX_PERIODS	6
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai_drv;
+	struct snd_pcm_substream *substream;
+
+	/* IIO */
+	struct iio_channel *iio_ch;
+	struct iio_cb_buffer *iio_cb;
+	bool iio_active;
+
+	/* PCM buffer */
+	unsigned char *pcm_buff;
+	unsigned int pos;
+};
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE,
+	.formats = SNDRV_PCM_FMTBIT_S32_LE,
+
+	.rate_min = 8000,
+	.rate_max = 32000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = DFSDM_MAX_PERIODS,
+
+	.period_bytes_min = 1,
+	.period_bytes_max = DFSDM_MAX_PERIOD_SIZE,
+	.buffer_bytes_max = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE
+};
+
+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);
+
+	if (priv->iio_active) {
+		iio_channel_stop_all_cb(priv->iio_cb);
+		priv->iio_active = false;
+	}
+}
+
+int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	char str_rate[10];
+	ssize_t size;
+	int ret = 0;
+
+	dev_dbg(dai->dev, "%s: rate :%d\n", __func__,
+		substream->runtime->rate);
+
+	snprintf(str_rate, sizeof(str_rate), "%d\n", substream->runtime->rate);
+	size = iio_write_channel_ext_info(priv->iio_ch, "audio_sampling_rate",
+					  str_rate, sizeof(str_rate));
+	if (size != sizeof(str_rate)) {
+		dev_err(dai->dev, "%s: Failed to set %s sampling rate\n",
+			__func__, str_rate);
+		return -EINVAL;
+	}
+
+	if (!priv->iio_active) {
+		ret = iio_channel_start_all_cb(priv->iio_cb);
+		if (!ret)
+			priv->iio_active = true;
+		else
+		dev_err(dai->dev, "%s: Failed to start IIO channel (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+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);
+	ssize_t size;
+
+	dev_dbg(dai->dev, "%s: Enter for freq %d\n", __func__, freq);
+
+	/* Set IIO frequency if CODEC is master as clock comes from SPI_IN*/
+	if (dir == SND_SOC_CLOCK_IN) {
+		char str_freq[10];
+
+		snprintf(str_freq, sizeof(str_freq), "%d\n", freq);
+		size = iio_write_channel_ext_info(priv->iio_ch, "spi_clk_freq",
+						  str_freq, sizeof(str_freq));
+		if (size != sizeof(str_freq)) {
+			dev_err(dai->dev, "%s: Failed to set SPI clock\n",
+				__func__);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.shutdown = stm32_adfsdm_shutdown,
+	.prepare = stm32_adfsdm_dai_prepare,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+};
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S32_LE,
+		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			      SNDRV_PCM_RATE_32000),
+		    },
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "stm32_dfsdm_audio",
+};
+
+static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private)
+{
+	struct stm32_adfsdm_priv *priv = private;
+	struct snd_soc_pcm_runtime *rtd = priv->substream->private_data;
+	u8 *pcm_buff = priv->pcm_buff;
+	u8 *src_buff = (u8 *)data;
+	unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream);
+	unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream);
+	unsigned int old_pos = priv->pos;
+	unsigned int cur_size = size;
+
+	dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %d\n",
+		__func__, &pcm_buff[priv->pos], priv->pos, size);
+
+	if ((priv->pos + size) > buff_size) {
+		memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos);
+		cur_size -= buff_size - priv->pos;
+		priv->pos = 0;
+	}
+
+	memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size);
+	priv->pos = (priv->pos + cur_size) % buff_size;
+
+	if ((cur_size != size) || (old_pos && (old_pos % period_size < size)))
+		snd_pcm_period_elapsed(priv->substream);
+
+	return 0;
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		priv->pos = 0;
+		return stm32_dfsdm_get_buff_cb(priv->iio_ch->indio_dev,
+					stm32_afsdm_pcm_cb, priv);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		return stm32_dfsdm_release_buff_cb(priv->iio_ch->indio_dev);
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int ret;
+
+	ret =  snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw);
+	if (!ret)
+		priv->substream = substream;
+
+	return ret;
+}
+
+static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	snd_pcm_lib_free_pages(substream);
+	priv->substream = NULL;
+
+	return 0;
+}
+
+static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
+					    struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	return bytes_to_frames(substream->runtime, priv->pos);
+}
+
+static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	unsigned int period_bytes;
+	int ret;
+
+	period_bytes = params_buffer_bytes(params) / params_periods(params);
+	ret =  snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+	priv->pcm_buff = substream->runtime->dma_area;
+
+	return iio_channel_cb_set_buffer_params(priv->iio_cb,
+						params_buffer_size(params),
+						params_period_size(params));
+}
+
+static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+
+	return 0;
+}
+
+static struct snd_pcm_ops stm32_adfsdm_pcm_ops = {
+	.open		= stm32_adfsdm_pcm_open,
+	.close		= stm32_adfsdm_pcm_close,
+	.hw_params	= stm32_adfsdm_pcm_hw_params,
+	.hw_free	= stm32_adfsdm_pcm_hw_free,
+	.trigger	= stm32_adfsdm_trigger,
+	.pointer	= stm32_adfsdm_pcm_pointer,
+};
+
+static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE;
+
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						     card->dev, size, size);
+}
+
+static struct snd_soc_platform_driver stm32_adfsdm_soc_platform = {
+	.ops		= &stm32_adfsdm_pcm_ops,
+	.pcm_new	= stm32_adfsdm_pcm_new,
+};
+
+static const struct of_device_id stm32_adfsdm_of_match[] = {
+	{.compatible = "st,stm32h7-adfsdm"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dai_drv = stm32_adfsdm_dai;
+	priv->dai_drv.name = pdev->dev.of_node->name;
+	priv->dai_drv.capture.stream_name = pdev->dev.of_node->name;
+
+	dev_set_drvdata(&pdev->dev, priv);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_adfsdm_dai_component,
+					      &priv->dai_drv, 1);
+	if (ret < 0)
+		return ret;
+
+	/* Associate iio channel */
+	priv->iio_ch  = devm_iio_channel_get_all(&pdev->dev);
+	if (IS_ERR(priv->iio_ch))
+		return -ENODEV;
+
+	priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, NULL, NULL);
+	if (IS_ERR(priv->iio_cb))
+		return -ENODEV;
+
+	ret = devm_snd_soc_register_platform(&pdev->dev,
+					     &stm32_adfsdm_soc_platform);
+	if (ret < 0)
+		dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
+			__func__);
+
+	return 0;
+}
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.probe = stm32_adfsdm_probe,
+	.driver = {
+		   .name = STM32_ADFSDM_DRV_NAME,
+		   .of_match_table = stm32_adfsdm_of_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");
+MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);
-- 
1.9.1

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

* [PATCH v3 11/11] ASoC: stm32: add DFSDM DAI support
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen,
	olivier moysan

Add driver to handle DAI interface for PDM microphones connected
to Digital Filter for Sigma Delta mModulators IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
       	- Suppress DMA engine
        - Suppress copy ops
        - Use IIO consmumer interface to enable/Disable DFSDM         
---
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 ++
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 362 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 376 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..041ddb9
--- /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 && STM32_DFSDM_ADC) || 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..05c4186
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,362 @@
+/*
+ * 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 <linux/iio/iio.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/adc/stm32-dfsdm-audio.h>
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define STM32_ADFSDM_DRV_NAME "stm32-adfsdm"
+
+#define DFSDM_MAX_PERIOD_SIZE	(PAGE_SIZE / 2)
+#define DFSDM_MAX_PERIODS	6
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai_drv;
+	struct snd_pcm_substream *substream;
+
+	/* IIO */
+	struct iio_channel *iio_ch;
+	struct iio_cb_buffer *iio_cb;
+	bool iio_active;
+
+	/* PCM buffer */
+	unsigned char *pcm_buff;
+	unsigned int pos;
+};
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE,
+	.formats = SNDRV_PCM_FMTBIT_S32_LE,
+
+	.rate_min = 8000,
+	.rate_max = 32000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = DFSDM_MAX_PERIODS,
+
+	.period_bytes_min = 1,
+	.period_bytes_max = DFSDM_MAX_PERIOD_SIZE,
+	.buffer_bytes_max = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE
+};
+
+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);
+
+	if (priv->iio_active) {
+		iio_channel_stop_all_cb(priv->iio_cb);
+		priv->iio_active = false;
+	}
+}
+
+int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	char str_rate[10];
+	ssize_t size;
+	int ret = 0;
+
+	dev_dbg(dai->dev, "%s: rate :%d\n", __func__,
+		substream->runtime->rate);
+
+	snprintf(str_rate, sizeof(str_rate), "%d\n", substream->runtime->rate);
+	size = iio_write_channel_ext_info(priv->iio_ch, "audio_sampling_rate",
+					  str_rate, sizeof(str_rate));
+	if (size != sizeof(str_rate)) {
+		dev_err(dai->dev, "%s: Failed to set %s sampling rate\n",
+			__func__, str_rate);
+		return -EINVAL;
+	}
+
+	if (!priv->iio_active) {
+		ret = iio_channel_start_all_cb(priv->iio_cb);
+		if (!ret)
+			priv->iio_active = true;
+		else
+		dev_err(dai->dev, "%s: Failed to start IIO channel (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+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);
+	ssize_t size;
+
+	dev_dbg(dai->dev, "%s: Enter for freq %d\n", __func__, freq);
+
+	/* Set IIO frequency if CODEC is master as clock comes from SPI_IN*/
+	if (dir == SND_SOC_CLOCK_IN) {
+		char str_freq[10];
+
+		snprintf(str_freq, sizeof(str_freq), "%d\n", freq);
+		size = iio_write_channel_ext_info(priv->iio_ch, "spi_clk_freq",
+						  str_freq, sizeof(str_freq));
+		if (size != sizeof(str_freq)) {
+			dev_err(dai->dev, "%s: Failed to set SPI clock\n",
+				__func__);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.shutdown = stm32_adfsdm_shutdown,
+	.prepare = stm32_adfsdm_dai_prepare,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+};
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S32_LE,
+		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			      SNDRV_PCM_RATE_32000),
+		    },
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "stm32_dfsdm_audio",
+};
+
+static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private)
+{
+	struct stm32_adfsdm_priv *priv = private;
+	struct snd_soc_pcm_runtime *rtd = priv->substream->private_data;
+	u8 *pcm_buff = priv->pcm_buff;
+	u8 *src_buff = (u8 *)data;
+	unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream);
+	unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream);
+	unsigned int old_pos = priv->pos;
+	unsigned int cur_size = size;
+
+	dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %d\n",
+		__func__, &pcm_buff[priv->pos], priv->pos, size);
+
+	if ((priv->pos + size) > buff_size) {
+		memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos);
+		cur_size -= buff_size - priv->pos;
+		priv->pos = 0;
+	}
+
+	memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size);
+	priv->pos = (priv->pos + cur_size) % buff_size;
+
+	if ((cur_size != size) || (old_pos && (old_pos % period_size < size)))
+		snd_pcm_period_elapsed(priv->substream);
+
+	return 0;
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		priv->pos = 0;
+		return stm32_dfsdm_get_buff_cb(priv->iio_ch->indio_dev,
+					stm32_afsdm_pcm_cb, priv);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		return stm32_dfsdm_release_buff_cb(priv->iio_ch->indio_dev);
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int ret;
+
+	ret =  snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw);
+	if (!ret)
+		priv->substream = substream;
+
+	return ret;
+}
+
+static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	snd_pcm_lib_free_pages(substream);
+	priv->substream = NULL;
+
+	return 0;
+}
+
+static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
+					    struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	return bytes_to_frames(substream->runtime, priv->pos);
+}
+
+static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	unsigned int period_bytes;
+	int ret;
+
+	period_bytes = params_buffer_bytes(params) / params_periods(params);
+	ret =  snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+	priv->pcm_buff = substream->runtime->dma_area;
+
+	return iio_channel_cb_set_buffer_params(priv->iio_cb,
+						params_buffer_size(params),
+						params_period_size(params));
+}
+
+static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+
+	return 0;
+}
+
+static struct snd_pcm_ops stm32_adfsdm_pcm_ops = {
+	.open		= stm32_adfsdm_pcm_open,
+	.close		= stm32_adfsdm_pcm_close,
+	.hw_params	= stm32_adfsdm_pcm_hw_params,
+	.hw_free	= stm32_adfsdm_pcm_hw_free,
+	.trigger	= stm32_adfsdm_trigger,
+	.pointer	= stm32_adfsdm_pcm_pointer,
+};
+
+static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE;
+
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						     card->dev, size, size);
+}
+
+static struct snd_soc_platform_driver stm32_adfsdm_soc_platform = {
+	.ops		= &stm32_adfsdm_pcm_ops,
+	.pcm_new	= stm32_adfsdm_pcm_new,
+};
+
+static const struct of_device_id stm32_adfsdm_of_match[] = {
+	{.compatible = "st,stm32h7-adfsdm"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dai_drv = stm32_adfsdm_dai;
+	priv->dai_drv.name = pdev->dev.of_node->name;
+	priv->dai_drv.capture.stream_name = pdev->dev.of_node->name;
+
+	dev_set_drvdata(&pdev->dev, priv);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_adfsdm_dai_component,
+					      &priv->dai_drv, 1);
+	if (ret < 0)
+		return ret;
+
+	/* Associate iio channel */
+	priv->iio_ch  = devm_iio_channel_get_all(&pdev->dev);
+	if (IS_ERR(priv->iio_ch))
+		return -ENODEV;
+
+	priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, NULL, NULL);
+	if (IS_ERR(priv->iio_cb))
+		return -ENODEV;
+
+	ret = devm_snd_soc_register_platform(&pdev->dev,
+					     &stm32_adfsdm_soc_platform);
+	if (ret < 0)
+		dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
+			__func__);
+
+	return 0;
+}
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.probe = stm32_adfsdm_probe,
+	.driver = {
+		   .name = STM32_ADFSDM_DRV_NAME,
+		   .of_match_table = stm32_adfsdm_of_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");
+MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);
-- 
1.9.1


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

* [PATCH v3 11/11] ASoC: stm32: add DFSDM DAI support
@ 2017-03-17 14:08   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

Add driver to handle DAI interface for PDM microphones connected
to Digital Filter for Sigma Delta mModulators IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
V2->V3:
       	- Suppress DMA engine
        - Suppress copy ops
        - Use IIO consmumer interface to enable/Disable DFSDM         
---
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 ++
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 362 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 376 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..041ddb9
--- /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 && STM32_DFSDM_ADC) || 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..05c4186
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,362 @@
+/*
+ * 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 <linux/iio/iio.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/adc/stm32-dfsdm-audio.h>
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#define STM32_ADFSDM_DRV_NAME "stm32-adfsdm"
+
+#define DFSDM_MAX_PERIOD_SIZE	(PAGE_SIZE / 2)
+#define DFSDM_MAX_PERIODS	6
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai_drv;
+	struct snd_pcm_substream *substream;
+
+	/* IIO */
+	struct iio_channel *iio_ch;
+	struct iio_cb_buffer *iio_cb;
+	bool iio_active;
+
+	/* PCM buffer */
+	unsigned char *pcm_buff;
+	unsigned int pos;
+};
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE,
+	.formats = SNDRV_PCM_FMTBIT_S32_LE,
+
+	.rate_min = 8000,
+	.rate_max = 32000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = DFSDM_MAX_PERIODS,
+
+	.period_bytes_min = 1,
+	.period_bytes_max = DFSDM_MAX_PERIOD_SIZE,
+	.buffer_bytes_max = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE
+};
+
+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);
+
+	if (priv->iio_active) {
+		iio_channel_stop_all_cb(priv->iio_cb);
+		priv->iio_active = false;
+	}
+}
+
+int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	char str_rate[10];
+	ssize_t size;
+	int ret = 0;
+
+	dev_dbg(dai->dev, "%s: rate :%d\n", __func__,
+		substream->runtime->rate);
+
+	snprintf(str_rate, sizeof(str_rate), "%d\n", substream->runtime->rate);
+	size = iio_write_channel_ext_info(priv->iio_ch, "audio_sampling_rate",
+					  str_rate, sizeof(str_rate));
+	if (size != sizeof(str_rate)) {
+		dev_err(dai->dev, "%s: Failed to set %s sampling rate\n",
+			__func__, str_rate);
+		return -EINVAL;
+	}
+
+	if (!priv->iio_active) {
+		ret = iio_channel_start_all_cb(priv->iio_cb);
+		if (!ret)
+			priv->iio_active = true;
+		else
+		dev_err(dai->dev, "%s: Failed to start IIO channel (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+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);
+	ssize_t size;
+
+	dev_dbg(dai->dev, "%s: Enter for freq %d\n", __func__, freq);
+
+	/* Set IIO frequency if CODEC is master as clock comes from SPI_IN*/
+	if (dir == SND_SOC_CLOCK_IN) {
+		char str_freq[10];
+
+		snprintf(str_freq, sizeof(str_freq), "%d\n", freq);
+		size = iio_write_channel_ext_info(priv->iio_ch, "spi_clk_freq",
+						  str_freq, sizeof(str_freq));
+		if (size != sizeof(str_freq)) {
+			dev_err(dai->dev, "%s: Failed to set SPI clock\n",
+				__func__);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.shutdown = stm32_adfsdm_shutdown,
+	.prepare = stm32_adfsdm_dai_prepare,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+};
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S32_LE,
+		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			      SNDRV_PCM_RATE_32000),
+		    },
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "stm32_dfsdm_audio",
+};
+
+static int stm32_afsdm_pcm_cb(const void *data, size_t size, void *private)
+{
+	struct stm32_adfsdm_priv *priv = private;
+	struct snd_soc_pcm_runtime *rtd = priv->substream->private_data;
+	u8 *pcm_buff = priv->pcm_buff;
+	u8 *src_buff = (u8 *)data;
+	unsigned int buff_size = snd_pcm_lib_buffer_bytes(priv->substream);
+	unsigned int period_size = snd_pcm_lib_period_bytes(priv->substream);
+	unsigned int old_pos = priv->pos;
+	unsigned int cur_size = size;
+
+	dev_dbg(rtd->dev, "%s: buff_add :%p, pos = %d, size = %d\n",
+		__func__, &pcm_buff[priv->pos], priv->pos, size);
+
+	if ((priv->pos + size) > buff_size) {
+		memcpy(&pcm_buff[priv->pos], src_buff, buff_size - priv->pos);
+		cur_size -= buff_size - priv->pos;
+		priv->pos = 0;
+	}
+
+	memcpy(&pcm_buff[priv->pos], &src_buff[size - cur_size], cur_size);
+	priv->pos = (priv->pos + cur_size) % buff_size;
+
+	if ((cur_size != size) || (old_pos && (old_pos % period_size < size)))
+		snd_pcm_period_elapsed(priv->substream);
+
+	return 0;
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		priv->pos = 0;
+		return stm32_dfsdm_get_buff_cb(priv->iio_ch->indio_dev,
+					stm32_afsdm_pcm_cb, priv);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		return stm32_dfsdm_release_buff_cb(priv->iio_ch->indio_dev);
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int ret;
+
+	ret =  snd_soc_set_runtime_hwparams(substream, &stm32_adfsdm_pcm_hw);
+	if (!ret)
+		priv->substream = substream;
+
+	return ret;
+}
+
+static int stm32_adfsdm_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	snd_pcm_lib_free_pages(substream);
+	priv->substream = NULL;
+
+	return 0;
+}
+
+static snd_pcm_uframes_t stm32_adfsdm_pcm_pointer(
+					    struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+	return bytes_to_frames(substream->runtime, priv->pos);
+}
+
+static int stm32_adfsdm_pcm_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv =
+		snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	unsigned int period_bytes;
+	int ret;
+
+	period_bytes = params_buffer_bytes(params) / params_periods(params);
+	ret =  snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+	priv->pcm_buff = substream->runtime->dma_area;
+
+	return iio_channel_cb_set_buffer_params(priv->iio_cb,
+						params_buffer_size(params),
+						params_period_size(params));
+}
+
+static int stm32_adfsdm_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+
+	return 0;
+}
+
+static struct snd_pcm_ops stm32_adfsdm_pcm_ops = {
+	.open		= stm32_adfsdm_pcm_open,
+	.close		= stm32_adfsdm_pcm_close,
+	.hw_params	= stm32_adfsdm_pcm_hw_params,
+	.hw_free	= stm32_adfsdm_pcm_hw_free,
+	.trigger	= stm32_adfsdm_trigger,
+	.pointer	= stm32_adfsdm_pcm_pointer,
+};
+
+static int stm32_adfsdm_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	unsigned int size = DFSDM_MAX_PERIODS * DFSDM_MAX_PERIOD_SIZE;
+
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						     card->dev, size, size);
+}
+
+static struct snd_soc_platform_driver stm32_adfsdm_soc_platform = {
+	.ops		= &stm32_adfsdm_pcm_ops,
+	.pcm_new	= stm32_adfsdm_pcm_new,
+};
+
+static const struct of_device_id stm32_adfsdm_of_match[] = {
+	{.compatible = "st,stm32h7-adfsdm"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dai_drv = stm32_adfsdm_dai;
+	priv->dai_drv.name = pdev->dev.of_node->name;
+	priv->dai_drv.capture.stream_name = pdev->dev.of_node->name;
+
+	dev_set_drvdata(&pdev->dev, priv);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_adfsdm_dai_component,
+					      &priv->dai_drv, 1);
+	if (ret < 0)
+		return ret;
+
+	/* Associate iio channel */
+	priv->iio_ch  = devm_iio_channel_get_all(&pdev->dev);
+	if (IS_ERR(priv->iio_ch))
+		return -ENODEV;
+
+	priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, NULL, NULL);
+	if (IS_ERR(priv->iio_cb))
+		return -ENODEV;
+
+	ret = devm_snd_soc_register_platform(&pdev->dev,
+					     &stm32_adfsdm_soc_platform);
+	if (ret < 0)
+		dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
+			__func__);
+
+	return 0;
+}
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.probe = stm32_adfsdm_probe,
+	.driver = {
+		   .name = STM32_ADFSDM_DRV_NAME,
+		   .of_match_table = stm32_adfsdm_of_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");
+MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);
-- 
1.9.1

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

* Re: [PATCH v3 00/11] Add STM32 DFSDM support
  2017-03-17 14:08 ` Arnaud Pouliquen
  (?)
@ 2017-03-17 16:36   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 16:36 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, Olivier MOYSAN, kernel, linux-iio,
	Maxime Coquelin, linux-arm-kernel, Alexandre TORGUE

Oops stupid guy i am...I have forgotten to tag the series as [RFC]
instead of [PATCH] in subjects.
Apologize for any inconvenience caused.

Regards
Arnaud

On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
> Hello,
> 
> I kept it as RFC as i reworked the global design (but also because not full validated...).
> Regarding Mark and Jonathan's comments, I decided to  try an implementation 
> with DMA support in IIO instead of handling it in ASOC.
> Solution seems more straightforward but as DMA cyclic mode and associated buffer
> block API is not part of IIO today, I still need some hacks...
> To be honest, I tried to integrate DMA cyclic management and associated buffer block 
> Consumer API. but no enough expertise on IIO to success... :(
> 
> Advantage vs V2:
> 	- No more data processing need in ASoC , done in IIO
>         - ASoC uses IIO consumer interface ( except for buffer callback)
> Drawback :
> 	- Need to create a "SPI bus" trigger to allo to use Buffer consumer API
>         	=> Need to find a solution to set this trigger by default in IIO audio driver
>         - No standard buffer management
>         	- Implement DMA cyclic in DFSDM IIO driver.
>                 - Define specific API to handle  buffer callback per period instead of 
>                   callback per samples generated by iio_push_to_buffers_with_timestamp.
>  
> V2-V3 delta:
> 	- New patches to support ASoC DMA codec in DT 
>   		- ASoC: Add Bindins for DMIC codec driver.
>   		- ASoC: codec: add DT support in dmic codec.
>         - New patches to allow in-kernel set of IIO buffer size and watermark   
>   		- IIO: consumer: allow to set buffer sizes.
> 	- IIO DFSDM drivers
>         	- Split audio and ADC support in 2 drivers.
>                 - Implement DMA cyclic mode.
>                 - Add SPI bus Trigger for buffer management.
>                 
> 	- IIO sigma delta adc drivers
>         	-  Suppress "simple and rename driver.
>          
>          - ASoC driver
>          	- Suppress DMA engine.
>                 - Suppress copy ops.
>                 - Use IIO consmumer interface to enable/Disable DFSDM.        
> 
> Regards,
> 
> Arnaud
> 
> Arnaud Pouliquen (11):
>   iio: Add hardware consumer support
>   IIO: Add DT bindings for sigma delta adc modulator
>   IIO: ADC: add sigma delta modulator support
>   IIO: add DT bindings for stm32 DFSDM filter
>   IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
>   IIO: ADC: add stm32 DFSDM support for PDM microphone
>   IIO: consumer: allow to set buffer sizes
>   ASoC: Add Bindins for DMIC codec driver
>   ASoC: codec: add DT support in dmic codec
>   ASoC: add bindings for stm32 DFSDM filter
>   ASoC: stm32: add DFSDM DAI support
> 
>  .../bindings/iio/adc/sigma-delta-modulator.txt     |  13 +
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 ++++
>  Documentation/devicetree/bindings/sound/dmic.txt   |  11 +
>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  |  41 ++
>  drivers/iio/Kconfig                                |   6 +
>  drivers/iio/Makefile                               |   1 +
>  drivers/iio/adc/Kconfig                            |  50 ++
>  drivers/iio/adc/Makefile                           |   4 +
>  drivers/iio/adc/sd_adc_modulator.c                 |  98 +++
>  drivers/iio/adc/stm32-dfsdm-adc.c                  | 419 ++++++++++++
>  drivers/iio/adc/stm32-dfsdm-audio.c                | 720 +++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c                 | 658 +++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h                      | 372 +++++++++++
>  drivers/iio/buffer/industrialio-buffer-cb.c        |  12 +
>  drivers/iio/hw_consumer.c                          | 156 +++++
>  include/linux/iio/adc/stm32-dfsdm-audio.h          |  41 ++
>  include/linux/iio/consumer.h                       |  13 +
>  include/linux/iio/hw_consumer.h                    |  12 +
>  sound/soc/Kconfig                                  |   1 +
>  sound/soc/Makefile                                 |   1 +
>  sound/soc/codecs/dmic.c                            |   8 +
>  sound/soc/stm/Kconfig                              |  10 +
>  sound/soc/stm/Makefile                             |   2 +
>  sound/soc/stm/stm32_adfsdm.c                       | 362 +++++++++++
>  24 files changed, 3131 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>  create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>  create mode 100644 drivers/iio/adc/sd_adc_modulator.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>  create mode 100644 drivers/iio/hw_consumer.c
>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>  create mode 100644 include/linux/iio/hw_consumer.h
>  create mode 100644 sound/soc/stm/Kconfig
>  create mode 100644 sound/soc/stm/Makefile
>  create mode 100644 sound/soc/stm/stm32_adfsdm.c
> 
> V2:
> 
> Patch-set associated to this RFC proposes an implementation of the
> DFSDM features shared between ASoC and IIO frameworks.
> 
> Patch-set is only a Skeleton of the drivers, so a base to discuss and validate a design. 
> It contains minimum code to allow probing (with DT) and to expose the ASoC and IIO ABI.
> Hope that is sufficent in a first step to allow to focus on APIs.
> 
> In this patch-set there are two new APIs used:
> 	- IIO in-kern API: based on hw_customer API proposed by Lars
>  	- ASOC <-> IIO API inspired by API defined for hdmi-codec for ASoC/DRM interconnect. 
>    	  API is dedicated to DFSDM only.
> 
> Notice also that this design is based on following assumption:
> 	- Audio stream ABI interface is ASoC, no access to data through IIO ABI for PDM. 
> 	- ASoC DMA should be used for audio transfer as designed for real time stream.
> 	- Need some runtime parameters exchange between ASoC and IIO
> 	  due to the correlation between the sample frequency, the DFSDM decimation 
>           factor and the associated scaling.
> 
> - "ASoC: dmaengine_pcm: add copy support" patch:
>  I added a patch in ASoC that allows to implement a copy function to process data 
>  after DMA transfer. Requested, as DFSDM samples captured contain channel ID 
>  on 8-LSB bits and need also a potential rescale to present DAT on 24-bits.  	
> 
> - "IIO: ADC: add sigma delta modulator support" patch:
> Simple dummy driver created to support external Sigma delta modulator. 
> It is binded to DFSDM driver through hw_customer API.
> 
> 
> - 
> 1.9.1
> 

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

* Re: [PATCH v3 00/11] Add STM32 DFSDM support
@ 2017-03-17 16:36   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 16:36 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

Oops stupid guy i am...I have forgotten to tag the series as [RFC]
instead of [PATCH] in subjects.
Apologize for any inconvenience caused.

Regards
Arnaud

On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
> Hello,
> 
> I kept it as RFC as i reworked the global design (but also because not full validated...).
> Regarding Mark and Jonathan's comments, I decided to  try an implementation 
> with DMA support in IIO instead of handling it in ASOC.
> Solution seems more straightforward but as DMA cyclic mode and associated buffer
> block API is not part of IIO today, I still need some hacks...
> To be honest, I tried to integrate DMA cyclic management and associated buffer block 
> Consumer API. but no enough expertise on IIO to success... :(
> 
> Advantage vs V2:
> 	- No more data processing need in ASoC , done in IIO
>         - ASoC uses IIO consumer interface ( except for buffer callback)
> Drawback :
> 	- Need to create a "SPI bus" trigger to allo to use Buffer consumer API
>         	=> Need to find a solution to set this trigger by default in IIO audio driver
>         - No standard buffer management
>         	- Implement DMA cyclic in DFSDM IIO driver.
>                 - Define specific API to handle  buffer callback per period instead of 
>                   callback per samples generated by iio_push_to_buffers_with_timestamp.
>  
> V2-V3 delta:
> 	- New patches to support ASoC DMA codec in DT 
>   		- ASoC: Add Bindins for DMIC codec driver.
>   		- ASoC: codec: add DT support in dmic codec.
>         - New patches to allow in-kernel set of IIO buffer size and watermark   
>   		- IIO: consumer: allow to set buffer sizes.
> 	- IIO DFSDM drivers
>         	- Split audio and ADC support in 2 drivers.
>                 - Implement DMA cyclic mode.
>                 - Add SPI bus Trigger for buffer management.
>                 
> 	- IIO sigma delta adc drivers
>         	-  Suppress "simple and rename driver.
>          
>          - ASoC driver
>          	- Suppress DMA engine.
>                 - Suppress copy ops.
>                 - Use IIO consmumer interface to enable/Disable DFSDM.        
> 
> Regards,
> 
> Arnaud
> 
> Arnaud Pouliquen (11):
>   iio: Add hardware consumer support
>   IIO: Add DT bindings for sigma delta adc modulator
>   IIO: ADC: add sigma delta modulator support
>   IIO: add DT bindings for stm32 DFSDM filter
>   IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
>   IIO: ADC: add stm32 DFSDM support for PDM microphone
>   IIO: consumer: allow to set buffer sizes
>   ASoC: Add Bindins for DMIC codec driver
>   ASoC: codec: add DT support in dmic codec
>   ASoC: add bindings for stm32 DFSDM filter
>   ASoC: stm32: add DFSDM DAI support
> 
>  .../bindings/iio/adc/sigma-delta-modulator.txt     |  13 +
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 ++++
>  Documentation/devicetree/bindings/sound/dmic.txt   |  11 +
>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  |  41 ++
>  drivers/iio/Kconfig                                |   6 +
>  drivers/iio/Makefile                               |   1 +
>  drivers/iio/adc/Kconfig                            |  50 ++
>  drivers/iio/adc/Makefile                           |   4 +
>  drivers/iio/adc/sd_adc_modulator.c                 |  98 +++
>  drivers/iio/adc/stm32-dfsdm-adc.c                  | 419 ++++++++++++
>  drivers/iio/adc/stm32-dfsdm-audio.c                | 720 +++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c                 | 658 +++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h                      | 372 +++++++++++
>  drivers/iio/buffer/industrialio-buffer-cb.c        |  12 +
>  drivers/iio/hw_consumer.c                          | 156 +++++
>  include/linux/iio/adc/stm32-dfsdm-audio.h          |  41 ++
>  include/linux/iio/consumer.h                       |  13 +
>  include/linux/iio/hw_consumer.h                    |  12 +
>  sound/soc/Kconfig                                  |   1 +
>  sound/soc/Makefile                                 |   1 +
>  sound/soc/codecs/dmic.c                            |   8 +
>  sound/soc/stm/Kconfig                              |  10 +
>  sound/soc/stm/Makefile                             |   2 +
>  sound/soc/stm/stm32_adfsdm.c                       | 362 +++++++++++
>  24 files changed, 3131 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>  create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>  create mode 100644 drivers/iio/adc/sd_adc_modulator.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>  create mode 100644 drivers/iio/hw_consumer.c
>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>  create mode 100644 include/linux/iio/hw_consumer.h
>  create mode 100644 sound/soc/stm/Kconfig
>  create mode 100644 sound/soc/stm/Makefile
>  create mode 100644 sound/soc/stm/stm32_adfsdm.c
> 
> V2:
> 
> Patch-set associated to this RFC proposes an implementation of the
> DFSDM features shared between ASoC and IIO frameworks.
> 
> Patch-set is only a Skeleton of the drivers, so a base to discuss and validate a design. 
> It contains minimum code to allow probing (with DT) and to expose the ASoC and IIO ABI.
> Hope that is sufficent in a first step to allow to focus on APIs.
> 
> In this patch-set there are two new APIs used:
> 	- IIO in-kern API: based on hw_customer API proposed by Lars
>  	- ASOC <-> IIO API inspired by API defined for hdmi-codec for ASoC/DRM interconnect. 
>    	  API is dedicated to DFSDM only.
> 
> Notice also that this design is based on following assumption:
> 	- Audio stream ABI interface is ASoC, no access to data through IIO ABI for PDM. 
> 	- ASoC DMA should be used for audio transfer as designed for real time stream.
> 	- Need some runtime parameters exchange between ASoC and IIO
> 	  due to the correlation between the sample frequency, the DFSDM decimation 
>           factor and the associated scaling.
> 
> - "ASoC: dmaengine_pcm: add copy support" patch:
>  I added a patch in ASoC that allows to implement a copy function to process data 
>  after DMA transfer. Requested, as DFSDM samples captured contain channel ID 
>  on 8-LSB bits and need also a potential rescale to present DAT on 24-bits.  	
> 
> - "IIO: ADC: add sigma delta modulator support" patch:
> Simple dummy driver created to support external Sigma delta modulator. 
> It is binded to DFSDM driver through hw_customer API.
> 
> 
> - 
> 1.9.1
> 

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

* [PATCH v3 00/11] Add STM32 DFSDM support
@ 2017-03-17 16:36   ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-17 16:36 UTC (permalink / raw)
  To: linux-arm-kernel

Oops stupid guy i am...I have forgotten to tag the series as [RFC]
instead of [PATCH] in subjects.
Apologize for any inconvenience caused.

Regards
Arnaud

On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
> Hello,
> 
> I kept it as RFC as i reworked the global design (but also because not full validated...).
> Regarding Mark and Jonathan's comments, I decided to  try an implementation 
> with DMA support in IIO instead of handling it in ASOC.
> Solution seems more straightforward but as DMA cyclic mode and associated buffer
> block API is not part of IIO today, I still need some hacks...
> To be honest, I tried to integrate DMA cyclic management and associated buffer block 
> Consumer API. but no enough expertise on IIO to success... :(
> 
> Advantage vs V2:
> 	- No more data processing need in ASoC , done in IIO
>         - ASoC uses IIO consumer interface ( except for buffer callback)
> Drawback :
> 	- Need to create a "SPI bus" trigger to allo to use Buffer consumer API
>         	=> Need to find a solution to set this trigger by default in IIO audio driver
>         - No standard buffer management
>         	- Implement DMA cyclic in DFSDM IIO driver.
>                 - Define specific API to handle  buffer callback per period instead of 
>                   callback per samples generated by iio_push_to_buffers_with_timestamp.
>  
> V2-V3 delta:
> 	- New patches to support ASoC DMA codec in DT 
>   		- ASoC: Add Bindins for DMIC codec driver.
>   		- ASoC: codec: add DT support in dmic codec.
>         - New patches to allow in-kernel set of IIO buffer size and watermark   
>   		- IIO: consumer: allow to set buffer sizes.
> 	- IIO DFSDM drivers
>         	- Split audio and ADC support in 2 drivers.
>                 - Implement DMA cyclic mode.
>                 - Add SPI bus Trigger for buffer management.
>                 
> 	- IIO sigma delta adc drivers
>         	-  Suppress "simple and rename driver.
>          
>          - ASoC driver
>          	- Suppress DMA engine.
>                 - Suppress copy ops.
>                 - Use IIO consmumer interface to enable/Disable DFSDM.        
> 
> Regards,
> 
> Arnaud
> 
> Arnaud Pouliquen (11):
>   iio: Add hardware consumer support
>   IIO: Add DT bindings for sigma delta adc modulator
>   IIO: ADC: add sigma delta modulator support
>   IIO: add DT bindings for stm32 DFSDM filter
>   IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
>   IIO: ADC: add stm32 DFSDM support for PDM microphone
>   IIO: consumer: allow to set buffer sizes
>   ASoC: Add Bindins for DMIC codec driver
>   ASoC: codec: add DT support in dmic codec
>   ASoC: add bindings for stm32 DFSDM filter
>   ASoC: stm32: add DFSDM DAI support
> 
>  .../bindings/iio/adc/sigma-delta-modulator.txt     |  13 +
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 ++++
>  Documentation/devicetree/bindings/sound/dmic.txt   |  11 +
>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  |  41 ++
>  drivers/iio/Kconfig                                |   6 +
>  drivers/iio/Makefile                               |   1 +
>  drivers/iio/adc/Kconfig                            |  50 ++
>  drivers/iio/adc/Makefile                           |   4 +
>  drivers/iio/adc/sd_adc_modulator.c                 |  98 +++
>  drivers/iio/adc/stm32-dfsdm-adc.c                  | 419 ++++++++++++
>  drivers/iio/adc/stm32-dfsdm-audio.c                | 720 +++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c                 | 658 +++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h                      | 372 +++++++++++
>  drivers/iio/buffer/industrialio-buffer-cb.c        |  12 +
>  drivers/iio/hw_consumer.c                          | 156 +++++
>  include/linux/iio/adc/stm32-dfsdm-audio.h          |  41 ++
>  include/linux/iio/consumer.h                       |  13 +
>  include/linux/iio/hw_consumer.h                    |  12 +
>  sound/soc/Kconfig                                  |   1 +
>  sound/soc/Makefile                                 |   1 +
>  sound/soc/codecs/dmic.c                            |   8 +
>  sound/soc/stm/Kconfig                              |  10 +
>  sound/soc/stm/Makefile                             |   2 +
>  sound/soc/stm/stm32_adfsdm.c                       | 362 +++++++++++
>  24 files changed, 3131 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>  create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>  create mode 100644 drivers/iio/adc/sd_adc_modulator.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>  create mode 100644 drivers/iio/hw_consumer.c
>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>  create mode 100644 include/linux/iio/hw_consumer.h
>  create mode 100644 sound/soc/stm/Kconfig
>  create mode 100644 sound/soc/stm/Makefile
>  create mode 100644 sound/soc/stm/stm32_adfsdm.c
> 
> V2:
> 
> Patch-set associated to this RFC proposes an implementation of the
> DFSDM features shared between ASoC and IIO frameworks.
> 
> Patch-set is only a Skeleton of the drivers, so a base to discuss and validate a design. 
> It contains minimum code to allow probing (with DT) and to expose the ASoC and IIO ABI.
> Hope that is sufficent in a first step to allow to focus on APIs.
> 
> In this patch-set there are two new APIs used:
> 	- IIO in-kern API: based on hw_customer API proposed by Lars
>  	- ASOC <-> IIO API inspired by API defined for hdmi-codec for ASoC/DRM interconnect. 
>    	  API is dedicated to DFSDM only.
> 
> Notice also that this design is based on following assumption:
> 	- Audio stream ABI interface is ASoC, no access to data through IIO ABI for PDM. 
> 	- ASoC DMA should be used for audio transfer as designed for real time stream.
> 	- Need some runtime parameters exchange between ASoC and IIO
> 	  due to the correlation between the sample frequency, the DFSDM decimation 
>           factor and the associated scaling.
> 
> - "ASoC: dmaengine_pcm: add copy support" patch:
>  I added a patch in ASoC that allows to implement a copy function to process data 
>  after DMA transfer. Requested, as DFSDM samples captured contain channel ID 
>  on 8-LSB bits and need also a potential rescale to present DAT on 24-bits.  	
> 
> - "IIO: ADC: add sigma delta modulator support" patch:
> Simple dummy driver created to support external Sigma delta modulator. 
> It is binded to DFSDM driver through hw_customer API.
> 
> 
> - 
> 1.9.1
> 

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

* Re: [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-19 22:25       ` Jonathan Cameron
  -1 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:25 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
> in n bit samples through a low pass filter and an integrator.
> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
Various minor bits inline.

I'm mostly liking this.  I do slightly wondering if semantically it
should be the front end that has the channels rather than the
backend.  Would be fiddly to do though and probably not worth the
hassle.

Would love to see it running in a continuous mode in IIO, but
I guess that can follow along later.

The comment about the trigger has me confused
- perhaps you could elaborate further on that?

Jonathan
> ---
> V2 -> V3 :
> 	- Split audio and ADC support in 2 drivers
> 	- Implement DMA cyclic mode
> 	- Add SPI bus Trigger for buffer management
> 
>  drivers/iio/adc/Kconfig            |  26 ++
>  drivers/iio/adc/Makefile           |   2 +
>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>  5 files changed, 1477 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index d411d66..3e0eb11 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -452,6 +452,32 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_CORE
> +	tristate "STMicroelectronics STM32 dfsdm core"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the  driver for STMicroelectronics
> +	  STM32 digital filter for sigma delta converter.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-dfsdm-core.
> +
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 dfsdm adc"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select STM32_DFSDM_CORE
> +	select REGMAP_MMIO
> +	select IIO_BUFFER_DMAENGINE
> +	select IIO_HW_CONSUMER
> +	help
> +	  Select this option to support ADCSigma delta modulator for
> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
> +
> +	  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 c68819c..161f271 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,419 @@
> +/*
> + * This file is the ADC part of of the STM32 DFSDM driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen-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/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +#define DFSDM_TIMEOUT_US 100000
> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +struct stm32_dfsdm_adc {
> +	struct stm32_dfsdm *dfsdm;
> +	unsigned int fl_id;
> +	unsigned int ch_id;
> +
> +	unsigned int oversamp;
> +
> +	struct completion completion;
> +
> +	u32 *buffer;
> +
> +	/* Hardware consumer structure for Front End IIO */
> +	struct iio_hw_consumer *hwc;
> +};
> +
> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
> +{
> +	int ret;
> +
> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
> +	if (ret < 0)
> +		goto stop_dfsdm;
> +
> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	return 0;
> +
> +stop_channels:
> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
> +stop_dfsdm:
> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +
> +	return ret;
> +}
> +
> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
> +{
> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +
> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
> +
> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan, int *res)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	int ret;
> +
> +	reinit_completion(&adc->completion);
> +
> +	adc->buffer = res;
> +
> +	/* Unmask IRQ for regular conversion achievement*/
> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_conv(adc);
> +	if (ret < 0)
> +		return ret;
> +
> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> +							    DFSDM_TIMEOUT);
blank line perhaps.
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +
> +	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", *res);
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +
> +	stm32_dfsdm_stop_conv(adc);
> +
> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
> +	int ret = -EINVAL;
> +
> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
> +		if (!ret)
> +			adc->oversamp = val;
> +	}
blank line here.
> +	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);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_hw_consumer_enable(adc->hwc);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"%s: IIO enable failed (channel %d)\n",
> +				__func__, chan->channel);
> +			return ret;
> +		}
> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"%s: Conversion failed (channel %d)\n",
> +				__func__, chan->channel);
> +			return ret;
> +		}
> +
> +		iio_hw_consumer_disable(adc->hwc);
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		*val = adc->oversamp;
> +
> +		return IIO_VAL_INT;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_dfsdm_info_adc = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct stm32_dfsdm_adc *adc = arg;
> +	struct regmap *regmap = adc->dfsdm->regmap;
> +	unsigned int status;
> +
> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
> +
> +	if (status & DFSDM_ISR_REOCF_MASK) {
> +		/* read the data register clean the IRQ status */
> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
> +		complete(&adc->completion);
> +	}
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
What's this one?  Might want a comment given it's an irq you basically eat.
> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
> +				   DFSDM_ICR_CLRROVRF_MASK,
> +				   DFSDM_ICR_CLRROVRF_MASK);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return stm32_dfsdm_start_conv(adc);
> +}
> +
> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	stm32_dfsdm_stop_conv(adc);
blank line.
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
> +	.postenable = &stm32_dfsdm_postenable,
> +	.predisable = &stm32_dfsdm_predisable,
> +};
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int ch_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[ch_idx];
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = ch_idx;
> +
> +	/*
> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
> +	 */
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
> +
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +	adc->ch_id = ch->channel;
> +
> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
> +					  &adc->dfsdm->ch_list[ch->channel]);
> +}
> +
> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct iio_chan_spec *channels;
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int num_ch;
> +	int ret, chan_idx;
> +
> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
> +					     "st,adc-channels");
> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
> +		return num_ch < 0 ? num_ch : -EINVAL;
> +	}
> +
> +	/*
> +	 * Number of channel per filter is temporary limited to 1.
> +	 * Restriction should be cleaned with scan mode
> +	 */
> +	if (num_ch > 1) {
> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Bind to SD modulator IIO device */
> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
> +	if (IS_ERR(adc->hwc))
> +		return -EPROBE_DEFER;
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		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 free_hwc;
> +	}
> +
> +	indio_dev->num_channels = num_ch;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +
> +free_hwc:
> +	iio_hw_consumer_free(adc->hwc);
Given you have to free this in the error path, I would imagine you will
need a free somewhere in the main remove path?  Or just create a devm
version of iio_hw_consumer_alloc.  It will be useful in the long run.
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc"},
> +	{}
> +};
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	struct device_node *np = dev->of_node;
> +	struct iio_dev *iio;
> +	char *name;
> +	int ret, irq, val;
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
> +		return PTR_ERR(iio);
> +	}
> +
> +	adc = iio_priv(iio);
> +	if (IS_ERR(adc)) {
> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
> +		return PTR_ERR(adc);
> +	}
> +	adc->dfsdm = dev_get_drvdata(dev->parent);
> +
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = &stm32_dfsdm_info_adc;
> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
> +	if (ret != 0) {
> +		dev_err(dev, "Missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
not freed.  Maybe devm_kzalloc
> +	if (!name)
> +		return -ENOMEM;
> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
> +	iio->name = name;
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
> +	 */
> +	irq = platform_get_irq(pdev, 0);
> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
> +			       0, pdev->name, adc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request IRQ\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set filter order\n");
> +		return ret;
> +	}
> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
> +	if (!ret)
> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
> +
> +	ret = stm32_dfsdm_adc_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	init_completion(&adc->completion);
> +
> +	return iio_device_register(iio);
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	iio_device_unregister(iio);
If all you have is this in remove, you can probably get away with
devm_iio_device_register and get rid of the remove entirely.
> +
> +	return 0;
> +}
> +
> +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-qxv4g6HH51o@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
> new file mode 100644
> index 0000000..488e456
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
> @@ -0,0 +1,658 @@
> +/*
> + * This file is part the core part STM32 DFSDM 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/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +struct stm32_dfsdm_dev_data {
> +	unsigned int num_filters;
> +	unsigned int num_channels;
> +	const struct regmap_config *regmap_cfg;
> +};
> +
> +#define STM32H7_DFSDM_NUM_FILTERS	4
> +#define STM32H7_DFSDM_NUM_CHANNELS	8
> +
> +#define DFSDM_MAX_INT_OVERSAMPLING 256
> +
> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
> +
> +#define DFSDM_MAX_RES BIT(31)
> +#define DFSDM_DATA_RES BIT(23)
> +
> +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 = 0x2B8,
> +	.volatile_reg = stm32_dfsdm_volatile_reg,
> +	.fast_io = true,
> +};
> +
> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
> +};
> +
> +struct dfsdm_priv {
> +	struct platform_device *pdev; /* platform device*/
> +
> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
> +
> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
> +	atomic_t n_active_ch;	/* number of current active channels */
> +
> +	/* Clock */
> +	struct clk *clk; /* DFSDM clock */
> +	struct clk *aclk; /* audio clock */
> +};
> +
> +/**
> + * stm32_dfsdm_set_osrs - compute filter parameters.
Naming would suggest it's more specific than this.
Setting over sampling ratios?
> + *
> + * Enable interface if n_active_ch is not null.
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fast: Fast mode enabled or disabled
> + * @oversamp: Expected oversampling between filtered sample and SD input stream
> + */
> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
> +			 unsigned int oversamp)
> +{
> +	unsigned int i, d, fosr, iosr;
> +	u64 res;
> +	s64 delta;
> +	unsigned int m = 1;	/* multiplication factor */
> +	unsigned int p = fl->ford;	/* filter order (ford) */
> +
> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
> +	/*
> +	 * This function tries to compute filter oversampling and integrator
> +	 * oversampling, base on oversampling ratio requested by user.
> +	 *
> +	 * Decimation d depends on the filter order and the oversampling ratios.
> +	 * ford: filter order
> +	 * fosr: filter over sampling ratio
> +	 * iosr: integrator over sampling ratio
> +	 */
> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
> +		m = 2;
> +		p = 2;
> +	}
> +
> +	/*
> +	 * Looks for filter and integrator oversampling ratios which allows
> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
> +				d = fosr * (iosr + 3) + 2;
> +			else
> +				d = fosr * (iosr - 1 + p) + p;
> +
> +			if (d > oversamp)
> +				break;
> +			else if (d != oversamp)
> +				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 > DFSDM_MAX_RES)
> +					break;
> +			}
> +			if (res > DFSDM_MAX_RES)
> +				continue;
> +			res = res * (u64)m * (u64)iosr;
> +			if (res > DFSDM_MAX_RES)
> +				continue;
> +
> +			delta = res - DFSDM_DATA_RES;
> +
> +			if (res >= fl->res) {
> +				fl->res = res;
> +				fl->fosr = fosr;
> +				fl->iosr = iosr;
> +				fl->fast = fast;
> +				pr_debug("%s: fosr = %d, iosr = %d\n",
> +					 __func__, fl->fosr, fl->iosr);
> +			}
> +
> +			if (!delta)
> +				return 0;
> +		}
> +	}
> +
> +	if (!fl->fosr)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/**
> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
> + *
> + * Enable interface if n_active_ch is not null.
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + */
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	struct device *dev = &priv->pdev->dev;
> +	unsigned int clk_div = priv->spi_clk_out_div;
> +	int ret;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		/* Enable clocks */
> +		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");
> +				goto disable_clk;
> +			}
> +		}
> +
> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
> +		if (ret < 0)
> +			goto disable_aclk;
> +
> +		/* Global enable of DFSDM interface */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(1));
> +		if (ret < 0)
> +			goto disable_aclk;
> +	}
> +
> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +
> +disable_aclk:
> +	clk_disable_unprepare(priv->aclk);
> +disable_clk:
> +	clk_disable_unprepare(priv->clk);
> +
> +	return ret;
> +}
> +
> +/**
> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
> + *
> + * Disable interface if n_active_ch is null
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + */
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	int ret;
> +
> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
> +		/* Global disable of DFSDM interface */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Stop SPI CLKOUT */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Disable clocks */
> +		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));
> +
> +	return 0;
> +}
> +
> +/**
> + * stm32_dfsdm_start_channel
> + *	Start DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: Channel index.
> + */
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
> +				  DFSDM_CHCFGR1_CHEN_MASK,
> +				  DFSDM_CHCFGR1_CHEN(1));
> +}
> +
> +/**
> + * stm32_dfsdm_stop_channel
> + *	Stop DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: Channel index.
> + */
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
> +			   DFSDM_CHCFGR1_CHEN_MASK,
> +			   DFSDM_CHCFGR1_CHEN(0));
> +}
> +
> +/**
> + * stm32_dfsdm_chan_configure
> + *	Configure DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: channel index.
> + */
> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
> +			       struct stm32_dfsdm_channel *ch)
> +{
> +	unsigned int id = ch->id;
> +	struct regmap *regmap = dfsdm->regmap;
> +	int ret;
> +
> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				 DFSDM_CHCFGR1_SITP_MASK,
> +				 DFSDM_CHCFGR1_SITP(ch->type));
> +	if (ret < 0)
> +		return ret;
Blank line here and in similar places makes it easier for my
eyes to parse at least...
I'd also like to see some docs in here, not all of these
are self explanatory.
> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
> +	if (ret < 0)
> +		return ret;
> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
> +}
> +
> +/**
> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter index.
> + */
> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	int ret;
> +
> +	/* Enable filter */
> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Start conversion */
> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_RSWSTART_MASK,
> +				  DFSDM_CR1_RSWSTART(1));
> +}
> +
> +/**
> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter index.
> + */
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +	/* Disable conversion */
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
> +}
> +
> +/**
> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
> + *	to channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: channel index.
> + * @fl_id: Filter index.
> + */
> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 unsigned int ch_id)
> +{
> +	struct regmap *regmap = dfsdm->regmap;
> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
> +	int ret;
> +
> +	/* Average integrator oversampling */
> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
> +				 DFSDM_FCR_IOSR(fl->iosr));
> +
> +	/* Filter order and Oversampling */
Please handle each error properly as it happens rather than mudling onwards.
If there is reason for this odd construction, then document it clearly.
> +	if (!ret)
> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
> +					 DFSDM_FCR_FOSR_MASK,
> +					 DFSDM_FCR_FOSR(fl->fosr));
> +
> +	if (!ret)
> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
> +					 DFSDM_FCR_FORD_MASK,
> +					 DFSDM_FCR_FORD(fl->ford));
> +
> +	/* If only one channel no scan mode supported for the moment */
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> +				 DFSDM_CR1_RCH_MASK,
> +				 DFSDM_CR1_RCH(ch_id));
> +
> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> +					 DFSDM_CR1_RSYNC_MASK,
> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
> +}
> +
> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
> +					 struct stm32_dfsdm *dfsdm)
> +{
> +	/*
> +	 * To be able to use buffer consumer interface a trigger is needed.
> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
> +	 * sense to define the serial interface ( SPI or manchester) as
> +	 * trigger source.
It's not actually the case that you have to have a triggrer.
There are plenty of drivers (particularly ones with hardware buffering)
where there is no trigger envolved.  That's not to say it doesn't make sense
here.

I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
somewhat...
> +	 */
> +
> +	struct iio_trigger *trig;
> +	int ret;
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
> +	if (!trig)
> +		return -ENOMEM;
> +
> +	trig->dev.parent = pdev->dev.parent;
> +	trig->ops = &dfsdm_trigger_ops;
> +
> +	iio_trigger_set_drvdata(trig, dfsdm);
> +
> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
> +	if (ret)
> +		return ret;
Just return ret in all cases.
> +
> +	return 0;
> +}
> +
> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
> +				 struct iio_dev *indio_dev,
> +				 struct iio_chan_spec *chan, int chan_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	struct stm32_dfsdm_channel *df_ch;
> +	const char *of_str;
> +	int ret, val;
> +
> +	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;
> +	}
> +
> +	df_ch =  &dfsdm->ch_list[ch->channel];
Extra space on the line above.
> +	df_ch->id = ch->channel;
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-types", chan_idx,
> +					    &of_str);
> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
> +	if (ret < 0 || val < 0)
> +		df_ch->type = 0;
> +	else
> +		df_ch->type = val;
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-clk-src", chan_idx,
> +					    &of_str);
> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
> +	if (ret < 0 || val < 0)
> +		df_ch->src = 0;
> +	else
> +		df_ch->src = val;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-alt-channel", chan_idx,
> +					 &df_ch->alt_si);
> +	if (ret < 0)
> +		df_ch->alt_si = 0;
> +
> +	return 0;
> +}
> +
> +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;
> +	unsigned long clk_freq;
> +	unsigned int spi_freq, rem;
> +	int ret;
> +
> +	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->dfsdm.phys_base = res->start;
> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
> +
> +	/* Source clock */
> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
> +	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");
> +	if (IS_ERR(priv->aclk))
> +		priv->aclk = NULL;
> +
> +	if (priv->aclk)
> +		clk_freq = clk_get_rate(priv->aclk);
> +	else
> +		clk_freq = clk_get_rate(priv->clk);
> +
> +	/* SPI clock freq */
> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
> +				   &spi_freq);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
> +		return ret;
> +	}
> +
> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
> +	priv->dfsdm.spi_master_freq = spi_freq;
> +
> +	if (rem) {
> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
> +	}
> +
> +	return 0;
> +};
> +
> +static const struct of_device_id stm32_dfsdm_of_match[] = {
> +	{
> +		.compatible = "st,stm32h7-dfsdm",
> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
> +	struct stm32_dfsdm *dfsdm;
> +	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_dfsdm_dev_data *)of_id->data;
> +	dfsdm = &priv->dfsdm;
> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
> +	if (!dfsdm->fl_list)
> +		return -ENOMEM;
> +
> +	dfsdm->num_fls = dev_data->num_filters;
> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
> +				      sizeof(*dfsdm->ch_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->ch_list)
> +		return -ENOMEM;
> +	dfsdm->num_chs = dev_data->num_channels;
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
> +					    &stm32h7_dfsdm_regmap_cfg);
> +	if (IS_ERR(dfsdm->regmap)) {
> +		ret = PTR_ERR(dfsdm->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
> +
> +		fl->id = i;
I'd like a comment on why this is needed...
> +	}
> +
> +	platform_set_drvdata(pdev, dfsdm);
> +
> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
> +}
> +
> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
> new file mode 100644
> index 0000000..bb7d74f
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm.h
> @@ -0,0 +1,371 @@
> +/*
> + * This file is part of STM32 DFSDM 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__H
> +#define MDF_STM32_DFSDM__H
> +
> +#include <linux/bitfield.h>
> +
> +#include <linux/iio/iio.h>
> +/*
> + * STM32 DFSDM - global register map
> + * ________________________________________________________
> + * | Offset |                 Registers block             |
> + * --------------------------------------------------------
> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
> + * --------------------------------------------------------
> + * | 0x020  |                CHANNEL 1                    |
> + * --------------------------------------------------------
> + * | ...    |                .....                        |
> + * --------------------------------------------------------
> + * | 0x0E0  |                CHANNEL 7                    |
> + * --------------------------------------------------------
> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
> + * --------------------------------------------------------
> + * | 0x200  |                FILTER  1                    |
> + * --------------------------------------------------------
> + * | 0x300  |                FILTER  2                    |
> + * --------------------------------------------------------
> + * | 0x400  |                FILTER  3                    |
> + * --------------------------------------------------------
> + */
> +
> +/*
> + * Channels register 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 register 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)
> +
> +/* 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,
> +};
> +
> +/**
> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
> + * TODO: complete structure.
nice :) RFC I guess :)
> + * @id:		filetr ID,
> + */
> +struct stm32_dfsdm_filter {
> +	unsigned int id;
> +	unsigned int iosr; /* integrator oversampling */
> +	unsigned int fosr; /* filter oversampling */
> +	enum stm32_dfsdm_sinc_order ford;
> +	u64 res; /* output sample resolution */
> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
> +	unsigned int fast; /* filter fast mode */
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
> + * TODO: complete structure.
> + * @id:		filetr ID,
filter
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;   /* id of the channel */
> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
> +	unsigned int alt_si; /* use alternative serial input interface */
> +};
> +
> +/**
> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
> + * @base:	control registers base cpu addr
> + * @phys_base:	DFSDM IP register physical address.
> + * @fl_list:	filter resources list
> + * @num_fl:	number of filter resources available
> + * @ch_list:	channel resources list
> + * @num_chs:	number of channel resources available
> + */
> +struct stm32_dfsdm {
> +	void __iomem	*base;
> +	phys_addr_t	phys_base;
> +	struct regmap *regmap;
> +	struct stm32_dfsdm_filter *fl_list;
> +	unsigned int num_fls;
> +	struct stm32_dfsdm_channel *ch_list;
> +	unsigned int num_chs;
> +	unsigned int spi_master_freq;
> +};
> +
> +struct stm32_dfsdm_str2field {
> +	const char	*name;
> +	unsigned int	val;
> +};
> +
> +/* DFSDM channel serial interface type */
> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
> +	{ 0, 0},
> +};
> +
> +/* DFSDM channel serial spi clock source */
> +enum stm32_dfsdm_spi_clk_src {
> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
> +};
> +
> +/* DFSDM channel clock source */
> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
> +	/* External SPI clock (CLKIN x) */
> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
> +	/* Internal SPI clock (CLKOUT) */
> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
> +	/* Internal SPI clock divided by 2 (falling edge) */
> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
> +	/* Internal SPI clock divided by 2 (falling edge) */
> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
> +	{ 0, 0 },
> +};
> +
> +/* DFSDM Serial interface trigger name  */
> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
> +
> +static inline int stm32_dfsdm_str2val(const char *str,
> +				      const struct stm32_dfsdm_str2field *list)
> +{
> +	const struct stm32_dfsdm_str2field *p = list;
> +
> +	for (p = list; p && p->name; p++) {
> +		if (!strcmp(p->name, str))
> +			return p->val;
> +	}
> +	return -EINVAL;
> +}
> +
> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
> +			 unsigned int oversamp);
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
> +
> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 unsigned int ch_id);
> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
> +			       struct stm32_dfsdm_channel *ch);
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
> +				 struct iio_dev *indio_dev,
> +				 struct iio_chan_spec *chan, int chan_idx);
> +
> +#endif
> 

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

* Re: [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-19 22:25       ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:25 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
> in n bit samples through a low pass filter and an integrator.
> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Various minor bits inline.

I'm mostly liking this.  I do slightly wondering if semantically it
should be the front end that has the channels rather than the
backend.  Would be fiddly to do though and probably not worth the
hassle.

Would love to see it running in a continuous mode in IIO, but
I guess that can follow along later.

The comment about the trigger has me confused
- perhaps you could elaborate further on that?

Jonathan
> ---
> V2 -> V3 :
> 	- Split audio and ADC support in 2 drivers
> 	- Implement DMA cyclic mode
> 	- Add SPI bus Trigger for buffer management
> 
>  drivers/iio/adc/Kconfig            |  26 ++
>  drivers/iio/adc/Makefile           |   2 +
>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>  5 files changed, 1477 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index d411d66..3e0eb11 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -452,6 +452,32 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_CORE
> +	tristate "STMicroelectronics STM32 dfsdm core"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the  driver for STMicroelectronics
> +	  STM32 digital filter for sigma delta converter.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-dfsdm-core.
> +
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 dfsdm adc"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select STM32_DFSDM_CORE
> +	select REGMAP_MMIO
> +	select IIO_BUFFER_DMAENGINE
> +	select IIO_HW_CONSUMER
> +	help
> +	  Select this option to support ADCSigma delta modulator for
> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
> +
> +	  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 c68819c..161f271 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,419 @@
> +/*
> + * This file is the ADC part of of the STM32 DFSDM driver
> + *
> + * Copyright (C) 2017, 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/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +#define DFSDM_TIMEOUT_US 100000
> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +struct stm32_dfsdm_adc {
> +	struct stm32_dfsdm *dfsdm;
> +	unsigned int fl_id;
> +	unsigned int ch_id;
> +
> +	unsigned int oversamp;
> +
> +	struct completion completion;
> +
> +	u32 *buffer;
> +
> +	/* Hardware consumer structure for Front End IIO */
> +	struct iio_hw_consumer *hwc;
> +};
> +
> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
> +{
> +	int ret;
> +
> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
> +	if (ret < 0)
> +		goto stop_dfsdm;
> +
> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	return 0;
> +
> +stop_channels:
> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
> +stop_dfsdm:
> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +
> +	return ret;
> +}
> +
> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
> +{
> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +
> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
> +
> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan, int *res)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	int ret;
> +
> +	reinit_completion(&adc->completion);
> +
> +	adc->buffer = res;
> +
> +	/* Unmask IRQ for regular conversion achievement*/
> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_conv(adc);
> +	if (ret < 0)
> +		return ret;
> +
> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> +							    DFSDM_TIMEOUT);
blank line perhaps.
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +
> +	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", *res);
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +
> +	stm32_dfsdm_stop_conv(adc);
> +
> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
> +	int ret = -EINVAL;
> +
> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
> +		if (!ret)
> +			adc->oversamp = val;
> +	}
blank line here.
> +	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);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_hw_consumer_enable(adc->hwc);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"%s: IIO enable failed (channel %d)\n",
> +				__func__, chan->channel);
> +			return ret;
> +		}
> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"%s: Conversion failed (channel %d)\n",
> +				__func__, chan->channel);
> +			return ret;
> +		}
> +
> +		iio_hw_consumer_disable(adc->hwc);
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		*val = adc->oversamp;
> +
> +		return IIO_VAL_INT;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_dfsdm_info_adc = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct stm32_dfsdm_adc *adc = arg;
> +	struct regmap *regmap = adc->dfsdm->regmap;
> +	unsigned int status;
> +
> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
> +
> +	if (status & DFSDM_ISR_REOCF_MASK) {
> +		/* read the data register clean the IRQ status */
> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
> +		complete(&adc->completion);
> +	}
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
What's this one?  Might want a comment given it's an irq you basically eat.
> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
> +				   DFSDM_ICR_CLRROVRF_MASK,
> +				   DFSDM_ICR_CLRROVRF_MASK);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return stm32_dfsdm_start_conv(adc);
> +}
> +
> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	stm32_dfsdm_stop_conv(adc);
blank line.
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
> +	.postenable = &stm32_dfsdm_postenable,
> +	.predisable = &stm32_dfsdm_predisable,
> +};
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int ch_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[ch_idx];
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = ch_idx;
> +
> +	/*
> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
> +	 */
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
> +
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +	adc->ch_id = ch->channel;
> +
> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
> +					  &adc->dfsdm->ch_list[ch->channel]);
> +}
> +
> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct iio_chan_spec *channels;
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int num_ch;
> +	int ret, chan_idx;
> +
> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
> +					     "st,adc-channels");
> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
> +		return num_ch < 0 ? num_ch : -EINVAL;
> +	}
> +
> +	/*
> +	 * Number of channel per filter is temporary limited to 1.
> +	 * Restriction should be cleaned with scan mode
> +	 */
> +	if (num_ch > 1) {
> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Bind to SD modulator IIO device */
> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
> +	if (IS_ERR(adc->hwc))
> +		return -EPROBE_DEFER;
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		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 free_hwc;
> +	}
> +
> +	indio_dev->num_channels = num_ch;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +
> +free_hwc:
> +	iio_hw_consumer_free(adc->hwc);
Given you have to free this in the error path, I would imagine you will
need a free somewhere in the main remove path?  Or just create a devm
version of iio_hw_consumer_alloc.  It will be useful in the long run.
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc"},
> +	{}
> +};
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	struct device_node *np = dev->of_node;
> +	struct iio_dev *iio;
> +	char *name;
> +	int ret, irq, val;
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
> +		return PTR_ERR(iio);
> +	}
> +
> +	adc = iio_priv(iio);
> +	if (IS_ERR(adc)) {
> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
> +		return PTR_ERR(adc);
> +	}
> +	adc->dfsdm = dev_get_drvdata(dev->parent);
> +
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = &stm32_dfsdm_info_adc;
> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
> +	if (ret != 0) {
> +		dev_err(dev, "Missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
not freed.  Maybe devm_kzalloc
> +	if (!name)
> +		return -ENOMEM;
> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
> +	iio->name = name;
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
> +	 */
> +	irq = platform_get_irq(pdev, 0);
> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
> +			       0, pdev->name, adc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request IRQ\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set filter order\n");
> +		return ret;
> +	}
> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
> +	if (!ret)
> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
> +
> +	ret = stm32_dfsdm_adc_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	init_completion(&adc->completion);
> +
> +	return iio_device_register(iio);
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	iio_device_unregister(iio);
If all you have is this in remove, you can probably get away with
devm_iio_device_register and get rid of the remove entirely.
> +
> +	return 0;
> +}
> +
> +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");
> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
> new file mode 100644
> index 0000000..488e456
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
> @@ -0,0 +1,658 @@
> +/*
> + * This file is part the core part STM32 DFSDM 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/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +struct stm32_dfsdm_dev_data {
> +	unsigned int num_filters;
> +	unsigned int num_channels;
> +	const struct regmap_config *regmap_cfg;
> +};
> +
> +#define STM32H7_DFSDM_NUM_FILTERS	4
> +#define STM32H7_DFSDM_NUM_CHANNELS	8
> +
> +#define DFSDM_MAX_INT_OVERSAMPLING 256
> +
> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
> +
> +#define DFSDM_MAX_RES BIT(31)
> +#define DFSDM_DATA_RES BIT(23)
> +
> +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 = 0x2B8,
> +	.volatile_reg = stm32_dfsdm_volatile_reg,
> +	.fast_io = true,
> +};
> +
> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
> +};
> +
> +struct dfsdm_priv {
> +	struct platform_device *pdev; /* platform device*/
> +
> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
> +
> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
> +	atomic_t n_active_ch;	/* number of current active channels */
> +
> +	/* Clock */
> +	struct clk *clk; /* DFSDM clock */
> +	struct clk *aclk; /* audio clock */
> +};
> +
> +/**
> + * stm32_dfsdm_set_osrs - compute filter parameters.
Naming would suggest it's more specific than this.
Setting over sampling ratios?
> + *
> + * Enable interface if n_active_ch is not null.
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fast: Fast mode enabled or disabled
> + * @oversamp: Expected oversampling between filtered sample and SD input stream
> + */
> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
> +			 unsigned int oversamp)
> +{
> +	unsigned int i, d, fosr, iosr;
> +	u64 res;
> +	s64 delta;
> +	unsigned int m = 1;	/* multiplication factor */
> +	unsigned int p = fl->ford;	/* filter order (ford) */
> +
> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
> +	/*
> +	 * This function tries to compute filter oversampling and integrator
> +	 * oversampling, base on oversampling ratio requested by user.
> +	 *
> +	 * Decimation d depends on the filter order and the oversampling ratios.
> +	 * ford: filter order
> +	 * fosr: filter over sampling ratio
> +	 * iosr: integrator over sampling ratio
> +	 */
> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
> +		m = 2;
> +		p = 2;
> +	}
> +
> +	/*
> +	 * Looks for filter and integrator oversampling ratios which allows
> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
> +				d = fosr * (iosr + 3) + 2;
> +			else
> +				d = fosr * (iosr - 1 + p) + p;
> +
> +			if (d > oversamp)
> +				break;
> +			else if (d != oversamp)
> +				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 > DFSDM_MAX_RES)
> +					break;
> +			}
> +			if (res > DFSDM_MAX_RES)
> +				continue;
> +			res = res * (u64)m * (u64)iosr;
> +			if (res > DFSDM_MAX_RES)
> +				continue;
> +
> +			delta = res - DFSDM_DATA_RES;
> +
> +			if (res >= fl->res) {
> +				fl->res = res;
> +				fl->fosr = fosr;
> +				fl->iosr = iosr;
> +				fl->fast = fast;
> +				pr_debug("%s: fosr = %d, iosr = %d\n",
> +					 __func__, fl->fosr, fl->iosr);
> +			}
> +
> +			if (!delta)
> +				return 0;
> +		}
> +	}
> +
> +	if (!fl->fosr)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/**
> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
> + *
> + * Enable interface if n_active_ch is not null.
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + */
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	struct device *dev = &priv->pdev->dev;
> +	unsigned int clk_div = priv->spi_clk_out_div;
> +	int ret;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		/* Enable clocks */
> +		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");
> +				goto disable_clk;
> +			}
> +		}
> +
> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
> +		if (ret < 0)
> +			goto disable_aclk;
> +
> +		/* Global enable of DFSDM interface */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(1));
> +		if (ret < 0)
> +			goto disable_aclk;
> +	}
> +
> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +
> +disable_aclk:
> +	clk_disable_unprepare(priv->aclk);
> +disable_clk:
> +	clk_disable_unprepare(priv->clk);
> +
> +	return ret;
> +}
> +
> +/**
> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
> + *
> + * Disable interface if n_active_ch is null
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + */
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	int ret;
> +
> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
> +		/* Global disable of DFSDM interface */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Stop SPI CLKOUT */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Disable clocks */
> +		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));
> +
> +	return 0;
> +}
> +
> +/**
> + * stm32_dfsdm_start_channel
> + *	Start DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: Channel index.
> + */
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
> +				  DFSDM_CHCFGR1_CHEN_MASK,
> +				  DFSDM_CHCFGR1_CHEN(1));
> +}
> +
> +/**
> + * stm32_dfsdm_stop_channel
> + *	Stop DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: Channel index.
> + */
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
> +			   DFSDM_CHCFGR1_CHEN_MASK,
> +			   DFSDM_CHCFGR1_CHEN(0));
> +}
> +
> +/**
> + * stm32_dfsdm_chan_configure
> + *	Configure DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: channel index.
> + */
> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
> +			       struct stm32_dfsdm_channel *ch)
> +{
> +	unsigned int id = ch->id;
> +	struct regmap *regmap = dfsdm->regmap;
> +	int ret;
> +
> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				 DFSDM_CHCFGR1_SITP_MASK,
> +				 DFSDM_CHCFGR1_SITP(ch->type));
> +	if (ret < 0)
> +		return ret;
Blank line here and in similar places makes it easier for my
eyes to parse at least...
I'd also like to see some docs in here, not all of these
are self explanatory.
> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
> +	if (ret < 0)
> +		return ret;
> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
> +}
> +
> +/**
> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter index.
> + */
> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	int ret;
> +
> +	/* Enable filter */
> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Start conversion */
> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_RSWSTART_MASK,
> +				  DFSDM_CR1_RSWSTART(1));
> +}
> +
> +/**
> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter index.
> + */
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +	/* Disable conversion */
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
> +}
> +
> +/**
> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
> + *	to channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: channel index.
> + * @fl_id: Filter index.
> + */
> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 unsigned int ch_id)
> +{
> +	struct regmap *regmap = dfsdm->regmap;
> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
> +	int ret;
> +
> +	/* Average integrator oversampling */
> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
> +				 DFSDM_FCR_IOSR(fl->iosr));
> +
> +	/* Filter order and Oversampling */
Please handle each error properly as it happens rather than mudling onwards.
If there is reason for this odd construction, then document it clearly.
> +	if (!ret)
> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
> +					 DFSDM_FCR_FOSR_MASK,
> +					 DFSDM_FCR_FOSR(fl->fosr));
> +
> +	if (!ret)
> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
> +					 DFSDM_FCR_FORD_MASK,
> +					 DFSDM_FCR_FORD(fl->ford));
> +
> +	/* If only one channel no scan mode supported for the moment */
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> +				 DFSDM_CR1_RCH_MASK,
> +				 DFSDM_CR1_RCH(ch_id));
> +
> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> +					 DFSDM_CR1_RSYNC_MASK,
> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
> +}
> +
> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
> +					 struct stm32_dfsdm *dfsdm)
> +{
> +	/*
> +	 * To be able to use buffer consumer interface a trigger is needed.
> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
> +	 * sense to define the serial interface ( SPI or manchester) as
> +	 * trigger source.
It's not actually the case that you have to have a triggrer.
There are plenty of drivers (particularly ones with hardware buffering)
where there is no trigger envolved.  That's not to say it doesn't make sense
here.

I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
somewhat...
> +	 */
> +
> +	struct iio_trigger *trig;
> +	int ret;
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
> +	if (!trig)
> +		return -ENOMEM;
> +
> +	trig->dev.parent = pdev->dev.parent;
> +	trig->ops = &dfsdm_trigger_ops;
> +
> +	iio_trigger_set_drvdata(trig, dfsdm);
> +
> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
> +	if (ret)
> +		return ret;
Just return ret in all cases.
> +
> +	return 0;
> +}
> +
> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
> +				 struct iio_dev *indio_dev,
> +				 struct iio_chan_spec *chan, int chan_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	struct stm32_dfsdm_channel *df_ch;
> +	const char *of_str;
> +	int ret, val;
> +
> +	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;
> +	}
> +
> +	df_ch =  &dfsdm->ch_list[ch->channel];
Extra space on the line above.
> +	df_ch->id = ch->channel;
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-types", chan_idx,
> +					    &of_str);
> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
> +	if (ret < 0 || val < 0)
> +		df_ch->type = 0;
> +	else
> +		df_ch->type = val;
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-clk-src", chan_idx,
> +					    &of_str);
> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
> +	if (ret < 0 || val < 0)
> +		df_ch->src = 0;
> +	else
> +		df_ch->src = val;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-alt-channel", chan_idx,
> +					 &df_ch->alt_si);
> +	if (ret < 0)
> +		df_ch->alt_si = 0;
> +
> +	return 0;
> +}
> +
> +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;
> +	unsigned long clk_freq;
> +	unsigned int spi_freq, rem;
> +	int ret;
> +
> +	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->dfsdm.phys_base = res->start;
> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
> +
> +	/* Source clock */
> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
> +	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");
> +	if (IS_ERR(priv->aclk))
> +		priv->aclk = NULL;
> +
> +	if (priv->aclk)
> +		clk_freq = clk_get_rate(priv->aclk);
> +	else
> +		clk_freq = clk_get_rate(priv->clk);
> +
> +	/* SPI clock freq */
> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
> +				   &spi_freq);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
> +		return ret;
> +	}
> +
> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
> +	priv->dfsdm.spi_master_freq = spi_freq;
> +
> +	if (rem) {
> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
> +	}
> +
> +	return 0;
> +};
> +
> +static const struct of_device_id stm32_dfsdm_of_match[] = {
> +	{
> +		.compatible = "st,stm32h7-dfsdm",
> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
> +	struct stm32_dfsdm *dfsdm;
> +	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_dfsdm_dev_data *)of_id->data;
> +	dfsdm = &priv->dfsdm;
> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
> +	if (!dfsdm->fl_list)
> +		return -ENOMEM;
> +
> +	dfsdm->num_fls = dev_data->num_filters;
> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
> +				      sizeof(*dfsdm->ch_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->ch_list)
> +		return -ENOMEM;
> +	dfsdm->num_chs = dev_data->num_channels;
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
> +					    &stm32h7_dfsdm_regmap_cfg);
> +	if (IS_ERR(dfsdm->regmap)) {
> +		ret = PTR_ERR(dfsdm->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
> +
> +		fl->id = i;
I'd like a comment on why this is needed...
> +	}
> +
> +	platform_set_drvdata(pdev, dfsdm);
> +
> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
> +}
> +
> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
> new file mode 100644
> index 0000000..bb7d74f
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm.h
> @@ -0,0 +1,371 @@
> +/*
> + * This file is part of STM32 DFSDM 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__H
> +#define MDF_STM32_DFSDM__H
> +
> +#include <linux/bitfield.h>
> +
> +#include <linux/iio/iio.h>
> +/*
> + * STM32 DFSDM - global register map
> + * ________________________________________________________
> + * | Offset |                 Registers block             |
> + * --------------------------------------------------------
> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
> + * --------------------------------------------------------
> + * | 0x020  |                CHANNEL 1                    |
> + * --------------------------------------------------------
> + * | ...    |                .....                        |
> + * --------------------------------------------------------
> + * | 0x0E0  |                CHANNEL 7                    |
> + * --------------------------------------------------------
> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
> + * --------------------------------------------------------
> + * | 0x200  |                FILTER  1                    |
> + * --------------------------------------------------------
> + * | 0x300  |                FILTER  2                    |
> + * --------------------------------------------------------
> + * | 0x400  |                FILTER  3                    |
> + * --------------------------------------------------------
> + */
> +
> +/*
> + * Channels register 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 register 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)
> +
> +/* 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,
> +};
> +
> +/**
> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
> + * TODO: complete structure.
nice :) RFC I guess :)
> + * @id:		filetr ID,
> + */
> +struct stm32_dfsdm_filter {
> +	unsigned int id;
> +	unsigned int iosr; /* integrator oversampling */
> +	unsigned int fosr; /* filter oversampling */
> +	enum stm32_dfsdm_sinc_order ford;
> +	u64 res; /* output sample resolution */
> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
> +	unsigned int fast; /* filter fast mode */
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
> + * TODO: complete structure.
> + * @id:		filetr ID,
filter
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;   /* id of the channel */
> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
> +	unsigned int alt_si; /* use alternative serial input interface */
> +};
> +
> +/**
> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
> + * @base:	control registers base cpu addr
> + * @phys_base:	DFSDM IP register physical address.
> + * @fl_list:	filter resources list
> + * @num_fl:	number of filter resources available
> + * @ch_list:	channel resources list
> + * @num_chs:	number of channel resources available
> + */
> +struct stm32_dfsdm {
> +	void __iomem	*base;
> +	phys_addr_t	phys_base;
> +	struct regmap *regmap;
> +	struct stm32_dfsdm_filter *fl_list;
> +	unsigned int num_fls;
> +	struct stm32_dfsdm_channel *ch_list;
> +	unsigned int num_chs;
> +	unsigned int spi_master_freq;
> +};
> +
> +struct stm32_dfsdm_str2field {
> +	const char	*name;
> +	unsigned int	val;
> +};
> +
> +/* DFSDM channel serial interface type */
> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
> +	{ 0, 0},
> +};
> +
> +/* DFSDM channel serial spi clock source */
> +enum stm32_dfsdm_spi_clk_src {
> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
> +};
> +
> +/* DFSDM channel clock source */
> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
> +	/* External SPI clock (CLKIN x) */
> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
> +	/* Internal SPI clock (CLKOUT) */
> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
> +	/* Internal SPI clock divided by 2 (falling edge) */
> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
> +	/* Internal SPI clock divided by 2 (falling edge) */
> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
> +	{ 0, 0 },
> +};
> +
> +/* DFSDM Serial interface trigger name  */
> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
> +
> +static inline int stm32_dfsdm_str2val(const char *str,
> +				      const struct stm32_dfsdm_str2field *list)
> +{
> +	const struct stm32_dfsdm_str2field *p = list;
> +
> +	for (p = list; p && p->name; p++) {
> +		if (!strcmp(p->name, str))
> +			return p->val;
> +	}
> +	return -EINVAL;
> +}
> +
> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
> +			 unsigned int oversamp);
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
> +
> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 unsigned int ch_id);
> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
> +			       struct stm32_dfsdm_channel *ch);
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
> +				 struct iio_dev *indio_dev,
> +				 struct iio_chan_spec *chan, int chan_idx);
> +
> +#endif
> 


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

* [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-19 22:25       ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:25 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
> in n bit samples through a low pass filter and an integrator.
> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Various minor bits inline.

I'm mostly liking this.  I do slightly wondering if semantically it
should be the front end that has the channels rather than the
backend.  Would be fiddly to do though and probably not worth the
hassle.

Would love to see it running in a continuous mode in IIO, but
I guess that can follow along later.

The comment about the trigger has me confused
- perhaps you could elaborate further on that?

Jonathan
> ---
> V2 -> V3 :
> 	- Split audio and ADC support in 2 drivers
> 	- Implement DMA cyclic mode
> 	- Add SPI bus Trigger for buffer management
> 
>  drivers/iio/adc/Kconfig            |  26 ++
>  drivers/iio/adc/Makefile           |   2 +
>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>  5 files changed, 1477 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index d411d66..3e0eb11 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -452,6 +452,32 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_CORE
> +	tristate "STMicroelectronics STM32 dfsdm core"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the  driver for STMicroelectronics
> +	  STM32 digital filter for sigma delta converter.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-dfsdm-core.
> +
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 dfsdm adc"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select STM32_DFSDM_CORE
> +	select REGMAP_MMIO
> +	select IIO_BUFFER_DMAENGINE
> +	select IIO_HW_CONSUMER
> +	help
> +	  Select this option to support ADCSigma delta modulator for
> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
> +
> +	  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 c68819c..161f271 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,419 @@
> +/*
> + * This file is the ADC part of of the STM32 DFSDM driver
> + *
> + * Copyright (C) 2017, 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/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +#define DFSDM_TIMEOUT_US 100000
> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +struct stm32_dfsdm_adc {
> +	struct stm32_dfsdm *dfsdm;
> +	unsigned int fl_id;
> +	unsigned int ch_id;
> +
> +	unsigned int oversamp;
> +
> +	struct completion completion;
> +
> +	u32 *buffer;
> +
> +	/* Hardware consumer structure for Front End IIO */
> +	struct iio_hw_consumer *hwc;
> +};
> +
> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
> +{
> +	int ret;
> +
> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
> +	if (ret < 0)
> +		goto stop_dfsdm;
> +
> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	return 0;
> +
> +stop_channels:
> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
> +stop_dfsdm:
> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +
> +	return ret;
> +}
> +
> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
> +{
> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +
> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
> +
> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan, int *res)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	int ret;
> +
> +	reinit_completion(&adc->completion);
> +
> +	adc->buffer = res;
> +
> +	/* Unmask IRQ for regular conversion achievement*/
> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_conv(adc);
> +	if (ret < 0)
> +		return ret;
> +
> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> +							    DFSDM_TIMEOUT);
blank line perhaps.
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +
> +	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", *res);
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +
> +	stm32_dfsdm_stop_conv(adc);
> +
> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
> +	int ret = -EINVAL;
> +
> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
> +		if (!ret)
> +			adc->oversamp = val;
> +	}
blank line here.
> +	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);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_hw_consumer_enable(adc->hwc);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"%s: IIO enable failed (channel %d)\n",
> +				__func__, chan->channel);
> +			return ret;
> +		}
> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"%s: Conversion failed (channel %d)\n",
> +				__func__, chan->channel);
> +			return ret;
> +		}
> +
> +		iio_hw_consumer_disable(adc->hwc);
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		*val = adc->oversamp;
> +
> +		return IIO_VAL_INT;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_dfsdm_info_adc = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct stm32_dfsdm_adc *adc = arg;
> +	struct regmap *regmap = adc->dfsdm->regmap;
> +	unsigned int status;
> +
> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
> +
> +	if (status & DFSDM_ISR_REOCF_MASK) {
> +		/* read the data register clean the IRQ status */
> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
> +		complete(&adc->completion);
> +	}
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
What's this one?  Might want a comment given it's an irq you basically eat.
> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
> +				   DFSDM_ICR_CLRROVRF_MASK,
> +				   DFSDM_ICR_CLRROVRF_MASK);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return stm32_dfsdm_start_conv(adc);
> +}
> +
> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	stm32_dfsdm_stop_conv(adc);
blank line.
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
> +	.postenable = &stm32_dfsdm_postenable,
> +	.predisable = &stm32_dfsdm_predisable,
> +};
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int ch_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[ch_idx];
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = ch_idx;
> +
> +	/*
> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
> +	 */
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
> +
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +	adc->ch_id = ch->channel;
> +
> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
> +					  &adc->dfsdm->ch_list[ch->channel]);
> +}
> +
> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct iio_chan_spec *channels;
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int num_ch;
> +	int ret, chan_idx;
> +
> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
> +					     "st,adc-channels");
> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
> +		return num_ch < 0 ? num_ch : -EINVAL;
> +	}
> +
> +	/*
> +	 * Number of channel per filter is temporary limited to 1.
> +	 * Restriction should be cleaned with scan mode
> +	 */
> +	if (num_ch > 1) {
> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Bind to SD modulator IIO device */
> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
> +	if (IS_ERR(adc->hwc))
> +		return -EPROBE_DEFER;
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		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 free_hwc;
> +	}
> +
> +	indio_dev->num_channels = num_ch;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +
> +free_hwc:
> +	iio_hw_consumer_free(adc->hwc);
Given you have to free this in the error path, I would imagine you will
need a free somewhere in the main remove path?  Or just create a devm
version of iio_hw_consumer_alloc.  It will be useful in the long run.
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc"},
> +	{}
> +};
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	struct device_node *np = dev->of_node;
> +	struct iio_dev *iio;
> +	char *name;
> +	int ret, irq, val;
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
> +		return PTR_ERR(iio);
> +	}
> +
> +	adc = iio_priv(iio);
> +	if (IS_ERR(adc)) {
> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
> +		return PTR_ERR(adc);
> +	}
> +	adc->dfsdm = dev_get_drvdata(dev->parent);
> +
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = &stm32_dfsdm_info_adc;
> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
> +	if (ret != 0) {
> +		dev_err(dev, "Missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
not freed.  Maybe devm_kzalloc
> +	if (!name)
> +		return -ENOMEM;
> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
> +	iio->name = name;
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
> +	 */
> +	irq = platform_get_irq(pdev, 0);
> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
> +			       0, pdev->name, adc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request IRQ\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set filter order\n");
> +		return ret;
> +	}
> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
> +	if (!ret)
> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
> +
> +	ret = stm32_dfsdm_adc_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	init_completion(&adc->completion);
> +
> +	return iio_device_register(iio);
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	iio_device_unregister(iio);
If all you have is this in remove, you can probably get away with
devm_iio_device_register and get rid of the remove entirely.
> +
> +	return 0;
> +}
> +
> +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");
> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
> new file mode 100644
> index 0000000..488e456
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
> @@ -0,0 +1,658 @@
> +/*
> + * This file is part the core part STM32 DFSDM 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/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +struct stm32_dfsdm_dev_data {
> +	unsigned int num_filters;
> +	unsigned int num_channels;
> +	const struct regmap_config *regmap_cfg;
> +};
> +
> +#define STM32H7_DFSDM_NUM_FILTERS	4
> +#define STM32H7_DFSDM_NUM_CHANNELS	8
> +
> +#define DFSDM_MAX_INT_OVERSAMPLING 256
> +
> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
> +
> +#define DFSDM_MAX_RES BIT(31)
> +#define DFSDM_DATA_RES BIT(23)
> +
> +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 = 0x2B8,
> +	.volatile_reg = stm32_dfsdm_volatile_reg,
> +	.fast_io = true,
> +};
> +
> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
> +};
> +
> +struct dfsdm_priv {
> +	struct platform_device *pdev; /* platform device*/
> +
> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
> +
> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
> +	atomic_t n_active_ch;	/* number of current active channels */
> +
> +	/* Clock */
> +	struct clk *clk; /* DFSDM clock */
> +	struct clk *aclk; /* audio clock */
> +};
> +
> +/**
> + * stm32_dfsdm_set_osrs - compute filter parameters.
Naming would suggest it's more specific than this.
Setting over sampling ratios?
> + *
> + * Enable interface if n_active_ch is not null.
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fast: Fast mode enabled or disabled
> + * @oversamp: Expected oversampling between filtered sample and SD input stream
> + */
> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
> +			 unsigned int oversamp)
> +{
> +	unsigned int i, d, fosr, iosr;
> +	u64 res;
> +	s64 delta;
> +	unsigned int m = 1;	/* multiplication factor */
> +	unsigned int p = fl->ford;	/* filter order (ford) */
> +
> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
> +	/*
> +	 * This function tries to compute filter oversampling and integrator
> +	 * oversampling, base on oversampling ratio requested by user.
> +	 *
> +	 * Decimation d depends on the filter order and the oversampling ratios.
> +	 * ford: filter order
> +	 * fosr: filter over sampling ratio
> +	 * iosr: integrator over sampling ratio
> +	 */
> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
> +		m = 2;
> +		p = 2;
> +	}
> +
> +	/*
> +	 * Looks for filter and integrator oversampling ratios which allows
> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
> +				d = fosr * (iosr + 3) + 2;
> +			else
> +				d = fosr * (iosr - 1 + p) + p;
> +
> +			if (d > oversamp)
> +				break;
> +			else if (d != oversamp)
> +				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 > DFSDM_MAX_RES)
> +					break;
> +			}
> +			if (res > DFSDM_MAX_RES)
> +				continue;
> +			res = res * (u64)m * (u64)iosr;
> +			if (res > DFSDM_MAX_RES)
> +				continue;
> +
> +			delta = res - DFSDM_DATA_RES;
> +
> +			if (res >= fl->res) {
> +				fl->res = res;
> +				fl->fosr = fosr;
> +				fl->iosr = iosr;
> +				fl->fast = fast;
> +				pr_debug("%s: fosr = %d, iosr = %d\n",
> +					 __func__, fl->fosr, fl->iosr);
> +			}
> +
> +			if (!delta)
> +				return 0;
> +		}
> +	}
> +
> +	if (!fl->fosr)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/**
> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
> + *
> + * Enable interface if n_active_ch is not null.
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + */
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	struct device *dev = &priv->pdev->dev;
> +	unsigned int clk_div = priv->spi_clk_out_div;
> +	int ret;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		/* Enable clocks */
> +		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");
> +				goto disable_clk;
> +			}
> +		}
> +
> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
> +		if (ret < 0)
> +			goto disable_aclk;
> +
> +		/* Global enable of DFSDM interface */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(1));
> +		if (ret < 0)
> +			goto disable_aclk;
> +	}
> +
> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +
> +disable_aclk:
> +	clk_disable_unprepare(priv->aclk);
> +disable_clk:
> +	clk_disable_unprepare(priv->clk);
> +
> +	return ret;
> +}
> +
> +/**
> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
> + *
> + * Disable interface if n_active_ch is null
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + */
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	int ret;
> +
> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
> +		/* Global disable of DFSDM interface */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Stop SPI CLKOUT */
> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Disable clocks */
> +		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));
> +
> +	return 0;
> +}
> +
> +/**
> + * stm32_dfsdm_start_channel
> + *	Start DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: Channel index.
> + */
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
> +				  DFSDM_CHCFGR1_CHEN_MASK,
> +				  DFSDM_CHCFGR1_CHEN(1));
> +}
> +
> +/**
> + * stm32_dfsdm_stop_channel
> + *	Stop DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: Channel index.
> + */
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
> +			   DFSDM_CHCFGR1_CHEN_MASK,
> +			   DFSDM_CHCFGR1_CHEN(0));
> +}
> +
> +/**
> + * stm32_dfsdm_chan_configure
> + *	Configure DFSDM IP channels and associated interface.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: channel index.
> + */
> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
> +			       struct stm32_dfsdm_channel *ch)
> +{
> +	unsigned int id = ch->id;
> +	struct regmap *regmap = dfsdm->regmap;
> +	int ret;
> +
> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				 DFSDM_CHCFGR1_SITP_MASK,
> +				 DFSDM_CHCFGR1_SITP(ch->type));
> +	if (ret < 0)
> +		return ret;
Blank line here and in similar places makes it easier for my
eyes to parse at least...
I'd also like to see some docs in here, not all of these
are self explanatory.
> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
> +	if (ret < 0)
> +		return ret;
> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
> +}
> +
> +/**
> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter index.
> + */
> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	int ret;
> +
> +	/* Enable filter */
> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Start conversion */
> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_RSWSTART_MASK,
> +				  DFSDM_CR1_RSWSTART(1));
> +}
> +
> +/**
> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter index.
> + */
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	/* Mask IRQ for regular conversion achievement*/
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> +	/* Disable conversion */
> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
> +}
> +
> +/**
> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
> + *	to channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: channel index.
> + * @fl_id: Filter index.
> + */
> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 unsigned int ch_id)
> +{
> +	struct regmap *regmap = dfsdm->regmap;
> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
> +	int ret;
> +
> +	/* Average integrator oversampling */
> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
> +				 DFSDM_FCR_IOSR(fl->iosr));
> +
> +	/* Filter order and Oversampling */
Please handle each error properly as it happens rather than mudling onwards.
If there is reason for this odd construction, then document it clearly.
> +	if (!ret)
> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
> +					 DFSDM_FCR_FOSR_MASK,
> +					 DFSDM_FCR_FOSR(fl->fosr));
> +
> +	if (!ret)
> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
> +					 DFSDM_FCR_FORD_MASK,
> +					 DFSDM_FCR_FORD(fl->ford));
> +
> +	/* If only one channel no scan mode supported for the moment */
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> +				 DFSDM_CR1_RCH_MASK,
> +				 DFSDM_CR1_RCH(ch_id));
> +
> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> +					 DFSDM_CR1_RSYNC_MASK,
> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
> +}
> +
> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
> +	.owner = THIS_MODULE,
> +};
> +
> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
> +					 struct stm32_dfsdm *dfsdm)
> +{
> +	/*
> +	 * To be able to use buffer consumer interface a trigger is needed.
> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
> +	 * sense to define the serial interface ( SPI or manchester) as
> +	 * trigger source.
It's not actually the case that you have to have a triggrer.
There are plenty of drivers (particularly ones with hardware buffering)
where there is no trigger envolved.  That's not to say it doesn't make sense
here.

I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
somewhat...
> +	 */
> +
> +	struct iio_trigger *trig;
> +	int ret;
> +
> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
> +	if (!trig)
> +		return -ENOMEM;
> +
> +	trig->dev.parent = pdev->dev.parent;
> +	trig->ops = &dfsdm_trigger_ops;
> +
> +	iio_trigger_set_drvdata(trig, dfsdm);
> +
> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
> +	if (ret)
> +		return ret;
Just return ret in all cases.
> +
> +	return 0;
> +}
> +
> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
> +				 struct iio_dev *indio_dev,
> +				 struct iio_chan_spec *chan, int chan_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	struct stm32_dfsdm_channel *df_ch;
> +	const char *of_str;
> +	int ret, val;
> +
> +	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;
> +	}
> +
> +	df_ch =  &dfsdm->ch_list[ch->channel];
Extra space on the line above.
> +	df_ch->id = ch->channel;
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-types", chan_idx,
> +					    &of_str);
> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
> +	if (ret < 0 || val < 0)
> +		df_ch->type = 0;
> +	else
> +		df_ch->type = val;
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-clk-src", chan_idx,
> +					    &of_str);
> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
> +	if (ret < 0 || val < 0)
> +		df_ch->src = 0;
> +	else
> +		df_ch->src = val;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-alt-channel", chan_idx,
> +					 &df_ch->alt_si);
> +	if (ret < 0)
> +		df_ch->alt_si = 0;
> +
> +	return 0;
> +}
> +
> +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;
> +	unsigned long clk_freq;
> +	unsigned int spi_freq, rem;
> +	int ret;
> +
> +	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->dfsdm.phys_base = res->start;
> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
> +
> +	/* Source clock */
> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
> +	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");
> +	if (IS_ERR(priv->aclk))
> +		priv->aclk = NULL;
> +
> +	if (priv->aclk)
> +		clk_freq = clk_get_rate(priv->aclk);
> +	else
> +		clk_freq = clk_get_rate(priv->clk);
> +
> +	/* SPI clock freq */
> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
> +				   &spi_freq);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
> +		return ret;
> +	}
> +
> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
> +	priv->dfsdm.spi_master_freq = spi_freq;
> +
> +	if (rem) {
> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
> +	}
> +
> +	return 0;
> +};
> +
> +static const struct of_device_id stm32_dfsdm_of_match[] = {
> +	{
> +		.compatible = "st,stm32h7-dfsdm",
> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
> +	struct stm32_dfsdm *dfsdm;
> +	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_dfsdm_dev_data *)of_id->data;
> +	dfsdm = &priv->dfsdm;
> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
> +	if (!dfsdm->fl_list)
> +		return -ENOMEM;
> +
> +	dfsdm->num_fls = dev_data->num_filters;
> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
> +				      sizeof(*dfsdm->ch_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->ch_list)
> +		return -ENOMEM;
> +	dfsdm->num_chs = dev_data->num_channels;
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
> +					    &stm32h7_dfsdm_regmap_cfg);
> +	if (IS_ERR(dfsdm->regmap)) {
> +		ret = PTR_ERR(dfsdm->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
> +
> +		fl->id = i;
I'd like a comment on why this is needed...
> +	}
> +
> +	platform_set_drvdata(pdev, dfsdm);
> +
> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
> +}
> +
> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
> new file mode 100644
> index 0000000..bb7d74f
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm.h
> @@ -0,0 +1,371 @@
> +/*
> + * This file is part of STM32 DFSDM 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__H
> +#define MDF_STM32_DFSDM__H
> +
> +#include <linux/bitfield.h>
> +
> +#include <linux/iio/iio.h>
> +/*
> + * STM32 DFSDM - global register map
> + * ________________________________________________________
> + * | Offset |                 Registers block             |
> + * --------------------------------------------------------
> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
> + * --------------------------------------------------------
> + * | 0x020  |                CHANNEL 1                    |
> + * --------------------------------------------------------
> + * | ...    |                .....                        |
> + * --------------------------------------------------------
> + * | 0x0E0  |                CHANNEL 7                    |
> + * --------------------------------------------------------
> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
> + * --------------------------------------------------------
> + * | 0x200  |                FILTER  1                    |
> + * --------------------------------------------------------
> + * | 0x300  |                FILTER  2                    |
> + * --------------------------------------------------------
> + * | 0x400  |                FILTER  3                    |
> + * --------------------------------------------------------
> + */
> +
> +/*
> + * Channels register 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 register 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)
> +
> +/* 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,
> +};
> +
> +/**
> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
> + * TODO: complete structure.
nice :) RFC I guess :)
> + * @id:		filetr ID,
> + */
> +struct stm32_dfsdm_filter {
> +	unsigned int id;
> +	unsigned int iosr; /* integrator oversampling */
> +	unsigned int fosr; /* filter oversampling */
> +	enum stm32_dfsdm_sinc_order ford;
> +	u64 res; /* output sample resolution */
> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
> +	unsigned int fast; /* filter fast mode */
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
> + * TODO: complete structure.
> + * @id:		filetr ID,
filter
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;   /* id of the channel */
> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
> +	unsigned int alt_si; /* use alternative serial input interface */
> +};
> +
> +/**
> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
> + * @base:	control registers base cpu addr
> + * @phys_base:	DFSDM IP register physical address.
> + * @fl_list:	filter resources list
> + * @num_fl:	number of filter resources available
> + * @ch_list:	channel resources list
> + * @num_chs:	number of channel resources available
> + */
> +struct stm32_dfsdm {
> +	void __iomem	*base;
> +	phys_addr_t	phys_base;
> +	struct regmap *regmap;
> +	struct stm32_dfsdm_filter *fl_list;
> +	unsigned int num_fls;
> +	struct stm32_dfsdm_channel *ch_list;
> +	unsigned int num_chs;
> +	unsigned int spi_master_freq;
> +};
> +
> +struct stm32_dfsdm_str2field {
> +	const char	*name;
> +	unsigned int	val;
> +};
> +
> +/* DFSDM channel serial interface type */
> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
> +	{ 0, 0},
> +};
> +
> +/* DFSDM channel serial spi clock source */
> +enum stm32_dfsdm_spi_clk_src {
> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
> +};
> +
> +/* DFSDM channel clock source */
> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
> +	/* External SPI clock (CLKIN x) */
> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
> +	/* Internal SPI clock (CLKOUT) */
> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
> +	/* Internal SPI clock divided by 2 (falling edge) */
> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
> +	/* Internal SPI clock divided by 2 (falling edge) */
> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
> +	{ 0, 0 },
> +};
> +
> +/* DFSDM Serial interface trigger name  */
> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
> +
> +static inline int stm32_dfsdm_str2val(const char *str,
> +				      const struct stm32_dfsdm_str2field *list)
> +{
> +	const struct stm32_dfsdm_str2field *p = list;
> +
> +	for (p = list; p && p->name; p++) {
> +		if (!strcmp(p->name, str))
> +			return p->val;
> +	}
> +	return -EINVAL;
> +}
> +
> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
> +			 unsigned int oversamp);
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
> +
> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 unsigned int ch_id);
> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
> +			       struct stm32_dfsdm_channel *ch);
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
> +				 struct iio_dev *indio_dev,
> +				 struct iio_chan_spec *chan, int chan_idx);
> +
> +#endif
> 

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

* Re: [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-19 22:38       ` Jonathan Cameron
  -1 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:38 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add DFSDM driver to handle PDM audio microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
So key element here is that we really need to have a proper way of
doing DMA buffer consumers from IIO (as you said in your cover letter).

Not entirely obvious how to do this.  Would like some input from Lars
if possible.  I'm afraid I don't have an suitable hardware to try anything
out on (or the time really!).  This will obviously be quite different from
the single scan stuff you have seen is there at the moment.

Whether this whole approach makes sense vs doing the dma in the alsa driver
isn't clear to me.

On the plus side, presumably we'll get love dma ADC support for free
as part of doing it this way ;)

It's been a while since I looked at the IIO dma buffer stuff at all. Will
try and refresh my memory sometime this week.

Jonathan
> ---
>  drivers/iio/adc/Kconfig                   |  13 +
>  drivers/iio/adc/Makefile                  |   1 +
>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>  4 files changed, 775 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 3e0eb11..c108933 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-dfsdm-adc.
>  
> +config STM32_DFSDM_AUDIO
> +	tristate "STMicroelectronics STM32 dfsdm audio
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select STM32_DFSDM_CORE
> +	select REGMAP_MMIO
> +	select IIO_BUFFER_DMAENGINE
> +	help
> +	  Select this option to support Audio PDM micophone for
> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-dfsdm-audio.
> +
>  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 161f271..79f975d 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
> new file mode 100644
> index 0000000..115ef8f
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
> @@ -0,0 +1,720 @@
> +/*
> + * This file is the ADC part of of the STM32 DFSDM driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen-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/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
> +
> +struct stm32_dfsdm_audio {
> +	struct stm32_dfsdm *dfsdm;
> +	unsigned int fl_id;
> +	unsigned int ch_id;
> +	unsigned int spi_freq;  /* SPI bus clock frequency */
> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
> +
> +	u8 *rx_buf;
> +	unsigned int bufi; /* Buffer current position */
> +	unsigned int buf_sz; /* Buffer size */
> +
> +	struct dma_chan	*dma_chan;
> +	dma_addr_t dma_buf;
> +
> +	int (*cb)(const void *data, size_t size, void *cb_priv);
> +	void *cb_priv;
> +};
> +
> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
> +
> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
> +				    const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
> +}
> +
> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
> +				    const struct iio_chan_spec *chan,
> +				    const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
> +	unsigned int spi_freq = pdmc->spi_freq;
> +	unsigned int sample_freq;
> +	int ret;
> +
> +	ret = kstrtoint(buf, 0, &sample_freq);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
> +	if (!sample_freq)
> +		return -EINVAL;
> +
> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +		spi_freq = pdmc->dfsdm->spi_master_freq;
> +
> +	if (spi_freq % sample_freq)
> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
> +			 spi_freq / (spi_freq / sample_freq));
> +
> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			"Not able to find filter parameter that match!\n");
> +		return ret;
> +	}
> +	pdmc->sample_freq = sample_freq;
> +
> +	return len;
> +}
> +
> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
> +				      const struct iio_chan_spec *chan,
> +				      char *buf)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
> +}
> +
> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
> +				      const struct iio_chan_spec *chan,
> +				      const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
> +	unsigned int sample_freq = pdmc->sample_freq;
> +	unsigned int spi_freq;
> +	int ret;
> +
> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +		return -EPERM;
> +
> +	ret = kstrtoint(buf, 0, &spi_freq);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
> +	if (!spi_freq)
> +		return -EINVAL;
> +
> +	if (sample_freq) {
> +		if (spi_freq % sample_freq)
> +			dev_warn(&indio_dev->dev,
> +				 "Sampling rate not accurate (%d)\n",
> +				 spi_freq / (spi_freq / sample_freq));
> +
> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"No filter parameters that match!\n");
> +			return ret;
> +		}
> +	}
> +	pdmc->spi_freq = spi_freq;
> +
> +	return len;
> +}
> +
> +/*
> + * Define external info for SPI Frequency and audio sampling rate that can be
> + * configured by ASoC driver through consumer.h API
> + */
> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
> +	/* filter oversampling: Post filter oversampling ratio */
> +	{
> +		.name = "audio_sampling_rate",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = dfsdm_audio_get_rate,
> +		.write = dfsdm_audio_set_rate,
> +	},
> +	/* data_right_bit_shift : Filter output data shifting */
> +	{
> +		.name = "spi_clk_freq",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = dfsdm_audio_get_spiclk,
> +		.write = dfsdm_audio_set_spiclk,
> +	},
> +	{},
> +};
> +
> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
> +{
> +	struct regmap *regmap = pdmc->dfsdm->regmap;
> +	int ret;
> +
> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
> +	if (ret < 0)
> +		goto stop_dfsdm;
> +
> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
> +					   pdmc->ch_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	/* Enable DMA transfer*/
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Enable conversion triggered by SPI clock*/
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	return 0;
> +
> +stop_channels:
> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
> +stop_dfsdm:
> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
> +
> +	return ret;
> +}
> +
> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
> +{
> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
> +
> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
> +
> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
> +}
> +
> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
> +				     unsigned int val)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
> +
> +	/*
> +	 * DMA cyclic transfers are used, buffer is split into two periods.
> +	 * There should be :
> +	 * - always one buffer (period) DMA is working on
> +	 * - one buffer (period) driver pushed to ASoC side ().
> +	 */
> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
> +	pdmc->buf_sz = watermark * 2;
> +
> +	return 0;
> +}
> +
> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
> +				 struct iio_trigger *trig)
> +{
> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_dfsdm_info_pdmc = {
> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
> +	.driver_module = THIS_MODULE,
> +	.validate_trigger = stm32_dfsdm_validate_trigger,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct stm32_dfsdm_audio *pdmc = arg;
> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
> +	struct regmap *regmap = pdmc->dfsdm->regmap;
> +	unsigned int status;
> +
> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
> +
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
> +				   DFSDM_ICR_CLRROVRF_MASK,
> +				   DFSDM_ICR_CLRROVRF_MASK);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
> +{
> +	struct dma_tx_state state;
> +	enum dma_status status;
> +
> +	status = dmaengine_tx_status(pdmc->dma_chan,
> +				     pdmc->dma_chan->cookie,
> +				     &state);
> +	if (status == DMA_IN_PROGRESS) {
> +		/* Residue is size in bytes from end of buffer */
> +		unsigned int i = pdmc->buf_sz - state.residue;
> +		unsigned int size;
> +
> +		/* Return available bytes */
> +		if (i >= pdmc->bufi)
> +			size = i - pdmc->bufi;
> +		else
> +			size = pdmc->buf_sz + i - pdmc->bufi;
> +
> +		return size;
> +	}
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
> +{
> +	struct iio_dev *indio_dev = data;
> +
> +	iio_trigger_poll_chained(indio_dev->trig);
This shouldn't be going through the trigger infrastructure at all really.
I'm not 100% sure what doing so is gaining you...
> +}
> +
> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct dma_async_tx_descriptor *desc;
> +	dma_cookie_t cookie;
> +	int ret;
> +
> +	if (!pdmc->dma_chan)
> +		return -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
> +		pdmc->buf_sz, pdmc->buf_sz / 2);
> +
> +	/* Prepare a DMA cyclic transaction */
> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
> +					 pdmc->dma_buf,
> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
> +					 DMA_DEV_TO_MEM,
> +					 DMA_PREP_INTERRUPT);
> +	if (!desc)
> +		return -EBUSY;
> +
> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
> +	desc->callback_param = indio_dev;
> +
> +	cookie = dmaengine_submit(desc);
> +	ret = dma_submit_error(cookie);
> +	if (ret) {
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +		return ret;
> +	}
> +
> +	/* Issue pending DMA requests */
> +	dma_async_issue_pending(pdmc->dma_chan);
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	/* Reset pdmc buffer index */
> +	pdmc->bufi = 0;
> +
> +	ret = stm32_dfsdm_start_conv(pdmc, false);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
> +		goto err_stop_conv;
> +	}
> +
> +	ret = iio_triggered_buffer_postenable(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
> +		goto err_stop_dma;
> +	}
> +
> +	return 0;
> +
> +err_stop_dma:
> +	if (pdmc->dma_chan)
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +err_stop_conv:
> +	stm32_dfsdm_stop_conv(pdmc);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	ret = iio_triggered_buffer_predisable(indio_dev);
> +	if (ret < 0)
> +		dev_err(&indio_dev->dev, "Predisable failed\n");
> +
> +	if (pdmc->dma_chan)
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +
> +	stm32_dfsdm_stop_conv(pdmc);
> +
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
> +	.postenable = &stm32_dfsdm_postenable,
> +	.predisable = &stm32_dfsdm_predisable,
> +};
> +
> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
> +{
OK. So now I kind of understand what the trigger provided in the adc driver
was about.  hmm.  Triggers are supposed to be per sample so this is indeed
a hack.  We need fullblown DMA buffer consumers to do this right.

Probably best person to comment on that is Lars. 
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	size_t old_pos;
> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
> +
> +	/*
> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
> +	 * an interface to push data samples per samples.
> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
> +	 * and interface is hacked using a private callback registered by ASoC.
> +	 * This should be a temporary solution waiting a cyclic DMA engine
> +	 * support in IIO.
> +	 */
> +
> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
> +		pdmc->bufi, available);
> +	old_pos = pdmc->bufi;
> +	while (available >= indio_dev->scan_bytes) {
> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
> +
> +		/* Mask 8 LSB that contains the channel ID */
> +		*buffer &= 0xFFFFFF00;
> +		available -= indio_dev->scan_bytes;
> +		pdmc->bufi += indio_dev->scan_bytes;
> +		if (pdmc->bufi >= pdmc->buf_sz) {
> +			if (pdmc->cb)
> +				pdmc->cb(&pdmc->rx_buf[old_pos],
> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
> +			pdmc->bufi = 0;
> +			old_pos = 0;
> +		}
> +	}
> +	if (pdmc->cb)
> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
> +				pdmc->cb_priv);
> +
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * stm32_dfsdm_get_buff_cb - register a callback
> + *	that will be called when DMA transfer period is achieved.
> + *
> + * @iio_dev: Handle to IIO device.
> + * @cb: pointer to callback function.
> + *	@data: pointer to data buffer
> + *	@size: size in byte of the data buffer
> + *	@private: pointer to consumer private structure
> + * @private: pointer to consumer private structure
> + */
> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
> +			    int (*cb)(const void *data, size_t size,
> +				      void *private),
> +			    void *private)
> +{
> +	struct stm32_dfsdm_audio *pdmc;
> +
> +	if (!iio_dev)
> +		return -EINVAL;
> +	pdmc = iio_priv(iio_dev);
> +
> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
> +		return -EINVAL;
> +
> +	pdmc->cb = cb;
> +	pdmc->cb_priv = private;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
> +
> +/**
> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
> + *
> + * @iio_dev: Handle to IIO device.
> + */
> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc;
> +
> +	if (!iio_dev)
> +		return -EINVAL;
> +	pdmc = iio_priv(iio_dev);
> +
> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
> +		return -EINVAL;
> +	pdmc->cb = NULL;
> +	pdmc->cb_priv = NULL;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
> +
> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct iio_chan_spec *ch;
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
> +	if (!ch)
> +		return -ENOMEM;
> +
> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = 0;
> +	ch->ext_info = dfsdm_adc_ext_info;
> +
> +	ch->scan_type.sign = 's';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +
> +	pdmc->ch_id = ch->channel;
> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
> +					 &pdmc->dfsdm->ch_list[ch->channel]);
> +
> +	indio_dev->num_channels = 1;
> +	indio_dev->channels = ch;
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-audio"},
> +	{}
> +};
> +
> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct dma_slave_config config;
> +	int ret;
> +
> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
> +	if (!pdmc->dma_chan)
> +		return -EINVAL;
> +
> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
> +					 DFSDM_DMA_BUFFER_SIZE,
> +					 &pdmc->dma_buf, GFP_KERNEL);
> +	if (!pdmc->rx_buf) {
> +		ret = -ENOMEM;
> +		goto err_release;
> +	}
> +
> +	/* Configure DMA channel to read data register */
> +	memset(&config, 0, sizeof(config));
> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +
> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
> +	if (ret)
> +		goto err_free;
> +
> +	return 0;
> +
> +err_free:
> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
> +			  pdmc->rx_buf, pdmc->dma_buf);
> +err_release:
> +	dma_release_channel(pdmc->dma_chan);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_audio *pdmc;
> +	struct device_node *np = dev->of_node;
> +	struct iio_dev *iio;
> +	char *name;
> +	int ret, irq, val;
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
> +		return PTR_ERR(iio);
> +	}
> +
> +	pdmc = iio_priv(iio);
> +	if (IS_ERR(pdmc)) {
> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
> +		return PTR_ERR(pdmc);
> +	}
> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
> +
> +	iio->name = np->name;
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = &stm32_dfsdm_info_pdmc;
> +	iio->modes = INDIO_DIRECT_MODE;
> +
> +	platform_set_drvdata(pdev, pdmc);
> +
> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
> +	if (ret != 0) {
> +		dev_err(dev, "Missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
> +	if (!name)
> +		return -ENOMEM;
> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
> +	iio->name = name;
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
> +	 */
> +	irq = platform_get_irq(pdev, 0);
> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
> +			       0, pdev->name, pdmc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request IRQ\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set filter order\n");
> +		return ret;
> +	}
> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
> +	if (!ret)
> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
> +
> +	ret = stm32_dfsdm_audio_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_audio_dma_request(iio);
> +	if (ret) {
> +		dev_err(&pdev->dev, "DMA request failed\n");
> +		return ret;
> +	}
> +
> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
> +
> +	ret = iio_triggered_buffer_setup(iio,
> +					 &iio_pollfunc_store_time,
> +					 &stm32_dfsdm_audio_trigger_handler,
> +					 &stm32_dfsdm_buffer_setup_ops);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Buffer setup failed\n");
> +		goto err_dma_disable;
> +	}
> +
> +	ret = iio_device_register(iio);
> +	if (ret) {
> +		dev_err(&pdev->dev, "IIO dev register failed\n");
> +		goto err_buffer_cleanup;
> +	}
> +
> +	return 0;
> +
> +err_buffer_cleanup:
> +	iio_triggered_buffer_cleanup(iio);
> +
> +err_dma_disable:
> +	if (pdmc->dma_chan) {
> +		dma_free_coherent(pdmc->dma_chan->device->dev,
> +				  DFSDM_DMA_BUFFER_SIZE,
> +				  pdmc->rx_buf, pdmc->dma_buf);
> +		dma_release_channel(pdmc->dma_chan);
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
> +
> +	iio_device_unregister(iio);
Same as in the other driver. Can just use the devm_iio_device_register
version and drop remove.
> +
> +	return 0;
> +}
> +
> +static struct platform_driver stm32_dfsdm_audio_driver = {
> +	.driver = {
> +		.name = "stm32-dfsdm-audio",
> +		.of_match_table = stm32_dfsdm_audio_match,
> +	},
> +	.probe = stm32_dfsdm_audio_probe,
> +	.remove = stm32_dfsdm_audio_remove,
> +};
> +module_platform_driver(stm32_dfsdm_audio_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
> new file mode 100644
> index 0000000..9b41b28
> --- /dev/null
> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
> @@ -0,0 +1,41 @@
> +/*
> + * This file discribe the STM32 DFSDM IIO driver API for audio part
> + *
> + * 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 STM32_DFSDM_AUDIO_H
> +#define STM32_DFSDM_AUDIO_H
> +
> +/**
> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
> + * @dev:	Pointer to client device.
> + * @indio_dev:	Device on which the channel exists.
> + * @cb:		Callback function.
> + *		@data:  pointer to data buffer
> + *		@size: size of the data buffer in bytes
> + * @private:	Private data passed to callback.
> + *
> + */
> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
> +			    int (*cb)(const void *data, size_t size,
> +				      void *private),
> +			    void *private);
> +/**
> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
> + * @indio_dev:	Device on which the channel exists.
> + */
> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
> +
> +#endif
> 

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

* Re: [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-19 22:38       ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:38 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add DFSDM driver to handle PDM audio microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
So key element here is that we really need to have a proper way of
doing DMA buffer consumers from IIO (as you said in your cover letter).

Not entirely obvious how to do this.  Would like some input from Lars
if possible.  I'm afraid I don't have an suitable hardware to try anything
out on (or the time really!).  This will obviously be quite different from
the single scan stuff you have seen is there at the moment.

Whether this whole approach makes sense vs doing the dma in the alsa driver
isn't clear to me.

On the plus side, presumably we'll get love dma ADC support for free
as part of doing it this way ;)

It's been a while since I looked at the IIO dma buffer stuff at all. Will
try and refresh my memory sometime this week.

Jonathan
> ---
>  drivers/iio/adc/Kconfig                   |  13 +
>  drivers/iio/adc/Makefile                  |   1 +
>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>  4 files changed, 775 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 3e0eb11..c108933 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-dfsdm-adc.
>  
> +config STM32_DFSDM_AUDIO
> +	tristate "STMicroelectronics STM32 dfsdm audio
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select STM32_DFSDM_CORE
> +	select REGMAP_MMIO
> +	select IIO_BUFFER_DMAENGINE
> +	help
> +	  Select this option to support Audio PDM micophone for
> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-dfsdm-audio.
> +
>  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 161f271..79f975d 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
> new file mode 100644
> index 0000000..115ef8f
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
> @@ -0,0 +1,720 @@
> +/*
> + * This file is the ADC part of of the STM32 DFSDM driver
> + *
> + * Copyright (C) 2017, 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/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
> +
> +struct stm32_dfsdm_audio {
> +	struct stm32_dfsdm *dfsdm;
> +	unsigned int fl_id;
> +	unsigned int ch_id;
> +	unsigned int spi_freq;  /* SPI bus clock frequency */
> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
> +
> +	u8 *rx_buf;
> +	unsigned int bufi; /* Buffer current position */
> +	unsigned int buf_sz; /* Buffer size */
> +
> +	struct dma_chan	*dma_chan;
> +	dma_addr_t dma_buf;
> +
> +	int (*cb)(const void *data, size_t size, void *cb_priv);
> +	void *cb_priv;
> +};
> +
> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
> +
> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
> +				    const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
> +}
> +
> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
> +				    const struct iio_chan_spec *chan,
> +				    const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
> +	unsigned int spi_freq = pdmc->spi_freq;
> +	unsigned int sample_freq;
> +	int ret;
> +
> +	ret = kstrtoint(buf, 0, &sample_freq);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
> +	if (!sample_freq)
> +		return -EINVAL;
> +
> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +		spi_freq = pdmc->dfsdm->spi_master_freq;
> +
> +	if (spi_freq % sample_freq)
> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
> +			 spi_freq / (spi_freq / sample_freq));
> +
> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			"Not able to find filter parameter that match!\n");
> +		return ret;
> +	}
> +	pdmc->sample_freq = sample_freq;
> +
> +	return len;
> +}
> +
> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
> +				      const struct iio_chan_spec *chan,
> +				      char *buf)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
> +}
> +
> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
> +				      const struct iio_chan_spec *chan,
> +				      const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
> +	unsigned int sample_freq = pdmc->sample_freq;
> +	unsigned int spi_freq;
> +	int ret;
> +
> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +		return -EPERM;
> +
> +	ret = kstrtoint(buf, 0, &spi_freq);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
> +	if (!spi_freq)
> +		return -EINVAL;
> +
> +	if (sample_freq) {
> +		if (spi_freq % sample_freq)
> +			dev_warn(&indio_dev->dev,
> +				 "Sampling rate not accurate (%d)\n",
> +				 spi_freq / (spi_freq / sample_freq));
> +
> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"No filter parameters that match!\n");
> +			return ret;
> +		}
> +	}
> +	pdmc->spi_freq = spi_freq;
> +
> +	return len;
> +}
> +
> +/*
> + * Define external info for SPI Frequency and audio sampling rate that can be
> + * configured by ASoC driver through consumer.h API
> + */
> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
> +	/* filter oversampling: Post filter oversampling ratio */
> +	{
> +		.name = "audio_sampling_rate",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = dfsdm_audio_get_rate,
> +		.write = dfsdm_audio_set_rate,
> +	},
> +	/* data_right_bit_shift : Filter output data shifting */
> +	{
> +		.name = "spi_clk_freq",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = dfsdm_audio_get_spiclk,
> +		.write = dfsdm_audio_set_spiclk,
> +	},
> +	{},
> +};
> +
> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
> +{
> +	struct regmap *regmap = pdmc->dfsdm->regmap;
> +	int ret;
> +
> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
> +	if (ret < 0)
> +		goto stop_dfsdm;
> +
> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
> +					   pdmc->ch_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	/* Enable DMA transfer*/
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Enable conversion triggered by SPI clock*/
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	return 0;
> +
> +stop_channels:
> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
> +stop_dfsdm:
> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
> +
> +	return ret;
> +}
> +
> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
> +{
> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
> +
> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
> +
> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
> +}
> +
> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
> +				     unsigned int val)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
> +
> +	/*
> +	 * DMA cyclic transfers are used, buffer is split into two periods.
> +	 * There should be :
> +	 * - always one buffer (period) DMA is working on
> +	 * - one buffer (period) driver pushed to ASoC side ().
> +	 */
> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
> +	pdmc->buf_sz = watermark * 2;
> +
> +	return 0;
> +}
> +
> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
> +				 struct iio_trigger *trig)
> +{
> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_dfsdm_info_pdmc = {
> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
> +	.driver_module = THIS_MODULE,
> +	.validate_trigger = stm32_dfsdm_validate_trigger,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct stm32_dfsdm_audio *pdmc = arg;
> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
> +	struct regmap *regmap = pdmc->dfsdm->regmap;
> +	unsigned int status;
> +
> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
> +
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
> +				   DFSDM_ICR_CLRROVRF_MASK,
> +				   DFSDM_ICR_CLRROVRF_MASK);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
> +{
> +	struct dma_tx_state state;
> +	enum dma_status status;
> +
> +	status = dmaengine_tx_status(pdmc->dma_chan,
> +				     pdmc->dma_chan->cookie,
> +				     &state);
> +	if (status == DMA_IN_PROGRESS) {
> +		/* Residue is size in bytes from end of buffer */
> +		unsigned int i = pdmc->buf_sz - state.residue;
> +		unsigned int size;
> +
> +		/* Return available bytes */
> +		if (i >= pdmc->bufi)
> +			size = i - pdmc->bufi;
> +		else
> +			size = pdmc->buf_sz + i - pdmc->bufi;
> +
> +		return size;
> +	}
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
> +{
> +	struct iio_dev *indio_dev = data;
> +
> +	iio_trigger_poll_chained(indio_dev->trig);
This shouldn't be going through the trigger infrastructure at all really.
I'm not 100% sure what doing so is gaining you...
> +}
> +
> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct dma_async_tx_descriptor *desc;
> +	dma_cookie_t cookie;
> +	int ret;
> +
> +	if (!pdmc->dma_chan)
> +		return -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
> +		pdmc->buf_sz, pdmc->buf_sz / 2);
> +
> +	/* Prepare a DMA cyclic transaction */
> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
> +					 pdmc->dma_buf,
> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
> +					 DMA_DEV_TO_MEM,
> +					 DMA_PREP_INTERRUPT);
> +	if (!desc)
> +		return -EBUSY;
> +
> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
> +	desc->callback_param = indio_dev;
> +
> +	cookie = dmaengine_submit(desc);
> +	ret = dma_submit_error(cookie);
> +	if (ret) {
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +		return ret;
> +	}
> +
> +	/* Issue pending DMA requests */
> +	dma_async_issue_pending(pdmc->dma_chan);
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	/* Reset pdmc buffer index */
> +	pdmc->bufi = 0;
> +
> +	ret = stm32_dfsdm_start_conv(pdmc, false);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
> +		goto err_stop_conv;
> +	}
> +
> +	ret = iio_triggered_buffer_postenable(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
> +		goto err_stop_dma;
> +	}
> +
> +	return 0;
> +
> +err_stop_dma:
> +	if (pdmc->dma_chan)
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +err_stop_conv:
> +	stm32_dfsdm_stop_conv(pdmc);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	ret = iio_triggered_buffer_predisable(indio_dev);
> +	if (ret < 0)
> +		dev_err(&indio_dev->dev, "Predisable failed\n");
> +
> +	if (pdmc->dma_chan)
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +
> +	stm32_dfsdm_stop_conv(pdmc);
> +
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
> +	.postenable = &stm32_dfsdm_postenable,
> +	.predisable = &stm32_dfsdm_predisable,
> +};
> +
> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
> +{
OK. So now I kind of understand what the trigger provided in the adc driver
was about.  hmm.  Triggers are supposed to be per sample so this is indeed
a hack.  We need fullblown DMA buffer consumers to do this right.

Probably best person to comment on that is Lars. 
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	size_t old_pos;
> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
> +
> +	/*
> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
> +	 * an interface to push data samples per samples.
> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
> +	 * and interface is hacked using a private callback registered by ASoC.
> +	 * This should be a temporary solution waiting a cyclic DMA engine
> +	 * support in IIO.
> +	 */
> +
> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
> +		pdmc->bufi, available);
> +	old_pos = pdmc->bufi;
> +	while (available >= indio_dev->scan_bytes) {
> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
> +
> +		/* Mask 8 LSB that contains the channel ID */
> +		*buffer &= 0xFFFFFF00;
> +		available -= indio_dev->scan_bytes;
> +		pdmc->bufi += indio_dev->scan_bytes;
> +		if (pdmc->bufi >= pdmc->buf_sz) {
> +			if (pdmc->cb)
> +				pdmc->cb(&pdmc->rx_buf[old_pos],
> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
> +			pdmc->bufi = 0;
> +			old_pos = 0;
> +		}
> +	}
> +	if (pdmc->cb)
> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
> +				pdmc->cb_priv);
> +
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * stm32_dfsdm_get_buff_cb - register a callback
> + *	that will be called when DMA transfer period is achieved.
> + *
> + * @iio_dev: Handle to IIO device.
> + * @cb: pointer to callback function.
> + *	@data: pointer to data buffer
> + *	@size: size in byte of the data buffer
> + *	@private: pointer to consumer private structure
> + * @private: pointer to consumer private structure
> + */
> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
> +			    int (*cb)(const void *data, size_t size,
> +				      void *private),
> +			    void *private)
> +{
> +	struct stm32_dfsdm_audio *pdmc;
> +
> +	if (!iio_dev)
> +		return -EINVAL;
> +	pdmc = iio_priv(iio_dev);
> +
> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
> +		return -EINVAL;
> +
> +	pdmc->cb = cb;
> +	pdmc->cb_priv = private;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
> +
> +/**
> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
> + *
> + * @iio_dev: Handle to IIO device.
> + */
> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc;
> +
> +	if (!iio_dev)
> +		return -EINVAL;
> +	pdmc = iio_priv(iio_dev);
> +
> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
> +		return -EINVAL;
> +	pdmc->cb = NULL;
> +	pdmc->cb_priv = NULL;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
> +
> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct iio_chan_spec *ch;
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
> +	if (!ch)
> +		return -ENOMEM;
> +
> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = 0;
> +	ch->ext_info = dfsdm_adc_ext_info;
> +
> +	ch->scan_type.sign = 's';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +
> +	pdmc->ch_id = ch->channel;
> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
> +					 &pdmc->dfsdm->ch_list[ch->channel]);
> +
> +	indio_dev->num_channels = 1;
> +	indio_dev->channels = ch;
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-audio"},
> +	{}
> +};
> +
> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct dma_slave_config config;
> +	int ret;
> +
> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
> +	if (!pdmc->dma_chan)
> +		return -EINVAL;
> +
> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
> +					 DFSDM_DMA_BUFFER_SIZE,
> +					 &pdmc->dma_buf, GFP_KERNEL);
> +	if (!pdmc->rx_buf) {
> +		ret = -ENOMEM;
> +		goto err_release;
> +	}
> +
> +	/* Configure DMA channel to read data register */
> +	memset(&config, 0, sizeof(config));
> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +
> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
> +	if (ret)
> +		goto err_free;
> +
> +	return 0;
> +
> +err_free:
> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
> +			  pdmc->rx_buf, pdmc->dma_buf);
> +err_release:
> +	dma_release_channel(pdmc->dma_chan);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_audio *pdmc;
> +	struct device_node *np = dev->of_node;
> +	struct iio_dev *iio;
> +	char *name;
> +	int ret, irq, val;
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
> +		return PTR_ERR(iio);
> +	}
> +
> +	pdmc = iio_priv(iio);
> +	if (IS_ERR(pdmc)) {
> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
> +		return PTR_ERR(pdmc);
> +	}
> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
> +
> +	iio->name = np->name;
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = &stm32_dfsdm_info_pdmc;
> +	iio->modes = INDIO_DIRECT_MODE;
> +
> +	platform_set_drvdata(pdev, pdmc);
> +
> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
> +	if (ret != 0) {
> +		dev_err(dev, "Missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
> +	if (!name)
> +		return -ENOMEM;
> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
> +	iio->name = name;
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
> +	 */
> +	irq = platform_get_irq(pdev, 0);
> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
> +			       0, pdev->name, pdmc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request IRQ\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set filter order\n");
> +		return ret;
> +	}
> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
> +	if (!ret)
> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
> +
> +	ret = stm32_dfsdm_audio_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_audio_dma_request(iio);
> +	if (ret) {
> +		dev_err(&pdev->dev, "DMA request failed\n");
> +		return ret;
> +	}
> +
> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
> +
> +	ret = iio_triggered_buffer_setup(iio,
> +					 &iio_pollfunc_store_time,
> +					 &stm32_dfsdm_audio_trigger_handler,
> +					 &stm32_dfsdm_buffer_setup_ops);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Buffer setup failed\n");
> +		goto err_dma_disable;
> +	}
> +
> +	ret = iio_device_register(iio);
> +	if (ret) {
> +		dev_err(&pdev->dev, "IIO dev register failed\n");
> +		goto err_buffer_cleanup;
> +	}
> +
> +	return 0;
> +
> +err_buffer_cleanup:
> +	iio_triggered_buffer_cleanup(iio);
> +
> +err_dma_disable:
> +	if (pdmc->dma_chan) {
> +		dma_free_coherent(pdmc->dma_chan->device->dev,
> +				  DFSDM_DMA_BUFFER_SIZE,
> +				  pdmc->rx_buf, pdmc->dma_buf);
> +		dma_release_channel(pdmc->dma_chan);
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
> +
> +	iio_device_unregister(iio);
Same as in the other driver. Can just use the devm_iio_device_register
version and drop remove.
> +
> +	return 0;
> +}
> +
> +static struct platform_driver stm32_dfsdm_audio_driver = {
> +	.driver = {
> +		.name = "stm32-dfsdm-audio",
> +		.of_match_table = stm32_dfsdm_audio_match,
> +	},
> +	.probe = stm32_dfsdm_audio_probe,
> +	.remove = stm32_dfsdm_audio_remove,
> +};
> +module_platform_driver(stm32_dfsdm_audio_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
> new file mode 100644
> index 0000000..9b41b28
> --- /dev/null
> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
> @@ -0,0 +1,41 @@
> +/*
> + * This file discribe the STM32 DFSDM IIO driver API for audio part
> + *
> + * 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 STM32_DFSDM_AUDIO_H
> +#define STM32_DFSDM_AUDIO_H
> +
> +/**
> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
> + * @dev:	Pointer to client device.
> + * @indio_dev:	Device on which the channel exists.
> + * @cb:		Callback function.
> + *		@data:  pointer to data buffer
> + *		@size: size of the data buffer in bytes
> + * @private:	Private data passed to callback.
> + *
> + */
> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
> +			    int (*cb)(const void *data, size_t size,
> +				      void *private),
> +			    void *private);
> +/**
> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
> + * @indio_dev:	Device on which the channel exists.
> + */
> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
> +
> +#endif
> 


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

* [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-19 22:38       ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:38 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add DFSDM driver to handle PDM audio microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
So key element here is that we really need to have a proper way of
doing DMA buffer consumers from IIO (as you said in your cover letter).

Not entirely obvious how to do this.  Would like some input from Lars
if possible.  I'm afraid I don't have an suitable hardware to try anything
out on (or the time really!).  This will obviously be quite different from
the single scan stuff you have seen is there at the moment.

Whether this whole approach makes sense vs doing the dma in the alsa driver
isn't clear to me.

On the plus side, presumably we'll get love dma ADC support for free
as part of doing it this way ;)

It's been a while since I looked at the IIO dma buffer stuff at all. Will
try and refresh my memory sometime this week.

Jonathan
> ---
>  drivers/iio/adc/Kconfig                   |  13 +
>  drivers/iio/adc/Makefile                  |   1 +
>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>  4 files changed, 775 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 3e0eb11..c108933 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-dfsdm-adc.
>  
> +config STM32_DFSDM_AUDIO
> +	tristate "STMicroelectronics STM32 dfsdm audio
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select STM32_DFSDM_CORE
> +	select REGMAP_MMIO
> +	select IIO_BUFFER_DMAENGINE
> +	help
> +	  Select this option to support Audio PDM micophone for
> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-dfsdm-audio.
> +
>  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 161f271..79f975d 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
> new file mode 100644
> index 0000000..115ef8f
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
> @@ -0,0 +1,720 @@
> +/*
> + * This file is the ADC part of of the STM32 DFSDM driver
> + *
> + * Copyright (C) 2017, 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/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
> +
> +struct stm32_dfsdm_audio {
> +	struct stm32_dfsdm *dfsdm;
> +	unsigned int fl_id;
> +	unsigned int ch_id;
> +	unsigned int spi_freq;  /* SPI bus clock frequency */
> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
> +
> +	u8 *rx_buf;
> +	unsigned int bufi; /* Buffer current position */
> +	unsigned int buf_sz; /* Buffer size */
> +
> +	struct dma_chan	*dma_chan;
> +	dma_addr_t dma_buf;
> +
> +	int (*cb)(const void *data, size_t size, void *cb_priv);
> +	void *cb_priv;
> +};
> +
> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
> +
> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
> +				    const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
> +}
> +
> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
> +				    const struct iio_chan_spec *chan,
> +				    const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
> +	unsigned int spi_freq = pdmc->spi_freq;
> +	unsigned int sample_freq;
> +	int ret;
> +
> +	ret = kstrtoint(buf, 0, &sample_freq);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
> +	if (!sample_freq)
> +		return -EINVAL;
> +
> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +		spi_freq = pdmc->dfsdm->spi_master_freq;
> +
> +	if (spi_freq % sample_freq)
> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
> +			 spi_freq / (spi_freq / sample_freq));
> +
> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			"Not able to find filter parameter that match!\n");
> +		return ret;
> +	}
> +	pdmc->sample_freq = sample_freq;
> +
> +	return len;
> +}
> +
> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
> +				      const struct iio_chan_spec *chan,
> +				      char *buf)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
> +}
> +
> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
> +				      const struct iio_chan_spec *chan,
> +				      const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
> +	unsigned int sample_freq = pdmc->sample_freq;
> +	unsigned int spi_freq;
> +	int ret;
> +
> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
> +		return -EPERM;
> +
> +	ret = kstrtoint(buf, 0, &spi_freq);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
> +	if (!spi_freq)
> +		return -EINVAL;
> +
> +	if (sample_freq) {
> +		if (spi_freq % sample_freq)
> +			dev_warn(&indio_dev->dev,
> +				 "Sampling rate not accurate (%d)\n",
> +				 spi_freq / (spi_freq / sample_freq));
> +
> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev,
> +				"No filter parameters that match!\n");
> +			return ret;
> +		}
> +	}
> +	pdmc->spi_freq = spi_freq;
> +
> +	return len;
> +}
> +
> +/*
> + * Define external info for SPI Frequency and audio sampling rate that can be
> + * configured by ASoC driver through consumer.h API
> + */
> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
> +	/* filter oversampling: Post filter oversampling ratio */
> +	{
> +		.name = "audio_sampling_rate",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = dfsdm_audio_get_rate,
> +		.write = dfsdm_audio_set_rate,
> +	},
> +	/* data_right_bit_shift : Filter output data shifting */
> +	{
> +		.name = "spi_clk_freq",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = dfsdm_audio_get_spiclk,
> +		.write = dfsdm_audio_set_spiclk,
> +	},
> +	{},
> +};
> +
> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
> +{
> +	struct regmap *regmap = pdmc->dfsdm->regmap;
> +	int ret;
> +
> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
> +	if (ret < 0)
> +		goto stop_dfsdm;
> +
> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
> +					   pdmc->ch_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	/* Enable DMA transfer*/
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Enable conversion triggered by SPI clock*/
> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
> +	if (ret < 0)
> +		goto stop_channels;
> +
> +	return 0;
> +
> +stop_channels:
> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
> +stop_dfsdm:
> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
> +
> +	return ret;
> +}
> +
> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
> +{
> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
> +
> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
> +
> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
> +}
> +
> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
> +				     unsigned int val)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
> +
> +	/*
> +	 * DMA cyclic transfers are used, buffer is split into two periods.
> +	 * There should be :
> +	 * - always one buffer (period) DMA is working on
> +	 * - one buffer (period) driver pushed to ASoC side ().
> +	 */
> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
> +	pdmc->buf_sz = watermark * 2;
> +
> +	return 0;
> +}
> +
> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
> +				 struct iio_trigger *trig)
> +{
> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
> +		return 0;
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info stm32_dfsdm_info_pdmc = {
> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
> +	.driver_module = THIS_MODULE,
> +	.validate_trigger = stm32_dfsdm_validate_trigger,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct stm32_dfsdm_audio *pdmc = arg;
> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
> +	struct regmap *regmap = pdmc->dfsdm->regmap;
> +	unsigned int status;
> +
> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
> +
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
> +				   DFSDM_ICR_CLRROVRF_MASK,
> +				   DFSDM_ICR_CLRROVRF_MASK);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
> +{
> +	struct dma_tx_state state;
> +	enum dma_status status;
> +
> +	status = dmaengine_tx_status(pdmc->dma_chan,
> +				     pdmc->dma_chan->cookie,
> +				     &state);
> +	if (status == DMA_IN_PROGRESS) {
> +		/* Residue is size in bytes from end of buffer */
> +		unsigned int i = pdmc->buf_sz - state.residue;
> +		unsigned int size;
> +
> +		/* Return available bytes */
> +		if (i >= pdmc->bufi)
> +			size = i - pdmc->bufi;
> +		else
> +			size = pdmc->buf_sz + i - pdmc->bufi;
> +
> +		return size;
> +	}
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
> +{
> +	struct iio_dev *indio_dev = data;
> +
> +	iio_trigger_poll_chained(indio_dev->trig);
This shouldn't be going through the trigger infrastructure at all really.
I'm not 100% sure what doing so is gaining you...
> +}
> +
> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct dma_async_tx_descriptor *desc;
> +	dma_cookie_t cookie;
> +	int ret;
> +
> +	if (!pdmc->dma_chan)
> +		return -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
> +		pdmc->buf_sz, pdmc->buf_sz / 2);
> +
> +	/* Prepare a DMA cyclic transaction */
> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
> +					 pdmc->dma_buf,
> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
> +					 DMA_DEV_TO_MEM,
> +					 DMA_PREP_INTERRUPT);
> +	if (!desc)
> +		return -EBUSY;
> +
> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
> +	desc->callback_param = indio_dev;
> +
> +	cookie = dmaengine_submit(desc);
> +	ret = dma_submit_error(cookie);
> +	if (ret) {
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +		return ret;
> +	}
> +
> +	/* Issue pending DMA requests */
> +	dma_async_issue_pending(pdmc->dma_chan);
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	/* Reset pdmc buffer index */
> +	pdmc->bufi = 0;
> +
> +	ret = stm32_dfsdm_start_conv(pdmc, false);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
> +		goto err_stop_conv;
> +	}
> +
> +	ret = iio_triggered_buffer_postenable(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
> +		goto err_stop_dma;
> +	}
> +
> +	return 0;
> +
> +err_stop_dma:
> +	if (pdmc->dma_chan)
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +err_stop_conv:
> +	stm32_dfsdm_stop_conv(pdmc);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	ret = iio_triggered_buffer_predisable(indio_dev);
> +	if (ret < 0)
> +		dev_err(&indio_dev->dev, "Predisable failed\n");
> +
> +	if (pdmc->dma_chan)
> +		dmaengine_terminate_all(pdmc->dma_chan);
> +
> +	stm32_dfsdm_stop_conv(pdmc);
> +
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
> +	.postenable = &stm32_dfsdm_postenable,
> +	.predisable = &stm32_dfsdm_predisable,
> +};
> +
> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
> +{
OK. So now I kind of understand what the trigger provided in the adc driver
was about.  hmm.  Triggers are supposed to be per sample so this is indeed
a hack.  We need fullblown DMA buffer consumers to do this right.

Probably best person to comment on that is Lars. 
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	size_t old_pos;
> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
> +
> +	/*
> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
> +	 * an interface to push data samples per samples.
> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
> +	 * and interface is hacked using a private callback registered by ASoC.
> +	 * This should be a temporary solution waiting a cyclic DMA engine
> +	 * support in IIO.
> +	 */
> +
> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
> +		pdmc->bufi, available);
> +	old_pos = pdmc->bufi;
> +	while (available >= indio_dev->scan_bytes) {
> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
> +
> +		/* Mask 8 LSB that contains the channel ID */
> +		*buffer &= 0xFFFFFF00;
> +		available -= indio_dev->scan_bytes;
> +		pdmc->bufi += indio_dev->scan_bytes;
> +		if (pdmc->bufi >= pdmc->buf_sz) {
> +			if (pdmc->cb)
> +				pdmc->cb(&pdmc->rx_buf[old_pos],
> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
> +			pdmc->bufi = 0;
> +			old_pos = 0;
> +		}
> +	}
> +	if (pdmc->cb)
> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
> +				pdmc->cb_priv);
> +
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * stm32_dfsdm_get_buff_cb - register a callback
> + *	that will be called when DMA transfer period is achieved.
> + *
> + * @iio_dev: Handle to IIO device.
> + * @cb: pointer to callback function.
> + *	@data: pointer to data buffer
> + *	@size: size in byte of the data buffer
> + *	@private: pointer to consumer private structure
> + * @private: pointer to consumer private structure
> + */
> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
> +			    int (*cb)(const void *data, size_t size,
> +				      void *private),
> +			    void *private)
> +{
> +	struct stm32_dfsdm_audio *pdmc;
> +
> +	if (!iio_dev)
> +		return -EINVAL;
> +	pdmc = iio_priv(iio_dev);
> +
> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
> +		return -EINVAL;
> +
> +	pdmc->cb = cb;
> +	pdmc->cb_priv = private;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
> +
> +/**
> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
> + *
> + * @iio_dev: Handle to IIO device.
> + */
> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc;
> +
> +	if (!iio_dev)
> +		return -EINVAL;
> +	pdmc = iio_priv(iio_dev);
> +
> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
> +		return -EINVAL;
> +	pdmc->cb = NULL;
> +	pdmc->cb_priv = NULL;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
> +
> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct iio_chan_spec *ch;
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	int ret;
> +
> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
> +	if (!ch)
> +		return -ENOMEM;
> +
> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = 0;
> +	ch->ext_info = dfsdm_adc_ext_info;
> +
> +	ch->scan_type.sign = 's';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +
> +	pdmc->ch_id = ch->channel;
> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
> +					 &pdmc->dfsdm->ch_list[ch->channel]);
> +
> +	indio_dev->num_channels = 1;
> +	indio_dev->channels = ch;
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-audio"},
> +	{}
> +};
> +
> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
> +	struct dma_slave_config config;
> +	int ret;
> +
> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
> +	if (!pdmc->dma_chan)
> +		return -EINVAL;
> +
> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
> +					 DFSDM_DMA_BUFFER_SIZE,
> +					 &pdmc->dma_buf, GFP_KERNEL);
> +	if (!pdmc->rx_buf) {
> +		ret = -ENOMEM;
> +		goto err_release;
> +	}
> +
> +	/* Configure DMA channel to read data register */
> +	memset(&config, 0, sizeof(config));
> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +
> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
> +	if (ret)
> +		goto err_free;
> +
> +	return 0;
> +
> +err_free:
> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
> +			  pdmc->rx_buf, pdmc->dma_buf);
> +err_release:
> +	dma_release_channel(pdmc->dma_chan);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_audio *pdmc;
> +	struct device_node *np = dev->of_node;
> +	struct iio_dev *iio;
> +	char *name;
> +	int ret, irq, val;
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
> +		return PTR_ERR(iio);
> +	}
> +
> +	pdmc = iio_priv(iio);
> +	if (IS_ERR(pdmc)) {
> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
> +		return PTR_ERR(pdmc);
> +	}
> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
> +
> +	iio->name = np->name;
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = &stm32_dfsdm_info_pdmc;
> +	iio->modes = INDIO_DIRECT_MODE;
> +
> +	platform_set_drvdata(pdev, pdmc);
> +
> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
> +	if (ret != 0) {
> +		dev_err(dev, "Missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
> +	if (!name)
> +		return -ENOMEM;
> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
> +	iio->name = name;
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
> +	 */
> +	irq = platform_get_irq(pdev, 0);
> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
> +			       0, pdev->name, pdmc);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to request IRQ\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set filter order\n");
> +		return ret;
> +	}
> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
> +
> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
> +	if (!ret)
> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
> +
> +	ret = stm32_dfsdm_audio_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = stm32_dfsdm_audio_dma_request(iio);
> +	if (ret) {
> +		dev_err(&pdev->dev, "DMA request failed\n");
> +		return ret;
> +	}
> +
> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
> +
> +	ret = iio_triggered_buffer_setup(iio,
> +					 &iio_pollfunc_store_time,
> +					 &stm32_dfsdm_audio_trigger_handler,
> +					 &stm32_dfsdm_buffer_setup_ops);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Buffer setup failed\n");
> +		goto err_dma_disable;
> +	}
> +
> +	ret = iio_device_register(iio);
> +	if (ret) {
> +		dev_err(&pdev->dev, "IIO dev register failed\n");
> +		goto err_buffer_cleanup;
> +	}
> +
> +	return 0;
> +
> +err_buffer_cleanup:
> +	iio_triggered_buffer_cleanup(iio);
> +
> +err_dma_disable:
> +	if (pdmc->dma_chan) {
> +		dma_free_coherent(pdmc->dma_chan->device->dev,
> +				  DFSDM_DMA_BUFFER_SIZE,
> +				  pdmc->rx_buf, pdmc->dma_buf);
> +		dma_release_channel(pdmc->dma_chan);
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
> +{
> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
> +
> +	iio_device_unregister(iio);
Same as in the other driver. Can just use the devm_iio_device_register
version and drop remove.
> +
> +	return 0;
> +}
> +
> +static struct platform_driver stm32_dfsdm_audio_driver = {
> +	.driver = {
> +		.name = "stm32-dfsdm-audio",
> +		.of_match_table = stm32_dfsdm_audio_match,
> +	},
> +	.probe = stm32_dfsdm_audio_probe,
> +	.remove = stm32_dfsdm_audio_remove,
> +};
> +module_platform_driver(stm32_dfsdm_audio_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
> new file mode 100644
> index 0000000..9b41b28
> --- /dev/null
> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
> @@ -0,0 +1,41 @@
> +/*
> + * This file discribe the STM32 DFSDM IIO driver API for audio part
> + *
> + * 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 STM32_DFSDM_AUDIO_H
> +#define STM32_DFSDM_AUDIO_H
> +
> +/**
> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
> + * @dev:	Pointer to client device.
> + * @indio_dev:	Device on which the channel exists.
> + * @cb:		Callback function.
> + *		@data:  pointer to data buffer
> + *		@size: size of the data buffer in bytes
> + * @private:	Private data passed to callback.
> + *
> + */
> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
> +			    int (*cb)(const void *data, size_t size,
> +				      void *private),
> +			    void *private);
> +/**
> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
> + * @indio_dev:	Device on which the channel exists.
> + */
> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
> +
> +#endif
> 

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

* Re: [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-19 22:44       ` Jonathan Cameron
  -1 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:44 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add iio consumer API to set buffer size and watermark according
> to sysfs API.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
Hmm. Not keen on the length one.  Setting a requested watermark
is fair enough.  There is no actually buffer in these cases though
so setting it's length is downright odd..

Guess this is part of the hacks we need to clean up by doing
the dma buffer consumer stuff right...

Jonathan
> ---
>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>  include/linux/iio/consumer.h                | 13 +++++++++++++
>  2 files changed, 25 insertions(+)
> 
> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
> index b8f550e..43c066a 100644
> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>  }
>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>  
> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
> +				      size_t length, size_t watermark)
> +{
> +	if (!length || length < watermark)
> +		return -EINVAL;
> +	cb_buff->buffer.watermark = watermark;
> +	cb_buff->buffer.length = length;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
> +
>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>  {
>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
> index cb44771..0f6e94d 100644
> --- a/include/linux/iio/consumer.h
> +++ b/include/linux/iio/consumer.h
> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>  						       void *private),
>  					     void *private);
>  /**
> + * iio_channel_cb_set_buffer_size() - set the buffer length.
> + * @cb_buffer:		The callback buffer from whom we want the channel
> + *			information.
> + * @length: buffer length in bytes
> + * @watermark: buffer watermark in bytes
> + *
> + * This function allows to configure the buffer length. The watermark if
> + * forced to half of the buffer.
> + */
> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
> +				     size_t length, size_t watermark);
> +
> +/**
>   * iio_channel_release_all_cb() - release and unregister the callback.
>   * @cb_buffer:		The callback buffer that was allocated.
>   */
> 

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

* Re: [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-19 22:44       ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:44 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add iio consumer API to set buffer size and watermark according
> to sysfs API.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Hmm. Not keen on the length one.  Setting a requested watermark
is fair enough.  There is no actually buffer in these cases though
so setting it's length is downright odd..

Guess this is part of the hacks we need to clean up by doing
the dma buffer consumer stuff right...

Jonathan
> ---
>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>  include/linux/iio/consumer.h                | 13 +++++++++++++
>  2 files changed, 25 insertions(+)
> 
> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
> index b8f550e..43c066a 100644
> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>  }
>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>  
> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
> +				      size_t length, size_t watermark)
> +{
> +	if (!length || length < watermark)
> +		return -EINVAL;
> +	cb_buff->buffer.watermark = watermark;
> +	cb_buff->buffer.length = length;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
> +
>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>  {
>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
> index cb44771..0f6e94d 100644
> --- a/include/linux/iio/consumer.h
> +++ b/include/linux/iio/consumer.h
> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>  						       void *private),
>  					     void *private);
>  /**
> + * iio_channel_cb_set_buffer_size() - set the buffer length.
> + * @cb_buffer:		The callback buffer from whom we want the channel
> + *			information.
> + * @length: buffer length in bytes
> + * @watermark: buffer watermark in bytes
> + *
> + * This function allows to configure the buffer length. The watermark if
> + * forced to half of the buffer.
> + */
> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
> +				     size_t length, size_t watermark);
> +
> +/**
>   * iio_channel_release_all_cb() - release and unregister the callback.
>   * @cb_buffer:		The callback buffer that was allocated.
>   */
> 


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

* [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-19 22:44       ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-19 22:44 UTC (permalink / raw)
  To: linux-arm-kernel

On 17/03/17 14:08, Arnaud Pouliquen wrote:
> Add iio consumer API to set buffer size and watermark according
> to sysfs API.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Hmm. Not keen on the length one.  Setting a requested watermark
is fair enough.  There is no actually buffer in these cases though
so setting it's length is downright odd..

Guess this is part of the hacks we need to clean up by doing
the dma buffer consumer stuff right...

Jonathan
> ---
>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>  include/linux/iio/consumer.h                | 13 +++++++++++++
>  2 files changed, 25 insertions(+)
> 
> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
> index b8f550e..43c066a 100644
> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>  }
>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>  
> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
> +				      size_t length, size_t watermark)
> +{
> +	if (!length || length < watermark)
> +		return -EINVAL;
> +	cb_buff->buffer.watermark = watermark;
> +	cb_buff->buffer.length = length;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
> +
>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>  {
>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
> index cb44771..0f6e94d 100644
> --- a/include/linux/iio/consumer.h
> +++ b/include/linux/iio/consumer.h
> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>  						       void *private),
>  					     void *private);
>  /**
> + * iio_channel_cb_set_buffer_size() - set the buffer length.
> + * @cb_buffer:		The callback buffer from whom we want the channel
> + *			information.
> + * @length: buffer length in bytes
> + * @watermark: buffer watermark in bytes
> + *
> + * This function allows to configure the buffer length. The watermark if
> + * forced to half of the buffer.
> + */
> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
> +				     size_t length, size_t watermark);
> +
> +/**
>   * iio_channel_release_all_cb() - release and unregister the callback.
>   * @cb_buffer:		The callback buffer that was allocated.
>   */
> 

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

* Re: [alsa-devel] [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-20  6:22       ` kbuild test robot
  -1 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:22 UTC (permalink / raw)
  Cc: kbuild-all-JC7UmRfGjtg, Rob Herring, Mark Rutland,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, olivier moysan,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	arnaud.pouliquen-qxv4g6HH51o, Maxime Coquelin,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Alexandre Torgue

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

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: i386-randconfig-x073-201712 (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 error/warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:6:0,
                    from include/linux/kernel.h:6,
                    from drivers/iio/buffer/industrialio-buffer-cb.c:8:
>> include/linux/export.h:67:20: error: redefinition of '__kstrtab_iio_channel_start_all_cb'
     static const char __kstrtab_##sym[]    \
                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
>> drivers/iio/buffer/industrialio-buffer-cb.c:124:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:67:20: note: previous definition of '__kstrtab_iio_channel_start_all_cb' was here
     static const char __kstrtab_##sym[]    \
                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-cb.c:117:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:70:36: error: redefinition of '__ksymtab_iio_channel_start_all_cb'
     static const struct kernel_symbol __ksymtab_##sym  \
                                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
>> drivers/iio/buffer/industrialio-buffer-cb.c:124:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:70:36: note: previous definition of '__ksymtab_iio_channel_start_all_cb' was here
     static const struct kernel_symbol __ksymtab_##sym  \
                                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-cb.c:117:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~

vim +/__kstrtab_iio_channel_start_all_cb +67 include/linux/export.h

f5016932 Paul Gortmaker  2011-05-23  61  #endif
f5016932 Paul Gortmaker  2011-05-23  62  
f5016932 Paul Gortmaker  2011-05-23  63  /* For every exported symbol, place a struct in the __ksymtab section */
f2355416 Nicolas Pitre   2016-01-22  64  #define ___EXPORT_SYMBOL(sym, sec)					\
f5016932 Paul Gortmaker  2011-05-23  65  	extern typeof(sym) sym;						\
f5016932 Paul Gortmaker  2011-05-23  66  	__CRC_SYMBOL(sym, sec)						\
f5016932 Paul Gortmaker  2011-05-23 @67  	static const char __kstrtab_##sym[]				\
f5016932 Paul Gortmaker  2011-05-23  68  	__attribute__((section("__ksymtab_strings"), aligned(1)))	\
b92021b0 Rusty Russell   2013-03-15  69  	= VMLINUX_SYMBOL_STR(sym);					\
b67067f1 Nicholas Piggin 2016-08-24  70  	static const struct kernel_symbol __ksymtab_##sym		\

:::::: The code at line 67 was first introduced by commit
:::::: f50169324df4ad942e544386d136216c8617636a module.h: split out the EXPORT_SYMBOL into export.h

:::::: TO: Paul Gortmaker <paul.gortmaker-CWA4WttNNZF54TAoqtyWWQ@public.gmane.org>
:::::: CC: Paul Gortmaker <paul.gortmaker-CWA4WttNNZF54TAoqtyWWQ@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: 26427 bytes --]

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

* Re: [alsa-devel] [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-20  6:22       ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:22 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: kbuild-all, Rob Herring, Mark Rutland, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

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

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: i386-randconfig-x073-201712 (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 error/warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:6:0,
                    from include/linux/kernel.h:6,
                    from drivers/iio/buffer/industrialio-buffer-cb.c:8:
>> include/linux/export.h:67:20: error: redefinition of '__kstrtab_iio_channel_start_all_cb'
     static const char __kstrtab_##sym[]    \
                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
>> drivers/iio/buffer/industrialio-buffer-cb.c:124:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:67:20: note: previous definition of '__kstrtab_iio_channel_start_all_cb' was here
     static const char __kstrtab_##sym[]    \
                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-cb.c:117:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:70:36: error: redefinition of '__ksymtab_iio_channel_start_all_cb'
     static const struct kernel_symbol __ksymtab_##sym  \
                                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
>> drivers/iio/buffer/industrialio-buffer-cb.c:124:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:70:36: note: previous definition of '__ksymtab_iio_channel_start_all_cb' was here
     static const struct kernel_symbol __ksymtab_##sym  \
                                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-cb.c:117:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~

vim +/__kstrtab_iio_channel_start_all_cb +67 include/linux/export.h

f5016932 Paul Gortmaker  2011-05-23  61  #endif
f5016932 Paul Gortmaker  2011-05-23  62  
f5016932 Paul Gortmaker  2011-05-23  63  /* For every exported symbol, place a struct in the __ksymtab section */
f2355416 Nicolas Pitre   2016-01-22  64  #define ___EXPORT_SYMBOL(sym, sec)					\
f5016932 Paul Gortmaker  2011-05-23  65  	extern typeof(sym) sym;						\
f5016932 Paul Gortmaker  2011-05-23  66  	__CRC_SYMBOL(sym, sec)						\
f5016932 Paul Gortmaker  2011-05-23 @67  	static const char __kstrtab_##sym[]				\
f5016932 Paul Gortmaker  2011-05-23  68  	__attribute__((section("__ksymtab_strings"), aligned(1)))	\
b92021b0 Rusty Russell   2013-03-15  69  	= VMLINUX_SYMBOL_STR(sym);					\
b67067f1 Nicholas Piggin 2016-08-24  70  	static const struct kernel_symbol __ksymtab_##sym		\

:::::: The code at line 67 was first introduced by commit
:::::: f50169324df4ad942e544386d136216c8617636a module.h: split out the EXPORT_SYMBOL into export.h

:::::: TO: Paul Gortmaker <paul.gortmaker@windriver.com>
:::::: CC: Paul Gortmaker <paul.gortmaker@windriver.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: 26427 bytes --]

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

* [alsa-devel] [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-20  6:22       ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: i386-randconfig-x073-201712 (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 error/warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:6:0,
                    from include/linux/kernel.h:6,
                    from drivers/iio/buffer/industrialio-buffer-cb.c:8:
>> include/linux/export.h:67:20: error: redefinition of '__kstrtab_iio_channel_start_all_cb'
     static const char __kstrtab_##sym[]    \
                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
>> drivers/iio/buffer/industrialio-buffer-cb.c:124:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:67:20: note: previous definition of '__kstrtab_iio_channel_start_all_cb' was here
     static const char __kstrtab_##sym[]    \
                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-cb.c:117:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:70:36: error: redefinition of '__ksymtab_iio_channel_start_all_cb'
     static const struct kernel_symbol __ksymtab_##sym  \
                                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
>> drivers/iio/buffer/industrialio-buffer-cb.c:124:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~
   include/linux/export.h:70:36: note: previous definition of '__ksymtab_iio_channel_start_all_cb' was here
     static const struct kernel_symbol __ksymtab_##sym  \
                                       ^
   include/linux/export.h:100:25: note: in expansion of macro '___EXPORT_SYMBOL'
    #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
                            ^~~~~~~~~~~~~~~~
   include/linux/export.h:107:2: note: in expansion of macro '__EXPORT_SYMBOL'
     __EXPORT_SYMBOL(sym, "_gpl")
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-cb.c:117:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
    ^~~~~~~~~~~~~~~~~

vim +/__kstrtab_iio_channel_start_all_cb +67 include/linux/export.h

f5016932 Paul Gortmaker  2011-05-23  61  #endif
f5016932 Paul Gortmaker  2011-05-23  62  
f5016932 Paul Gortmaker  2011-05-23  63  /* For every exported symbol, place a struct in the __ksymtab section */
f2355416 Nicolas Pitre   2016-01-22  64  #define ___EXPORT_SYMBOL(sym, sec)					\
f5016932 Paul Gortmaker  2011-05-23  65  	extern typeof(sym) sym;						\
f5016932 Paul Gortmaker  2011-05-23  66  	__CRC_SYMBOL(sym, sec)						\
f5016932 Paul Gortmaker  2011-05-23 @67  	static const char __kstrtab_##sym[]				\
f5016932 Paul Gortmaker  2011-05-23  68  	__attribute__((section("__ksymtab_strings"), aligned(1)))	\
b92021b0 Rusty Russell   2013-03-15  69  	= VMLINUX_SYMBOL_STR(sym);					\
b67067f1 Nicholas Piggin 2016-08-24  70  	static const struct kernel_symbol __ksymtab_##sym		\

:::::: The code at line 67 was first introduced by commit
:::::: f50169324df4ad942e544386d136216c8617636a module.h: split out the EXPORT_SYMBOL into export.h

:::::: TO: Paul Gortmaker <paul.gortmaker@windriver.com>
:::::: CC: Paul Gortmaker <paul.gortmaker@windriver.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: 26427 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170320/8ddf39b6/attachment-0001.gz>

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

* Re: [alsa-devel] [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-20  6:24     ` kbuild test robot
  -1 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:24 UTC (permalink / raw)
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	olivier moysan, kernel, Liam Girdwood, linux-iio, Mark Brown,
	arnaud.pouliquen, Takashi Iwai, Maxime Coquelin, Jaroslav Kysela,
	Alexandre Torgue, Rob Herring, kbuild-all,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	linux-arm-kernel

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

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: tile-allmodconfig (attached as .config)
compiler: tilegx-linux-gcc (GCC) 4.6.2
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=tile 

All errors (new ones prefixed by >>):

   drivers/iio/adc/sd_adc_modulator.c:84:1: error: 'adc081c_of_match' undeclared here (not in a function)
>> drivers/iio/adc/sd_adc_modulator.c:84:1: error: '__mod_of__adc081c_of_match_device_table' aliased to undefined symbol 'adc081c_of_match'

vim +84 drivers/iio/adc/sd_adc_modulator.c

    78	
    79	static const struct of_device_id sd_adc_of_match[] = {
    80		{ .compatible = "sd-modulator" },
    81		{ .compatible = "ads1201" },
    82		{ }
    83	};
  > 84	MODULE_DEVICE_TABLE(of, adc081c_of_match);
    85	
    86	static struct platform_driver iio_sd_mod_adc = {
    87		.driver = {

---
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: 48060 bytes --]

[-- Attachment #3: Type: text/plain, Size: 176 bytes --]

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

* Re: [alsa-devel] [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
@ 2017-03-20  6:24     ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:24 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: kbuild-all, Rob Herring, Mark Rutland, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

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

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: tile-allmodconfig (attached as .config)
compiler: tilegx-linux-gcc (GCC) 4.6.2
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=tile 

All errors (new ones prefixed by >>):

   drivers/iio/adc/sd_adc_modulator.c:84:1: error: 'adc081c_of_match' undeclared here (not in a function)
>> drivers/iio/adc/sd_adc_modulator.c:84:1: error: '__mod_of__adc081c_of_match_device_table' aliased to undefined symbol 'adc081c_of_match'

vim +84 drivers/iio/adc/sd_adc_modulator.c

    78	
    79	static const struct of_device_id sd_adc_of_match[] = {
    80		{ .compatible = "sd-modulator" },
    81		{ .compatible = "ads1201" },
    82		{ }
    83	};
  > 84	MODULE_DEVICE_TABLE(of, adc081c_of_match);
    85	
    86	static struct platform_driver iio_sd_mod_adc = {
    87		.driver = {

---
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: 48060 bytes --]

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

* [alsa-devel] [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
@ 2017-03-20  6:24     ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:24 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: tile-allmodconfig (attached as .config)
compiler: tilegx-linux-gcc (GCC) 4.6.2
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=tile 

All errors (new ones prefixed by >>):

   drivers/iio/adc/sd_adc_modulator.c:84:1: error: 'adc081c_of_match' undeclared here (not in a function)
>> drivers/iio/adc/sd_adc_modulator.c:84:1: error: '__mod_of__adc081c_of_match_device_table' aliased to undefined symbol 'adc081c_of_match'

vim +84 drivers/iio/adc/sd_adc_modulator.c

    78	
    79	static const struct of_device_id sd_adc_of_match[] = {
    80		{ .compatible = "sd-modulator" },
    81		{ .compatible = "ads1201" },
    82		{ }
    83	};
  > 84	MODULE_DEVICE_TABLE(of, adc081c_of_match);
    85	
    86	static struct platform_driver iio_sd_mod_adc = {
    87		.driver = {

---
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: 48060 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170320/5988d74d/attachment-0001.gz>

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

* Re: [alsa-devel] [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-20  6:51     ` kbuild test robot
  -1 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:51 UTC (permalink / raw)
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	olivier moysan, kernel, Liam Girdwood, linux-iio, Mark Brown,
	arnaud.pouliquen, Takashi Iwai, Maxime Coquelin, Jaroslav Kysela,
	Alexandre Torgue, Rob Herring, kbuild-all,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	linux-arm-kernel

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

Hi Arnaud,

[auto build test WARNING on asoc/for-next]
[also build test WARNING on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: x86_64-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=x86_64 

All warnings (new ones prefixed by >>):

   In file included from drivers/iio/adc/sd_adc_modulator.c:23:0:
   drivers/iio/adc/sd_adc_modulator.c:84:25: error: 'adc081c_of_match' undeclared here (not in a function)
    MODULE_DEVICE_TABLE(of, adc081c_of_match);
                            ^
   include/linux/module.h:212:21: note: in definition of macro 'MODULE_DEVICE_TABLE'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                        ^~~~
   include/linux/module.h:212:27: error: '__mod_of__adc081c_of_match_device_table' aliased to undefined symbol 'adc081c_of_match'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                              ^
>> drivers/iio/adc/sd_adc_modulator.c:84:1: note: in expansion of macro 'MODULE_DEVICE_TABLE'
    MODULE_DEVICE_TABLE(of, adc081c_of_match);
    ^~~~~~~~~~~~~~~~~~~

vim +/MODULE_DEVICE_TABLE +84 drivers/iio/adc/sd_adc_modulator.c

    68		iio->info = &iio_sd_mod_iio_info;
    69		iio->modes = INDIO_BUFFER_HARDWARE;
    70	
    71		iio->num_channels = 1;
    72		iio->channels = &stm32_dfsdm_ch;
    73	
    74		platform_set_drvdata(pdev, iio);
    75	
    76		return devm_iio_device_register(&pdev->dev, iio);
    77	}
    78	
    79	static const struct of_device_id sd_adc_of_match[] = {
    80		{ .compatible = "sd-modulator" },
    81		{ .compatible = "ads1201" },
    82		{ }
    83	};
  > 84	MODULE_DEVICE_TABLE(of, adc081c_of_match);
    85	
    86	static struct platform_driver iio_sd_mod_adc = {
    87		.driver = {
    88			.name = "iio_sd_adc_mod",
    89			.of_match_table = of_match_ptr(sd_adc_of_match),
    90		},
    91		.probe = iio_sd_mod_probe,
    92	};

---
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: 58882 bytes --]

[-- Attachment #3: Type: text/plain, Size: 176 bytes --]

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

* Re: [alsa-devel] [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
@ 2017-03-20  6:51     ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:51 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: kbuild-all, Rob Herring, Mark Rutland, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

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

Hi Arnaud,

[auto build test WARNING on asoc/for-next]
[also build test WARNING on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: x86_64-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=x86_64 

All warnings (new ones prefixed by >>):

   In file included from drivers/iio/adc/sd_adc_modulator.c:23:0:
   drivers/iio/adc/sd_adc_modulator.c:84:25: error: 'adc081c_of_match' undeclared here (not in a function)
    MODULE_DEVICE_TABLE(of, adc081c_of_match);
                            ^
   include/linux/module.h:212:21: note: in definition of macro 'MODULE_DEVICE_TABLE'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                        ^~~~
   include/linux/module.h:212:27: error: '__mod_of__adc081c_of_match_device_table' aliased to undefined symbol 'adc081c_of_match'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                              ^
>> drivers/iio/adc/sd_adc_modulator.c:84:1: note: in expansion of macro 'MODULE_DEVICE_TABLE'
    MODULE_DEVICE_TABLE(of, adc081c_of_match);
    ^~~~~~~~~~~~~~~~~~~

vim +/MODULE_DEVICE_TABLE +84 drivers/iio/adc/sd_adc_modulator.c

    68		iio->info = &iio_sd_mod_iio_info;
    69		iio->modes = INDIO_BUFFER_HARDWARE;
    70	
    71		iio->num_channels = 1;
    72		iio->channels = &stm32_dfsdm_ch;
    73	
    74		platform_set_drvdata(pdev, iio);
    75	
    76		return devm_iio_device_register(&pdev->dev, iio);
    77	}
    78	
    79	static const struct of_device_id sd_adc_of_match[] = {
    80		{ .compatible = "sd-modulator" },
    81		{ .compatible = "ads1201" },
    82		{ }
    83	};
  > 84	MODULE_DEVICE_TABLE(of, adc081c_of_match);
    85	
    86	static struct platform_driver iio_sd_mod_adc = {
    87		.driver = {
    88			.name = "iio_sd_adc_mod",
    89			.of_match_table = of_match_ptr(sd_adc_of_match),
    90		},
    91		.probe = iio_sd_mod_probe,
    92	};

---
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: 58882 bytes --]

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

* [alsa-devel] [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support
@ 2017-03-20  6:51     ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  6:51 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Arnaud,

[auto build test WARNING on asoc/for-next]
[also build test WARNING on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: x86_64-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=x86_64 

All warnings (new ones prefixed by >>):

   In file included from drivers/iio/adc/sd_adc_modulator.c:23:0:
   drivers/iio/adc/sd_adc_modulator.c:84:25: error: 'adc081c_of_match' undeclared here (not in a function)
    MODULE_DEVICE_TABLE(of, adc081c_of_match);
                            ^
   include/linux/module.h:212:21: note: in definition of macro 'MODULE_DEVICE_TABLE'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                        ^~~~
   include/linux/module.h:212:27: error: '__mod_of__adc081c_of_match_device_table' aliased to undefined symbol 'adc081c_of_match'
    extern const typeof(name) __mod_##type##__##name##_device_table  \
                              ^
>> drivers/iio/adc/sd_adc_modulator.c:84:1: note: in expansion of macro 'MODULE_DEVICE_TABLE'
    MODULE_DEVICE_TABLE(of, adc081c_of_match);
    ^~~~~~~~~~~~~~~~~~~

vim +/MODULE_DEVICE_TABLE +84 drivers/iio/adc/sd_adc_modulator.c

    68		iio->info = &iio_sd_mod_iio_info;
    69		iio->modes = INDIO_BUFFER_HARDWARE;
    70	
    71		iio->num_channels = 1;
    72		iio->channels = &stm32_dfsdm_ch;
    73	
    74		platform_set_drvdata(pdev, iio);
    75	
    76		return devm_iio_device_register(&pdev->dev, iio);
    77	}
    78	
    79	static const struct of_device_id sd_adc_of_match[] = {
    80		{ .compatible = "sd-modulator" },
    81		{ .compatible = "ads1201" },
    82		{ }
    83	};
  > 84	MODULE_DEVICE_TABLE(of, adc081c_of_match);
    85	
    86	static struct platform_driver iio_sd_mod_adc = {
    87		.driver = {
    88			.name = "iio_sd_adc_mod",
    89			.of_match_table = of_match_ptr(sd_adc_of_match),
    90		},
    91		.probe = iio_sd_mod_probe,
    92	};

---
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: 58882 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170320/50f8caa9/attachment-0001.gz>

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

* Re: [alsa-devel] [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  2017-03-17 14:08   ` Arnaud Pouliquen
@ 2017-03-20  7:22       ` kbuild test robot
  -1 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  7:22 UTC (permalink / raw)
  Cc: kbuild-all-JC7UmRfGjtg, Rob Herring, Mark Rutland,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, olivier moysan,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	arnaud.pouliquen-qxv4g6HH51o, Maxime Coquelin,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Alexandre Torgue

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

Hi Arnaud,

[auto build test WARNING on asoc/for-next]
[also build test WARNING on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: x86_64-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=x86_64 

All warnings (new ones prefixed by >>):

   drivers/iio/hw_consumer.c:23:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   In file included from include/asm-generic/bug.h:13:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/gfp.h:4,
                    from include/linux/slab.h:14,
                    from drivers/iio/hw_consumer.c:3:
   drivers/iio/hw_consumer.c: In function 'iio_buffer_to_hw_consumer_buffer':
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> drivers/iio/hw_consumer.c:29:9: note: in expansion of macro 'container_of'
     return container_of(buffer, struct hw_consumer_buffer, buffer);
            ^~~~~~~~~~~~
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:21: error: variable 'iio_hw_buf_access' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c:41:2: error: unknown field 'release' specified in initializer
     .release = &iio_hw_buf_release,
     ^
>> drivers/iio/hw_consumer.c:41:13: warning: excess elements in struct initializer
     .release = &iio_hw_buf_release,
                ^
   drivers/iio/hw_consumer.c:41:13: note: (near initialization for 'iio_hw_buf_access')
   drivers/iio/hw_consumer.c:42:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   In file included from drivers/iio/hw_consumer.c:7:0:
>> include/linux/iio/iio.h:353:32: warning: excess elements in struct initializer
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
>> drivers/iio/hw_consumer.c:42:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   include/linux/iio/iio.h:353:32: note: (near initialization for 'iio_hw_buf_access')
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
>> drivers/iio/hw_consumer.c:42:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_get_buffer':
   drivers/iio/hw_consumer.c:66:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&buf->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_alloc':
   drivers/iio/hw_consumer.c:110:3: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
      iio_buffer_put(&buf->buffer);
      ^~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_enable':
   drivers/iio/hw_consumer.c:135:9: error: implicit declaration of function 'iio_update_buffers' [-Werror=implicit-function-declaration]
      ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
            ^~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:45: error: storage size of 'iio_hw_buf_access' isn't known
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                                                ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dma.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_block_release':
   drivers/iio/buffer/industrialio-buffer-dma.c:104:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(&block->queue->buffer);
     ^~~~~~~~~~~~~~
   In file included from include/asm-generic/bug.h:13:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/gfp.h:4,
                    from include/linux/slab.h:14,
                    from drivers/iio/buffer/industrialio-buffer-dma.c:8:
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> drivers/iio/buffer/industrialio-buffer-dma.c:163:9: note: in expansion of macro 'container_of'
     return container_of(buf, struct iio_dma_buffer_queue, buffer);
            ^~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_alloc_block':
   drivers/iio/buffer/industrialio-buffer-dma.c:188:2: error: implicit declaration of function 'iio_buffer_get' [-Werror=implicit-function-declaration]
     iio_buffer_get(&queue->buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_read':
   drivers/iio/buffer/industrialio-buffer-dma.c:489:16: error: dereferencing pointer to incomplete type 'struct iio_buffer'
     if (n < buffer->bytes_per_datum)
                   ^~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_init':
   drivers/iio/buffer/industrialio-buffer-dma.c:614:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&queue->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
>> drivers/iio/buffer/industrialio-buffer-dma.c:164:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dmaengine.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   In file included from include/asm-generic/bug.h:13:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/gfp.h:4,
                    from include/linux/slab.h:14,
                    from drivers/iio/buffer/industrialio-buffer-dmaengine.c:8:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:43:9: note: in expansion of macro 'container_of'
     return container_of(buffer, struct dmaengine_buffer, queue.buffer);
            ^~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:21: error: variable 'iio_dmaengine_buffer_ops' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:2: error: unknown field 'read_first_n' specified in initializer
     .read_first_n = iio_dma_buffer_read,
     ^
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: warning: excess elements in struct initializer
     .read_first_n = iio_dma_buffer_read,
                     ^~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:2: error: unknown field 'set_bytes_per_datum' specified in initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: warning: excess elements in struct initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:2: error: unknown field 'set_length' specified in initializer
     .set_length = iio_dma_buffer_set_length,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: warning: excess elements in struct initializer
     .set_length = iio_dma_buffer_set_length,
                   ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:2: error: unknown field 'request_update' specified in initializer
     .request_update = iio_dma_buffer_request_update,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: warning: excess elements in struct initializer
     .request_update = iio_dma_buffer_request_update,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:2: error: unknown field 'enable' specified in initializer
     .enable = iio_dma_buffer_enable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: warning: excess elements in struct initializer
     .enable = iio_dma_buffer_enable,
               ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:2: error: unknown field 'disable' specified in initializer
     .disable = iio_dma_buffer_disable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: warning: excess elements in struct initializer
     .disable = iio_dma_buffer_disable,
                ^~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:2: error: unknown field 'data_available' specified in initializer
     .data_available = iio_dma_buffer_data_available,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: warning: excess elements in struct initializer
     .data_available = iio_dma_buffer_data_available,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:2: error: unknown field 'release' specified in initializer
     .release = iio_dmaengine_buffer_release,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: warning: excess elements in struct initializer
     .release = iio_dmaengine_buffer_release,
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   In file included from drivers/iio/buffer/industrialio-buffer-dmaengine.c:15:0:
>> include/linux/iio/iio.h:353:32: warning: excess elements in struct initializer
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   include/linux/iio/iio.h:353:32: note: (near initialization for 'iio_dmaengine_buffer_ops')
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:2: error: unknown field 'flags' specified in initializer
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: error: 'INDIO_BUFFER_FLAG_FIXED_WATERMARK' undeclared here (not in a function)
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: warning: excess elements in struct initializer
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_dmaengine_buffer_free':
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:206:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:45: error: storage size of 'iio_dmaengine_buffer_ops' isn't known
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                                                ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:44:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio//buffer/industrialio-buffer-dmaengine.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   In file included from include/asm-generic/bug.h:13:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/gfp.h:4,
                    from include/linux/slab.h:14,
                    from drivers/iio//buffer/industrialio-buffer-dmaengine.c:8:
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:43:9: note: in expansion of macro 'container_of'
     return container_of(buffer, struct dmaengine_buffer, queue.buffer);
            ^~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:109:21: error: variable 'iio_dmaengine_buffer_ops' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:110:2: error: unknown field 'read_first_n' specified in initializer
     .read_first_n = iio_dma_buffer_read,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:110:18: warning: excess elements in struct initializer
     .read_first_n = iio_dma_buffer_read,
                     ^~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:110:18: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:111:2: error: unknown field 'set_bytes_per_datum' specified in initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:111:25: warning: excess elements in struct initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:111:25: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:112:2: error: unknown field 'set_length' specified in initializer
     .set_length = iio_dma_buffer_set_length,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:112:16: warning: excess elements in struct initializer
     .set_length = iio_dma_buffer_set_length,
                   ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:112:16: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:113:2: error: unknown field 'request_update' specified in initializer
     .request_update = iio_dma_buffer_request_update,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:113:20: warning: excess elements in struct initializer
     .request_update = iio_dma_buffer_request_update,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:113:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:114:2: error: unknown field 'enable' specified in initializer
     .enable = iio_dma_buffer_enable,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:114:12: warning: excess elements in struct initializer
     .enable = iio_dma_buffer_enable,
               ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:114:12: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:115:2: error: unknown field 'disable' specified in initializer
     .disable = iio_dma_buffer_disable,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:115:13: warning: excess elements in struct initializer
     .disable = iio_dma_buffer_disable,
                ^~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:115:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:116:2: error: unknown field 'data_available' specified in initializer
     .data_available = iio_dma_buffer_data_available,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:116:20: warning: excess elements in struct initializer
     .data_available = iio_dma_buffer_data_available,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:116:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:117:2: error: unknown field 'release' specified in initializer
     .release = iio_dmaengine_buffer_release,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:117:13: warning: excess elements in struct initializer
     .release = iio_dmaengine_buffer_release,
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:117:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:119:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   In file included from drivers/iio//buffer/industrialio-buffer-dmaengine.c:15:0:
>> include/linux/iio/iio.h:353:32: warning: excess elements in struct initializer
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:119:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   include/linux/iio/iio.h:353:32: note: (near initialization for 'iio_dmaengine_buffer_ops')
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:119:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:120:2: error: unknown field 'flags' specified in initializer
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:120:11: error: 'INDIO_BUFFER_FLAG_FIXED_WATERMARK' undeclared here (not in a function)
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:120:11: warning: excess elements in struct initializer
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:120:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: In function 'iio_dmaengine_buffer_free':
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:206:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(buffer);
     ^~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:109:45: error: storage size of 'iio_dmaengine_buffer_ops' isn't known
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                                                ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:44:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors

vim +/container_of +29 drivers/iio/hw_consumer.c

d5ca88eb Lars-Peter Clausen 2017-03-17   1  #include <linux/err.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   2  #include <linux/export.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  @3  #include <linux/slab.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   4  #include <linux/mutex.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   5  #include <linux/of.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   6  
d5ca88eb Lars-Peter Clausen 2017-03-17   7  #include <linux/iio/iio.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   8  #include "iio_core.h"
d5ca88eb Lars-Peter Clausen 2017-03-17   9  #include <linux/iio/machine.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  10  #include <linux/iio/driver.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  11  #include <linux/iio/consumer.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  12  #include <linux/iio/hw_consumer.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  13  #include <linux/iio/buffer.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  14  
d5ca88eb Lars-Peter Clausen 2017-03-17  15  struct iio_hw_consumer {
d5ca88eb Lars-Peter Clausen 2017-03-17  16  	struct list_head buffers;
d5ca88eb Lars-Peter Clausen 2017-03-17  17  	struct iio_channel *channels;
d5ca88eb Lars-Peter Clausen 2017-03-17  18  };
d5ca88eb Lars-Peter Clausen 2017-03-17  19  
d5ca88eb Lars-Peter Clausen 2017-03-17  20  struct hw_consumer_buffer {
d5ca88eb Lars-Peter Clausen 2017-03-17  21  	struct list_head head;
d5ca88eb Lars-Peter Clausen 2017-03-17  22  	struct iio_dev *indio_dev;
d5ca88eb Lars-Peter Clausen 2017-03-17  23  	struct iio_buffer buffer;
d5ca88eb Lars-Peter Clausen 2017-03-17  24  };
d5ca88eb Lars-Peter Clausen 2017-03-17  25  
d5ca88eb Lars-Peter Clausen 2017-03-17  26  static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
d5ca88eb Lars-Peter Clausen 2017-03-17  27  	struct iio_buffer *buffer)
d5ca88eb Lars-Peter Clausen 2017-03-17  28  {
d5ca88eb Lars-Peter Clausen 2017-03-17 @29  	return container_of(buffer, struct hw_consumer_buffer, buffer);
d5ca88eb Lars-Peter Clausen 2017-03-17  30  }
d5ca88eb Lars-Peter Clausen 2017-03-17  31  
d5ca88eb Lars-Peter Clausen 2017-03-17  32  static void iio_hw_buf_release(struct iio_buffer *buffer)
d5ca88eb Lars-Peter Clausen 2017-03-17  33  {
d5ca88eb Lars-Peter Clausen 2017-03-17  34  	struct hw_consumer_buffer *hw_buf =
d5ca88eb Lars-Peter Clausen 2017-03-17  35  		iio_buffer_to_hw_consumer_buffer(buffer);
d5ca88eb Lars-Peter Clausen 2017-03-17  36  	kfree(hw_buf->buffer.scan_mask);
d5ca88eb Lars-Peter Clausen 2017-03-17  37  	kfree(hw_buf);
d5ca88eb Lars-Peter Clausen 2017-03-17  38  }
d5ca88eb Lars-Peter Clausen 2017-03-17  39  
d5ca88eb Lars-Peter Clausen 2017-03-17 @40  static const struct iio_buffer_access_funcs iio_hw_buf_access = {
d5ca88eb Lars-Peter Clausen 2017-03-17 @41  	.release = &iio_hw_buf_release,
d5ca88eb Lars-Peter Clausen 2017-03-17 @42  	.modes = INDIO_BUFFER_HARDWARE,
d5ca88eb Lars-Peter Clausen 2017-03-17  43  };
d5ca88eb Lars-Peter Clausen 2017-03-17  44  
d5ca88eb Lars-Peter Clausen 2017-03-17  45  static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(

:::::: The code at line 29 was first introduced by commit
:::::: d5ca88ebd75256ba43b57c82dfa9a3cbeb3cacf7 iio: Add hardware consumer support

:::::: TO: Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
:::::: CC: 0day robot <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@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: 58903 bytes --]

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

* Re: [alsa-devel] [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-20  7:22       ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  7:22 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: kbuild-all, Rob Herring, Mark Rutland, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

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

Hi Arnaud,

[auto build test WARNING on asoc/for-next]
[also build test WARNING on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: x86_64-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=x86_64 

All warnings (new ones prefixed by >>):

   drivers/iio/hw_consumer.c:23:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   In file included from include/asm-generic/bug.h:13:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/gfp.h:4,
                    from include/linux/slab.h:14,
                    from drivers/iio/hw_consumer.c:3:
   drivers/iio/hw_consumer.c: In function 'iio_buffer_to_hw_consumer_buffer':
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> drivers/iio/hw_consumer.c:29:9: note: in expansion of macro 'container_of'
     return container_of(buffer, struct hw_consumer_buffer, buffer);
            ^~~~~~~~~~~~
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:21: error: variable 'iio_hw_buf_access' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c:41:2: error: unknown field 'release' specified in initializer
     .release = &iio_hw_buf_release,
     ^
>> drivers/iio/hw_consumer.c:41:13: warning: excess elements in struct initializer
     .release = &iio_hw_buf_release,
                ^
   drivers/iio/hw_consumer.c:41:13: note: (near initialization for 'iio_hw_buf_access')
   drivers/iio/hw_consumer.c:42:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   In file included from drivers/iio/hw_consumer.c:7:0:
>> include/linux/iio/iio.h:353:32: warning: excess elements in struct initializer
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
>> drivers/iio/hw_consumer.c:42:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   include/linux/iio/iio.h:353:32: note: (near initialization for 'iio_hw_buf_access')
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
>> drivers/iio/hw_consumer.c:42:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_get_buffer':
   drivers/iio/hw_consumer.c:66:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&buf->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_alloc':
   drivers/iio/hw_consumer.c:110:3: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
      iio_buffer_put(&buf->buffer);
      ^~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_enable':
   drivers/iio/hw_consumer.c:135:9: error: implicit declaration of function 'iio_update_buffers' [-Werror=implicit-function-declaration]
      ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
            ^~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:45: error: storage size of 'iio_hw_buf_access' isn't known
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                                                ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dma.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_block_release':
   drivers/iio/buffer/industrialio-buffer-dma.c:104:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(&block->queue->buffer);
     ^~~~~~~~~~~~~~
   In file included from include/asm-generic/bug.h:13:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/gfp.h:4,
                    from include/linux/slab.h:14,
                    from drivers/iio/buffer/industrialio-buffer-dma.c:8:
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> drivers/iio/buffer/industrialio-buffer-dma.c:163:9: note: in expansion of macro 'container_of'
     return container_of(buf, struct iio_dma_buffer_queue, buffer);
            ^~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_alloc_block':
   drivers/iio/buffer/industrialio-buffer-dma.c:188:2: error: implicit declaration of function 'iio_buffer_get' [-Werror=implicit-function-declaration]
     iio_buffer_get(&queue->buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_read':
   drivers/iio/buffer/industrialio-buffer-dma.c:489:16: error: dereferencing pointer to incomplete type 'struct iio_buffer'
     if (n < buffer->bytes_per_datum)
                   ^~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_init':
   drivers/iio/buffer/industrialio-buffer-dma.c:614:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&queue->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
>> drivers/iio/buffer/industrialio-buffer-dma.c:164:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dmaengine.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   In file included from include/asm-generic/bug.h:13:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/gfp.h:4,
                    from include/linux/slab.h:14,
                    from drivers/iio/buffer/industrialio-buffer-dmaengine.c:8:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:43:9: note: in expansion of macro 'container_of'
     return container_of(buffer, struct dmaengine_buffer, queue.buffer);
            ^~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:21: error: variable 'iio_dmaengine_buffer_ops' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:2: error: unknown field 'read_first_n' specified in initializer
     .read_first_n = iio_dma_buffer_read,
     ^
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: warning: excess elements in struct initializer
     .read_first_n = iio_dma_buffer_read,
                     ^~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:2: error: unknown field 'set_bytes_per_datum' specified in initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: warning: excess elements in struct initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:2: error: unknown field 'set_length' specified in initializer
     .set_length = iio_dma_buffer_set_length,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: warning: excess elements in struct initializer
     .set_length = iio_dma_buffer_set_length,
                   ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:2: error: unknown field 'request_update' specified in initializer
     .request_update = iio_dma_buffer_request_update,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: warning: excess elements in struct initializer
     .request_update = iio_dma_buffer_request_update,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:2: error: unknown field 'enable' specified in initializer
     .enable = iio_dma_buffer_enable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: warning: excess elements in struct initializer
     .enable = iio_dma_buffer_enable,
               ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:2: error: unknown field 'disable' specified in initializer
     .disable = iio_dma_buffer_disable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: warning: excess elements in struct initializer
     .disable = iio_dma_buffer_disable,
                ^~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:2: error: unknown field 'data_available' specified in initializer
     .data_available = iio_dma_buffer_data_available,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: warning: excess elements in struct initializer
     .data_available = iio_dma_buffer_data_available,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:2: error: unknown field 'release' specified in initializer
     .release = iio_dmaengine_buffer_release,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: warning: excess elements in struct initializer
     .release = iio_dmaengine_buffer_release,
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   In file included from drivers/iio/buffer/industrialio-buffer-dmaengine.c:15:0:
>> include/linux/iio/iio.h:353:32: warning: excess elements in struct initializer
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   include/linux/iio/iio.h:353:32: note: (near initialization for 'iio_dmaengine_buffer_ops')
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:2: error: unknown field 'flags' specified in initializer
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: error: 'INDIO_BUFFER_FLAG_FIXED_WATERMARK' undeclared here (not in a function)
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: warning: excess elements in struct initializer
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_dmaengine_buffer_free':
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:206:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:45: error: storage size of 'iio_dmaengine_buffer_ops' isn't known
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                                                ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:44:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio//buffer/industrialio-buffer-dmaengine.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   In file included from include/asm-generic/bug.h:13:0,
                    from arch/x86/include/asm/bug.h:35,
                    from include/linux/bug.h:4,
                    from include/linux/mmdebug.h:4,
                    from include/linux/gfp.h:4,
                    from include/linux/slab.h:14,
                    from drivers/iio//buffer/industrialio-buffer-dmaengine.c:8:
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   include/linux/kernel.h:852:48: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:43:9: note: in expansion of macro 'container_of'
     return container_of(buffer, struct dmaengine_buffer, queue.buffer);
            ^~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:109:21: error: variable 'iio_dmaengine_buffer_ops' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:110:2: error: unknown field 'read_first_n' specified in initializer
     .read_first_n = iio_dma_buffer_read,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:110:18: warning: excess elements in struct initializer
     .read_first_n = iio_dma_buffer_read,
                     ^~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:110:18: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:111:2: error: unknown field 'set_bytes_per_datum' specified in initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:111:25: warning: excess elements in struct initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:111:25: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:112:2: error: unknown field 'set_length' specified in initializer
     .set_length = iio_dma_buffer_set_length,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:112:16: warning: excess elements in struct initializer
     .set_length = iio_dma_buffer_set_length,
                   ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:112:16: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:113:2: error: unknown field 'request_update' specified in initializer
     .request_update = iio_dma_buffer_request_update,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:113:20: warning: excess elements in struct initializer
     .request_update = iio_dma_buffer_request_update,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:113:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:114:2: error: unknown field 'enable' specified in initializer
     .enable = iio_dma_buffer_enable,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:114:12: warning: excess elements in struct initializer
     .enable = iio_dma_buffer_enable,
               ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:114:12: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:115:2: error: unknown field 'disable' specified in initializer
     .disable = iio_dma_buffer_disable,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:115:13: warning: excess elements in struct initializer
     .disable = iio_dma_buffer_disable,
                ^~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:115:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:116:2: error: unknown field 'data_available' specified in initializer
     .data_available = iio_dma_buffer_data_available,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:116:20: warning: excess elements in struct initializer
     .data_available = iio_dma_buffer_data_available,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:116:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:117:2: error: unknown field 'release' specified in initializer
     .release = iio_dmaengine_buffer_release,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:117:13: warning: excess elements in struct initializer
     .release = iio_dmaengine_buffer_release,
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:117:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:119:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   In file included from drivers/iio//buffer/industrialio-buffer-dmaengine.c:15:0:
>> include/linux/iio/iio.h:353:32: warning: excess elements in struct initializer
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:119:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   include/linux/iio/iio.h:353:32: note: (near initialization for 'iio_dmaengine_buffer_ops')
    #define INDIO_BUFFER_HARDWARE  0x08
                                   ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:119:11: note: in expansion of macro 'INDIO_BUFFER_HARDWARE'
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:120:2: error: unknown field 'flags' specified in initializer
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
     ^
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:120:11: error: 'INDIO_BUFFER_FLAG_FIXED_WATERMARK' undeclared here (not in a function)
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:120:11: warning: excess elements in struct initializer
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:120:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: In function 'iio_dmaengine_buffer_free':
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:206:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(buffer);
     ^~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:109:45: error: storage size of 'iio_dmaengine_buffer_ops' isn't known
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                                                ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio//buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   drivers/iio//buffer/industrialio-buffer-dmaengine.c:44:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors

vim +/container_of +29 drivers/iio/hw_consumer.c

d5ca88eb Lars-Peter Clausen 2017-03-17   1  #include <linux/err.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   2  #include <linux/export.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  @3  #include <linux/slab.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   4  #include <linux/mutex.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   5  #include <linux/of.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   6  
d5ca88eb Lars-Peter Clausen 2017-03-17   7  #include <linux/iio/iio.h>
d5ca88eb Lars-Peter Clausen 2017-03-17   8  #include "iio_core.h"
d5ca88eb Lars-Peter Clausen 2017-03-17   9  #include <linux/iio/machine.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  10  #include <linux/iio/driver.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  11  #include <linux/iio/consumer.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  12  #include <linux/iio/hw_consumer.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  13  #include <linux/iio/buffer.h>
d5ca88eb Lars-Peter Clausen 2017-03-17  14  
d5ca88eb Lars-Peter Clausen 2017-03-17  15  struct iio_hw_consumer {
d5ca88eb Lars-Peter Clausen 2017-03-17  16  	struct list_head buffers;
d5ca88eb Lars-Peter Clausen 2017-03-17  17  	struct iio_channel *channels;
d5ca88eb Lars-Peter Clausen 2017-03-17  18  };
d5ca88eb Lars-Peter Clausen 2017-03-17  19  
d5ca88eb Lars-Peter Clausen 2017-03-17  20  struct hw_consumer_buffer {
d5ca88eb Lars-Peter Clausen 2017-03-17  21  	struct list_head head;
d5ca88eb Lars-Peter Clausen 2017-03-17  22  	struct iio_dev *indio_dev;
d5ca88eb Lars-Peter Clausen 2017-03-17  23  	struct iio_buffer buffer;
d5ca88eb Lars-Peter Clausen 2017-03-17  24  };
d5ca88eb Lars-Peter Clausen 2017-03-17  25  
d5ca88eb Lars-Peter Clausen 2017-03-17  26  static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
d5ca88eb Lars-Peter Clausen 2017-03-17  27  	struct iio_buffer *buffer)
d5ca88eb Lars-Peter Clausen 2017-03-17  28  {
d5ca88eb Lars-Peter Clausen 2017-03-17 @29  	return container_of(buffer, struct hw_consumer_buffer, buffer);
d5ca88eb Lars-Peter Clausen 2017-03-17  30  }
d5ca88eb Lars-Peter Clausen 2017-03-17  31  
d5ca88eb Lars-Peter Clausen 2017-03-17  32  static void iio_hw_buf_release(struct iio_buffer *buffer)
d5ca88eb Lars-Peter Clausen 2017-03-17  33  {
d5ca88eb Lars-Peter Clausen 2017-03-17  34  	struct hw_consumer_buffer *hw_buf =
d5ca88eb Lars-Peter Clausen 2017-03-17  35  		iio_buffer_to_hw_consumer_buffer(buffer);
d5ca88eb Lars-Peter Clausen 2017-03-17  36  	kfree(hw_buf->buffer.scan_mask);
d5ca88eb Lars-Peter Clausen 2017-03-17  37  	kfree(hw_buf);
d5ca88eb Lars-Peter Clausen 2017-03-17  38  }
d5ca88eb Lars-Peter Clausen 2017-03-17  39  
d5ca88eb Lars-Peter Clausen 2017-03-17 @40  static const struct iio_buffer_access_funcs iio_hw_buf_access = {
d5ca88eb Lars-Peter Clausen 2017-03-17 @41  	.release = &iio_hw_buf_release,
d5ca88eb Lars-Peter Clausen 2017-03-17 @42  	.modes = INDIO_BUFFER_HARDWARE,
d5ca88eb Lars-Peter Clausen 2017-03-17  43  };
d5ca88eb Lars-Peter Clausen 2017-03-17  44  
d5ca88eb Lars-Peter Clausen 2017-03-17  45  static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(

:::::: The code at line 29 was first introduced by commit
:::::: d5ca88ebd75256ba43b57c82dfa9a3cbeb3cacf7 iio: Add hardware consumer support

:::::: TO: Lars-Peter Clausen <lars@metafoo.de>
:::::: CC: 0day robot <fengguang.wu@intel.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: 58903 bytes --]

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

* Re: [PATCH v3 01/11] iio: Add hardware consumer support
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
  (?)
@ 2017-03-20  7:48   ` Peter Meerwald-Stadler
  -1 siblings, 0 replies; 109+ messages in thread
From: Peter Meerwald-Stadler @ 2017-03-20  7:48 UTC (permalink / raw)
  To: Arnaud Pouliquen; +Cc: Jonathan Cameron, Lars-Peter Clausen, linux-iio


> Hardware consumer's can be used when one IIO device has a direct connection
> to another device in hardware.

consumers

> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2 -> V3:
> 	 Just Adding sign off.  No update but Jonathan comment.
>          Need to be taken into account (in agreement with Lars)
>          when transforming rfc in patch.
> ---
>  drivers/iio/Kconfig             |   6 ++
>  drivers/iio/Makefile            |   1 +
>  drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/iio/hw_consumer.h |  12 ++++
>  4 files changed, 175 insertions(+)
>  create mode 100644 drivers/iio/hw_consumer.c
>  create mode 100644 include/linux/iio/hw_consumer.h
> 
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 6743b18..956dd18 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -30,6 +30,12 @@ config IIO_CONFIGFS
>  	  (e.g. software triggers). For more info see
>  	  Documentation/iio/iio_configfs.txt.
>  
> +config IIO_HW_CONSUMER
> +	tristate
> +	help
> +	  Hardware consumer buffer
> +

drop extra newline

> +
>  config IIO_TRIGGER
>  	bool "Enable triggered sampling support"
>  	help
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 87e4c43..8472b97 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
> +obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o

consistency? there is none... (naming, order)
  
>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>  obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
> diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
> new file mode 100644
> index 0000000..66f0732
> --- /dev/null
> +++ b/drivers/iio/hw_consumer.c
> @@ -0,0 +1,156 @@
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +
> +#include <linux/iio/iio.h>
> +#include "iio_core.h"
> +#include <linux/iio/machine.h>
> +#include <linux/iio/driver.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +struct iio_hw_consumer {
> +	struct list_head buffers;
> +	struct iio_channel *channels;
> +};
> +
> +struct hw_consumer_buffer {
> +	struct list_head head;
> +	struct iio_dev *indio_dev;
> +	struct iio_buffer buffer;
> +};
> +
> +static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
> +	struct iio_buffer *buffer)
> +{
> +	return container_of(buffer, struct hw_consumer_buffer, buffer);
> +}
> +
> +static void iio_hw_buf_release(struct iio_buffer *buffer)
> +{
> +	struct hw_consumer_buffer *hw_buf =
> +		iio_buffer_to_hw_consumer_buffer(buffer);
> +	kfree(hw_buf->buffer.scan_mask);
> +	kfree(hw_buf);
> +}
> +
> +static const struct iio_buffer_access_funcs iio_hw_buf_access = {
> +	.release = &iio_hw_buf_release,
> +	.modes = INDIO_BUFFER_HARDWARE,
> +};
> +
> +static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
> +	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head) {
> +		if (buf->indio_dev == indio_dev)
> +			return buf;
> +	}
> +
> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
> +	if (!buf)
> +		return NULL;
> +
> +	buf->buffer.access = &iio_hw_buf_access;
> +	buf->indio_dev = indio_dev;
> +	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
> +		sizeof(long), GFP_KERNEL);
> +	if (!buf->buffer.scan_mask)
> +		goto err_free_buf;
> +
> +	iio_buffer_init(&buf->buffer);
> +	list_add_tail(&buf->head, &hwc->buffers);
> +
> +	return buf;
> +
> +err_free_buf:
> +	kfree(buf);
> +	return NULL;
> +}
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
> +{
> +	struct hw_consumer_buffer *buf;
> +	struct iio_hw_consumer *hwc;
> +	struct iio_channel *chan;
> +	int ret;
> +
> +	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
> +	if (!hwc)
> +		return ERR_PTR(-ENOMEM);
> +
> +	INIT_LIST_HEAD(&hwc->buffers);
> +
> +	hwc->channels = iio_channel_get_all(dev);
> +	if (IS_ERR(hwc->channels)) {
> +		ret = PTR_ERR(hwc->channels);
> +		goto err_free_hwc;
> +	}
> +
> +	chan = &hwc->channels[0];
> +	while (chan->indio_dev) {
> +		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
> +		if (buf == NULL) {
> +			ret = -ENOMEM;
> +			goto err_put_buffers;
> +		}
> +		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
> +		chan++;
> +	}
> +
> +	return hwc;
> +
> +err_put_buffers:
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_buffer_put(&buf->buffer);
> +	iio_channel_release_all(hwc->channels);
> +err_free_hwc:
> +	kfree(hwc);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
> +
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	iio_channel_release_all(hwc->channels);
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_buffer_put(&buf->buffer);
> +	kfree(hwc);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
> +
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +	int ret;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head) {
> +		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
> +		if (ret)
> +			goto err_disable_buffers;
> +	}
> +
> +	return 0;
> +
> +err_disable_buffers:
> +	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
> +
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
> diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
> new file mode 100644
> index 0000000..f12653d
> --- /dev/null
> +++ b/include/linux/iio/hw_consumer.h
> @@ -0,0 +1,12 @@
> +#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
> +#define LINUX_IIO_HW_CONSUMER_BUFFER_H
> +
> +struct device;
> +struct iio_hw_consumer;
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
> +
> +#endif
> 

-- 

Peter Meerwald-Stadler
Mobile: +43 664 24 44 418

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

* Re: [alsa-devel] [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-20  8:04       ` kbuild test robot
  -1 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  8:04 UTC (permalink / raw)
  Cc: kbuild-all-JC7UmRfGjtg, Rob Herring, Mark Rutland,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, olivier moysan,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	arnaud.pouliquen-qxv4g6HH51o, Maxime Coquelin,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Alexandre Torgue

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

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: i386-allyesconfig (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 errors (new ones prefixed by >>):

   drivers/iio/hw_consumer.c:23:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_buffer_to_hw_consumer_buffer':
>> drivers/iio/hw_consumer.c:29:79: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buffer, struct hw_consumer_buffer, buffer);
                                                                                  ^
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:21: error: variable 'iio_hw_buf_access' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c:41:2: error: unknown field 'release' specified in initializer
     .release = &iio_hw_buf_release,
     ^
   drivers/iio/hw_consumer.c:41:13: warning: excess elements in struct initializer
     .release = &iio_hw_buf_release,
                ^
   drivers/iio/hw_consumer.c:41:13: note: (near initialization for 'iio_hw_buf_access')
   drivers/iio/hw_consumer.c:42:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   drivers/iio/hw_consumer.c:42:11: warning: excess elements in struct initializer
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~
   drivers/iio/hw_consumer.c:42:11: note: (near initialization for 'iio_hw_buf_access')
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_get_buffer':
   drivers/iio/hw_consumer.c:66:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&buf->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_alloc':
   drivers/iio/hw_consumer.c:110:3: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
      iio_buffer_put(&buf->buffer);
      ^~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_enable':
   drivers/iio/hw_consumer.c:135:9: error: implicit declaration of function 'iio_update_buffers' [-Werror=implicit-function-declaration]
      ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
            ^~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:45: error: storage size of 'iio_hw_buf_access' isn't known
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                                                ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dma.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_block_release':
   drivers/iio/buffer/industrialio-buffer-dma.c:104:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(&block->queue->buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
>> drivers/iio/buffer/industrialio-buffer-dma.c:163:81: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buf, struct iio_dma_buffer_queue, buffer);
                                                                                    ^
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_alloc_block':
   drivers/iio/buffer/industrialio-buffer-dma.c:188:2: error: implicit declaration of function 'iio_buffer_get' [-Werror=implicit-function-declaration]
     iio_buffer_get(&queue->buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_read':
   drivers/iio/buffer/industrialio-buffer-dma.c:489:16: error: dereferencing pointer to incomplete type 'struct iio_buffer'
     if (n < buffer->bytes_per_datum)
                   ^~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_init':
   drivers/iio/buffer/industrialio-buffer-dma.c:614:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&queue->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
   drivers/iio/buffer/industrialio-buffer-dma.c:164:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dmaengine.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:43:83: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buffer, struct dmaengine_buffer, queue.buffer);
                                                                                      ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:21: error: variable 'iio_dmaengine_buffer_ops' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:2: error: unknown field 'read_first_n' specified in initializer
     .read_first_n = iio_dma_buffer_read,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: warning: excess elements in struct initializer
     .read_first_n = iio_dma_buffer_read,
                     ^~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:2: error: unknown field 'set_bytes_per_datum' specified in initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: warning: excess elements in struct initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:2: error: unknown field 'set_length' specified in initializer
     .set_length = iio_dma_buffer_set_length,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: warning: excess elements in struct initializer
     .set_length = iio_dma_buffer_set_length,
                   ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:2: error: unknown field 'request_update' specified in initializer
     .request_update = iio_dma_buffer_request_update,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: warning: excess elements in struct initializer
     .request_update = iio_dma_buffer_request_update,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:2: error: unknown field 'enable' specified in initializer
     .enable = iio_dma_buffer_enable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: warning: excess elements in struct initializer
     .enable = iio_dma_buffer_enable,
               ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:2: error: unknown field 'disable' specified in initializer
     .disable = iio_dma_buffer_disable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: warning: excess elements in struct initializer
     .disable = iio_dma_buffer_disable,
                ^~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:2: error: unknown field 'data_available' specified in initializer
     .data_available = iio_dma_buffer_data_available,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: warning: excess elements in struct initializer
     .data_available = iio_dma_buffer_data_available,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:2: error: unknown field 'release' specified in initializer
     .release = iio_dmaengine_buffer_release,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: warning: excess elements in struct initializer
     .release = iio_dmaengine_buffer_release,
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: warning: excess elements in struct initializer
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:2: error: unknown field 'flags' specified in initializer
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: error: 'INDIO_BUFFER_FLAG_FIXED_WATERMARK' undeclared here (not in a function)
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: warning: excess elements in struct initializer
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_dmaengine_buffer_free':
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:206:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:45: error: storage size of 'iio_dmaengine_buffer_ops' isn't known
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                                                ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:44:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors

vim +29 drivers/iio/hw_consumer.c

d5ca88eb Lars-Peter Clausen 2017-03-17  17  	struct iio_channel *channels;
d5ca88eb Lars-Peter Clausen 2017-03-17  18  };
d5ca88eb Lars-Peter Clausen 2017-03-17  19  
d5ca88eb Lars-Peter Clausen 2017-03-17  20  struct hw_consumer_buffer {
d5ca88eb Lars-Peter Clausen 2017-03-17  21  	struct list_head head;
d5ca88eb Lars-Peter Clausen 2017-03-17  22  	struct iio_dev *indio_dev;
d5ca88eb Lars-Peter Clausen 2017-03-17 @23  	struct iio_buffer buffer;
d5ca88eb Lars-Peter Clausen 2017-03-17  24  };
d5ca88eb Lars-Peter Clausen 2017-03-17  25  
d5ca88eb Lars-Peter Clausen 2017-03-17  26  static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
d5ca88eb Lars-Peter Clausen 2017-03-17  27  	struct iio_buffer *buffer)
d5ca88eb Lars-Peter Clausen 2017-03-17  28  {
d5ca88eb Lars-Peter Clausen 2017-03-17 @29  	return container_of(buffer, struct hw_consumer_buffer, buffer);
d5ca88eb Lars-Peter Clausen 2017-03-17  30  }
d5ca88eb Lars-Peter Clausen 2017-03-17  31  
d5ca88eb Lars-Peter Clausen 2017-03-17  32  static void iio_hw_buf_release(struct iio_buffer *buffer)

:::::: The code at line 29 was first introduced by commit
:::::: d5ca88ebd75256ba43b57c82dfa9a3cbeb3cacf7 iio: Add hardware consumer support

:::::: TO: Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
:::::: CC: 0day robot <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@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: 58385 bytes --]

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

* Re: [alsa-devel] [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-20  8:04       ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  8:04 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: kbuild-all, Rob Herring, Mark Rutland, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	devicetree, alsa-devel, olivier moysan, kernel, linux-iio,
	arnaud.pouliquen, Maxime Coquelin, linux-arm-kernel,
	Alexandre Torgue

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

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: i386-allyesconfig (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 errors (new ones prefixed by >>):

   drivers/iio/hw_consumer.c:23:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_buffer_to_hw_consumer_buffer':
>> drivers/iio/hw_consumer.c:29:79: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buffer, struct hw_consumer_buffer, buffer);
                                                                                  ^
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:21: error: variable 'iio_hw_buf_access' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c:41:2: error: unknown field 'release' specified in initializer
     .release = &iio_hw_buf_release,
     ^
   drivers/iio/hw_consumer.c:41:13: warning: excess elements in struct initializer
     .release = &iio_hw_buf_release,
                ^
   drivers/iio/hw_consumer.c:41:13: note: (near initialization for 'iio_hw_buf_access')
   drivers/iio/hw_consumer.c:42:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   drivers/iio/hw_consumer.c:42:11: warning: excess elements in struct initializer
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~
   drivers/iio/hw_consumer.c:42:11: note: (near initialization for 'iio_hw_buf_access')
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_get_buffer':
   drivers/iio/hw_consumer.c:66:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&buf->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_alloc':
   drivers/iio/hw_consumer.c:110:3: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
      iio_buffer_put(&buf->buffer);
      ^~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_enable':
   drivers/iio/hw_consumer.c:135:9: error: implicit declaration of function 'iio_update_buffers' [-Werror=implicit-function-declaration]
      ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
            ^~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:45: error: storage size of 'iio_hw_buf_access' isn't known
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                                                ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dma.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_block_release':
   drivers/iio/buffer/industrialio-buffer-dma.c:104:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(&block->queue->buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
>> drivers/iio/buffer/industrialio-buffer-dma.c:163:81: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buf, struct iio_dma_buffer_queue, buffer);
                                                                                    ^
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_alloc_block':
   drivers/iio/buffer/industrialio-buffer-dma.c:188:2: error: implicit declaration of function 'iio_buffer_get' [-Werror=implicit-function-declaration]
     iio_buffer_get(&queue->buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_read':
   drivers/iio/buffer/industrialio-buffer-dma.c:489:16: error: dereferencing pointer to incomplete type 'struct iio_buffer'
     if (n < buffer->bytes_per_datum)
                   ^~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_init':
   drivers/iio/buffer/industrialio-buffer-dma.c:614:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&queue->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
   drivers/iio/buffer/industrialio-buffer-dma.c:164:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dmaengine.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:43:83: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buffer, struct dmaengine_buffer, queue.buffer);
                                                                                      ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:21: error: variable 'iio_dmaengine_buffer_ops' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:2: error: unknown field 'read_first_n' specified in initializer
     .read_first_n = iio_dma_buffer_read,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: warning: excess elements in struct initializer
     .read_first_n = iio_dma_buffer_read,
                     ^~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:2: error: unknown field 'set_bytes_per_datum' specified in initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: warning: excess elements in struct initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:2: error: unknown field 'set_length' specified in initializer
     .set_length = iio_dma_buffer_set_length,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: warning: excess elements in struct initializer
     .set_length = iio_dma_buffer_set_length,
                   ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:2: error: unknown field 'request_update' specified in initializer
     .request_update = iio_dma_buffer_request_update,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: warning: excess elements in struct initializer
     .request_update = iio_dma_buffer_request_update,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:2: error: unknown field 'enable' specified in initializer
     .enable = iio_dma_buffer_enable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: warning: excess elements in struct initializer
     .enable = iio_dma_buffer_enable,
               ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:2: error: unknown field 'disable' specified in initializer
     .disable = iio_dma_buffer_disable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: warning: excess elements in struct initializer
     .disable = iio_dma_buffer_disable,
                ^~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:2: error: unknown field 'data_available' specified in initializer
     .data_available = iio_dma_buffer_data_available,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: warning: excess elements in struct initializer
     .data_available = iio_dma_buffer_data_available,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:2: error: unknown field 'release' specified in initializer
     .release = iio_dmaengine_buffer_release,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: warning: excess elements in struct initializer
     .release = iio_dmaengine_buffer_release,
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: warning: excess elements in struct initializer
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:2: error: unknown field 'flags' specified in initializer
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: error: 'INDIO_BUFFER_FLAG_FIXED_WATERMARK' undeclared here (not in a function)
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: warning: excess elements in struct initializer
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_dmaengine_buffer_free':
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:206:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:45: error: storage size of 'iio_dmaengine_buffer_ops' isn't known
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                                                ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:44:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors

vim +29 drivers/iio/hw_consumer.c

d5ca88eb Lars-Peter Clausen 2017-03-17  17  	struct iio_channel *channels;
d5ca88eb Lars-Peter Clausen 2017-03-17  18  };
d5ca88eb Lars-Peter Clausen 2017-03-17  19  
d5ca88eb Lars-Peter Clausen 2017-03-17  20  struct hw_consumer_buffer {
d5ca88eb Lars-Peter Clausen 2017-03-17  21  	struct list_head head;
d5ca88eb Lars-Peter Clausen 2017-03-17  22  	struct iio_dev *indio_dev;
d5ca88eb Lars-Peter Clausen 2017-03-17 @23  	struct iio_buffer buffer;
d5ca88eb Lars-Peter Clausen 2017-03-17  24  };
d5ca88eb Lars-Peter Clausen 2017-03-17  25  
d5ca88eb Lars-Peter Clausen 2017-03-17  26  static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
d5ca88eb Lars-Peter Clausen 2017-03-17  27  	struct iio_buffer *buffer)
d5ca88eb Lars-Peter Clausen 2017-03-17  28  {
d5ca88eb Lars-Peter Clausen 2017-03-17 @29  	return container_of(buffer, struct hw_consumer_buffer, buffer);
d5ca88eb Lars-Peter Clausen 2017-03-17  30  }
d5ca88eb Lars-Peter Clausen 2017-03-17  31  
d5ca88eb Lars-Peter Clausen 2017-03-17  32  static void iio_hw_buf_release(struct iio_buffer *buffer)

:::::: The code at line 29 was first introduced by commit
:::::: d5ca88ebd75256ba43b57c82dfa9a3cbeb3cacf7 iio: Add hardware consumer support

:::::: TO: Lars-Peter Clausen <lars@metafoo.de>
:::::: CC: 0day robot <fengguang.wu@intel.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: 58385 bytes --]

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

* [alsa-devel] [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-20  8:04       ` kbuild test robot
  0 siblings, 0 replies; 109+ messages in thread
From: kbuild test robot @ 2017-03-20  8:04 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Arnaud,

[auto build test ERROR on asoc/for-next]
[also build test ERROR on v4.11-rc3]
[cannot apply to iio/togreg next-20170310]
[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/20170320-133247
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: i386-allyesconfig (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 errors (new ones prefixed by >>):

   drivers/iio/hw_consumer.c:23:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_buffer_to_hw_consumer_buffer':
>> drivers/iio/hw_consumer.c:29:79: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buffer, struct hw_consumer_buffer, buffer);
                                                                                  ^
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:21: error: variable 'iio_hw_buf_access' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c:41:2: error: unknown field 'release' specified in initializer
     .release = &iio_hw_buf_release,
     ^
   drivers/iio/hw_consumer.c:41:13: warning: excess elements in struct initializer
     .release = &iio_hw_buf_release,
                ^
   drivers/iio/hw_consumer.c:41:13: note: (near initialization for 'iio_hw_buf_access')
   drivers/iio/hw_consumer.c:42:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   drivers/iio/hw_consumer.c:42:11: warning: excess elements in struct initializer
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~
   drivers/iio/hw_consumer.c:42:11: note: (near initialization for 'iio_hw_buf_access')
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_get_buffer':
   drivers/iio/hw_consumer.c:66:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&buf->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_alloc':
   drivers/iio/hw_consumer.c:110:3: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
      iio_buffer_put(&buf->buffer);
      ^~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: In function 'iio_hw_consumer_enable':
   drivers/iio/hw_consumer.c:135:9: error: implicit declaration of function 'iio_update_buffers' [-Werror=implicit-function-declaration]
      ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
            ^~~~~~~~~~~~~~~~~~
   drivers/iio/hw_consumer.c: At top level:
   drivers/iio/hw_consumer.c:40:45: error: storage size of 'iio_hw_buf_access' isn't known
    static const struct iio_buffer_access_funcs iio_hw_buf_access = {
                                                ^~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dma.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_block_release':
   drivers/iio/buffer/industrialio-buffer-dma.c:104:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(&block->queue->buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
>> drivers/iio/buffer/industrialio-buffer-dma.c:163:81: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buf, struct iio_dma_buffer_queue, buffer);
                                                                                    ^
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_alloc_block':
   drivers/iio/buffer/industrialio-buffer-dma.c:188:2: error: implicit declaration of function 'iio_buffer_get' [-Werror=implicit-function-declaration]
     iio_buffer_get(&queue->buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_read':
   drivers/iio/buffer/industrialio-buffer-dma.c:489:16: error: dereferencing pointer to incomplete type 'struct iio_buffer'
     if (n < buffer->bytes_per_datum)
                   ^~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_dma_buffer_init':
   drivers/iio/buffer/industrialio-buffer-dma.c:614:2: error: implicit declaration of function 'iio_buffer_init' [-Werror=implicit-function-declaration]
     iio_buffer_init(&queue->buffer);
     ^~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dma.c: In function 'iio_buffer_to_queue':
   drivers/iio/buffer/industrialio-buffer-dma.c:164:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors
--
   In file included from drivers/iio/buffer/industrialio-buffer-dmaengine.c:17:0:
   include/linux/iio/buffer-dma.h:107:20: error: field 'buffer' has incomplete type
     struct iio_buffer buffer;
                       ^~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
>> drivers/iio/buffer/industrialio-buffer-dmaengine.c:43:83: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     return container_of(buffer, struct dmaengine_buffer, queue.buffer);
                                                                                      ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:21: error: variable 'iio_dmaengine_buffer_ops' has initializer but incomplete type
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                        ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:2: error: unknown field 'read_first_n' specified in initializer
     .read_first_n = iio_dma_buffer_read,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: warning: excess elements in struct initializer
     .read_first_n = iio_dma_buffer_read,
                     ^~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:110:18: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:2: error: unknown field 'set_bytes_per_datum' specified in initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: warning: excess elements in struct initializer
     .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:111:25: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:2: error: unknown field 'set_length' specified in initializer
     .set_length = iio_dma_buffer_set_length,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: warning: excess elements in struct initializer
     .set_length = iio_dma_buffer_set_length,
                   ^~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:112:16: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:2: error: unknown field 'request_update' specified in initializer
     .request_update = iio_dma_buffer_request_update,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: warning: excess elements in struct initializer
     .request_update = iio_dma_buffer_request_update,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:113:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:2: error: unknown field 'enable' specified in initializer
     .enable = iio_dma_buffer_enable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: warning: excess elements in struct initializer
     .enable = iio_dma_buffer_enable,
               ^~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:114:12: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:2: error: unknown field 'disable' specified in initializer
     .disable = iio_dma_buffer_disable,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: warning: excess elements in struct initializer
     .disable = iio_dma_buffer_disable,
                ^~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:115:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:2: error: unknown field 'data_available' specified in initializer
     .data_available = iio_dma_buffer_data_available,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: warning: excess elements in struct initializer
     .data_available = iio_dma_buffer_data_available,
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:116:20: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:2: error: unknown field 'release' specified in initializer
     .release = iio_dmaengine_buffer_release,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: warning: excess elements in struct initializer
     .release = iio_dmaengine_buffer_release,
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:117:13: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:2: error: unknown field 'modes' specified in initializer
     .modes = INDIO_BUFFER_HARDWARE,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: warning: excess elements in struct initializer
     .modes = INDIO_BUFFER_HARDWARE,
              ^~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:119:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:2: error: unknown field 'flags' specified in initializer
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
     ^
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: error: 'INDIO_BUFFER_FLAG_FIXED_WATERMARK' undeclared here (not in a function)
     .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: warning: excess elements in struct initializer
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:120:11: note: (near initialization for 'iio_dmaengine_buffer_ops')
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_dmaengine_buffer_free':
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:206:2: error: implicit declaration of function 'iio_buffer_put' [-Werror=implicit-function-declaration]
     iio_buffer_put(buffer);
     ^~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: At top level:
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:109:45: error: storage size of 'iio_dmaengine_buffer_ops' isn't known
    static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
                                                ^~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/buffer/industrialio-buffer-dmaengine.c: In function 'iio_buffer_to_dmaengine_buffer':
   drivers/iio/buffer/industrialio-buffer-dmaengine.c:44:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   cc1: some warnings being treated as errors

vim +29 drivers/iio/hw_consumer.c

d5ca88eb Lars-Peter Clausen 2017-03-17  17  	struct iio_channel *channels;
d5ca88eb Lars-Peter Clausen 2017-03-17  18  };
d5ca88eb Lars-Peter Clausen 2017-03-17  19  
d5ca88eb Lars-Peter Clausen 2017-03-17  20  struct hw_consumer_buffer {
d5ca88eb Lars-Peter Clausen 2017-03-17  21  	struct list_head head;
d5ca88eb Lars-Peter Clausen 2017-03-17  22  	struct iio_dev *indio_dev;
d5ca88eb Lars-Peter Clausen 2017-03-17 @23  	struct iio_buffer buffer;
d5ca88eb Lars-Peter Clausen 2017-03-17  24  };
d5ca88eb Lars-Peter Clausen 2017-03-17  25  
d5ca88eb Lars-Peter Clausen 2017-03-17  26  static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
d5ca88eb Lars-Peter Clausen 2017-03-17  27  	struct iio_buffer *buffer)
d5ca88eb Lars-Peter Clausen 2017-03-17  28  {
d5ca88eb Lars-Peter Clausen 2017-03-17 @29  	return container_of(buffer, struct hw_consumer_buffer, buffer);
d5ca88eb Lars-Peter Clausen 2017-03-17  30  }
d5ca88eb Lars-Peter Clausen 2017-03-17  31  
d5ca88eb Lars-Peter Clausen 2017-03-17  32  static void iio_hw_buf_release(struct iio_buffer *buffer)

:::::: The code at line 29 was first introduced by commit
:::::: d5ca88ebd75256ba43b57c82dfa9a3cbeb3cacf7 iio: Add hardware consumer support

:::::: TO: Lars-Peter Clausen <lars@metafoo.de>
:::::: CC: 0day robot <fengguang.wu@intel.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: 58385 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170320/955c7942/attachment-0001.gz>

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

* Re: [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
  2017-03-17 14:08   ` Arnaud Pouliquen
                     ` (2 preceding siblings ...)
  (?)
@ 2017-03-20  8:08   ` Peter Meerwald-Stadler
  -1 siblings, 0 replies; 109+ messages in thread
From: Peter Meerwald-Stadler @ 2017-03-20  8:08 UTC (permalink / raw)
  To: Arnaud Pouliquen; +Cc: Jonathan Cameron, Lars-Peter Clausen, linux-iio


> Add iio consumer API to set buffer size and watermark according
> to sysfs API.

comments below
 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>  include/linux/iio/consumer.h                | 13 +++++++++++++
>  2 files changed, 25 insertions(+)
> 
> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
> index b8f550e..43c066a 100644
> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>  }
>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>  
> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
> +				      size_t length, size_t watermark)
> +{
> +	if (!length || length < watermark)
> +		return -EINVAL;
> +	cb_buff->buffer.watermark = watermark;
> +	cb_buff->buffer.length = length;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
> +
>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>  {
>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
> index cb44771..0f6e94d 100644
> --- a/include/linux/iio/consumer.h
> +++ b/include/linux/iio/consumer.h
> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>  						       void *private),
>  					     void *private);
>  /**
> + * iio_channel_cb_set_buffer_size() - set the buffer length.
> + * @cb_buffer:		The callback buffer from whom we want the channel
> + *			information.
> + * @length: buffer length in bytes
> + * @watermark: buffer watermark in bytes
> + *
> + * This function allows to configure the buffer length. The watermark if

if -> is

> + * forced to half of the buffer.
> + */
> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
> +				     size_t length, size_t watermark);
> +
> +/**
>   * iio_channel_release_all_cb() - release and unregister the callback.
>   * @cb_buffer:		The callback buffer that was allocated.
>   */
> 

-- 

Peter Meerwald-Stadler
Mobile: +43 664 24 44 418

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

* Re: [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  2017-03-19 22:25       ` Jonathan Cameron
  (?)
@ 2017-03-20 11:24         ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:24 UTC (permalink / raw)
  To: Jonathan Cameron, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, Olivier MOYSAN, kernel, linux-iio,
	Maxime Coquelin, linux-arm-kernel, Alexandre TORGUE

Hello Jonathan

Thanks for your comments
Few answers in-line.

Regards
Arnaud

On 03/19/2017 11:25 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
>> in n bit samples through a low pass filter and an integrator.
>> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Various minor bits inline.
> 
> I'm mostly liking this.  I do slightly wondering if semantically it
> should be the front end that has the channels rather than the
> backend.  Would be fiddly to do though and probably not worth the
> hassle.
DFSDM support the scan mode, so several front ends can be connected to
One filter. In this case not possible to expose channel FE.

> 
> Would love to see it running in a continuous mode in IIO, but
> I guess that can follow along later.
Yes for the rest of the management it should be quite close to the
stm32-adc driver.

> 
> The comment about the trigger has me confused
> - perhaps you could elaborate further on that?
Code associated to the trigger should be part of the [PATCH v3 06/11]
IIO: ADC: add stm32 DFSDM support for PDM microphone, as it concern the
audio part...
I did not found a way to use consumer.h interface to enable DFSDM IIO,
without defining triggered buffer. that's why i defined a trigger and
use it.
But i just saw that my reasoning is wrong. I'm linked to trigger in
stm32-dfsdm-audio.c because i use iio_triggered_buffer_postenable and
iio_triggered_buffer_predisable. As i don't use the callback for buffer
no need to call it...i can call the ASoC callback directly in DMA IRQ.
Still a hack but more logic...

> 
> Jonathan
>> ---
>> V2 -> V3 :
>> 	- Split audio and ADC support in 2 drivers
>> 	- Implement DMA cyclic mode
>> 	- Add SPI bus Trigger for buffer management
>>
>>  drivers/iio/adc/Kconfig            |  26 ++
>>  drivers/iio/adc/Makefile           |   2 +
>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>>  5 files changed, 1477 insertions(+)
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index d411d66..3e0eb11 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -452,6 +452,32 @@ config STM32_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-adc.
>>  
>> +config STM32_DFSDM_CORE
>> +	tristate "STMicroelectronics STM32 dfsdm core"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	help
>> +	  Select this option to enable the  driver for STMicroelectronics
>> +	  STM32 digital filter for sigma delta converter.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-dfsdm-core.
>> +
>> +config STM32_DFSDM_ADC
>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select STM32_DFSDM_CORE
>> +	select REGMAP_MMIO
>> +	select IIO_BUFFER_DMAENGINE
>> +	select IIO_HW_CONSUMER
>> +	help
>> +	  Select this option to support ADCSigma delta modulator for
>> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
>> +
>> +	  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 c68819c..161f271 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>> @@ -0,0 +1,419 @@
>> +/*
>> + * This file is the ADC part of of the STM32 DFSDM driver
>> + *
>> + * Copyright (C) 2017, 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/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +#define DFSDM_TIMEOUT_US 100000
>> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
>> +
>> +struct stm32_dfsdm_adc {
>> +	struct stm32_dfsdm *dfsdm;
>> +	unsigned int fl_id;
>> +	unsigned int ch_id;
>> +
>> +	unsigned int oversamp;
>> +
>> +	struct completion completion;
>> +
>> +	u32 *buffer;
>> +
>> +	/* Hardware consumer structure for Front End IIO */
>> +	struct iio_hw_consumer *hwc;
>> +};
>> +
>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
>> +{
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_dfsdm;
>> +
>> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	return 0;
>> +
>> +stop_channels:
>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>> +stop_dfsdm:
>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>> +
>> +	return ret;
>> +}
>> +
>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
>> +{
>> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
>> +
>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>> +
>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>> +}
>> +
>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>> +				   const struct iio_chan_spec *chan, int *res)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	long timeout;
>> +	int ret;
>> +
>> +	reinit_completion(&adc->completion);
>> +
>> +	adc->buffer = res;
>> +
>> +	/* Unmask IRQ for regular conversion achievement*/
>> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_conv(adc);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
>> +							    DFSDM_TIMEOUT);
> blank line perhaps.
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +
>> +	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", *res);
>> +		ret = IIO_VAL_INT;
>> +	}
>> +
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +
>> +	stm32_dfsdm_stop_conv(adc);
>> +
>> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
>> +	int ret = -EINVAL;
>> +
>> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
>> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
>> +		if (!ret)
>> +			adc->oversamp = val;
>> +	}
> blank line here.
>> +	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);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = iio_hw_consumer_enable(adc->hwc);
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"%s: IIO enable failed (channel %d)\n",
>> +				__func__, chan->channel);
>> +			return ret;
>> +		}
>> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"%s: Conversion failed (channel %d)\n",
>> +				__func__, chan->channel);
>> +			return ret;
>> +		}
>> +
>> +		iio_hw_consumer_disable(adc->hwc);
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		*val = adc->oversamp;
>> +
>> +		return IIO_VAL_INT;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info stm32_dfsdm_info_adc = {
>> +	.read_raw = stm32_dfsdm_read_raw,
>> +	.write_raw = stm32_dfsdm_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	struct stm32_dfsdm_adc *adc = arg;
>> +	struct regmap *regmap = adc->dfsdm->regmap;
>> +	unsigned int status;
>> +
>> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
>> +
>> +	if (status & DFSDM_ISR_REOCF_MASK) {
>> +		/* read the data register clean the IRQ status */
>> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
>> +		complete(&adc->completion);
>> +	}
>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> What's this one?  Might want a comment given it's an irq you basically eat.
Yes  at least an error message that to inform on an overrun.
>> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
>> +				   DFSDM_ICR_CLRROVRF_MASK,
>> +				   DFSDM_ICR_CLRROVRF_MASK);
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +
>> +	return stm32_dfsdm_start_conv(adc);
>> +}
>> +
>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +
>> +	stm32_dfsdm_stop_conv(adc);
> blank line.
>> +	return 0;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>> +	.postenable = &stm32_dfsdm_postenable,
>> +	.predisable = &stm32_dfsdm_predisable,
>> +};
>> +
>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>> +					 struct iio_chan_spec *chan,
>> +					 int ch_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[ch_idx];
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = ch_idx;
>> +
>> +	/*
>> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
>> +	 */
>> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>> +
>> +	ch->scan_type.sign = 'u';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +	adc->ch_id = ch->channel;
>> +
>> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
>> +					  &adc->dfsdm->ch_list[ch->channel]);
>> +}
>> +
>> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
>> +{
>> +	struct iio_chan_spec *channels;
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	unsigned int num_ch;
>> +	int ret, chan_idx;
>> +
>> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
>> +					     "st,adc-channels");
>> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>> +		return num_ch < 0 ? num_ch : -EINVAL;
>> +	}
>> +
>> +	/*
>> +	 * Number of channel per filter is temporary limited to 1.
>> +	 * Restriction should be cleaned with scan mode
>> +	 */
>> +	if (num_ch > 1) {
>> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Bind to SD modulator IIO device */
>> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>> +	if (IS_ERR(adc->hwc))
>> +		return -EPROBE_DEFER;
>> +
>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>> +				GFP_KERNEL);
>> +	if (!channels)
>> +		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 free_hwc;
>> +	}
>> +
>> +	indio_dev->num_channels = num_ch;
>> +	indio_dev->channels = channels;
>> +
>> +	return 0;
>> +
>> +free_hwc:
>> +	iio_hw_consumer_free(adc->hwc);
> Given you have to free this in the error path, I would imagine you will
> need a free somewhere in the main remove path?  Or just create a devm
> version of iio_hw_consumer_alloc.  It will be useful in the long run.
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
>> +	{ .compatible = "st,stm32-dfsdm-adc"},
>> +	{}
>> +};
>> +
>> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct stm32_dfsdm_adc *adc;
>> +	struct device_node *np = dev->of_node;
>> +	struct iio_dev *iio;
>> +	char *name;
>> +	int ret, irq, val;
>> +
>> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
>> +	if (IS_ERR(iio)) {
>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>> +		return PTR_ERR(iio);
>> +	}
>> +
>> +	adc = iio_priv(iio);
>> +	if (IS_ERR(adc)) {
>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>> +		return PTR_ERR(adc);
>> +	}
>> +	adc->dfsdm = dev_get_drvdata(dev->parent);
>> +
>> +	iio->dev.parent = dev;
>> +	iio->dev.of_node = np;
>> +	iio->info = &stm32_dfsdm_info_adc;
>> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
>> +
>> +	platform_set_drvdata(pdev, adc);
>> +
>> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
>> +	if (ret != 0) {
>> +		dev_err(dev, "Missing reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
> not freed.  Maybe devm_kzalloc
>> +	if (!name)
>> +		return -ENOMEM;
>> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
>> +	iio->name = name;
>> +
>> +	/*
>> +	 * In a first step IRQs generated for channels are not treated.
>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>> +	 */
>> +	irq = platform_get_irq(pdev, 0);
>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>> +			       0, pdev->name, adc);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to request IRQ\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set filter order\n");
>> +		return ret;
>> +	}
>> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>> +	if (!ret)
>> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
>> +
>> +	ret = stm32_dfsdm_adc_chan_init(iio);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	init_completion(&adc->completion);
>> +
>> +	return iio_device_register(iio);
>> +}
>> +
>> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	iio_device_unregister(iio);
> If all you have is this in remove, you can probably get away with
> devm_iio_device_register and get rid of the remove entirely.
>> +
>> +	return 0;
>> +}
>> +
>> +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");
>> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
>> new file mode 100644
>> index 0000000..488e456
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
>> @@ -0,0 +1,658 @@
>> +/*
>> + * This file is part the core part STM32 DFSDM 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/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +struct stm32_dfsdm_dev_data {
>> +	unsigned int num_filters;
>> +	unsigned int num_channels;
>> +	const struct regmap_config *regmap_cfg;
>> +};
>> +
>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>> +#define STM32H7_DFSDM_NUM_CHANNELS	8
>> +
>> +#define DFSDM_MAX_INT_OVERSAMPLING 256
>> +
>> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
>> +
>> +#define DFSDM_MAX_RES BIT(31)
>> +#define DFSDM_DATA_RES BIT(23)
>> +
>> +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 = 0x2B8,
>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>> +	.fast_io = true,
>> +};
>> +
>> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
>> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
>> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>> +};
>> +
>> +struct dfsdm_priv {
>> +	struct platform_device *pdev; /* platform device*/
>> +
>> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
>> +
>> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
>> +	atomic_t n_active_ch;	/* number of current active channels */
>> +
>> +	/* Clock */
>> +	struct clk *clk; /* DFSDM clock */
>> +	struct clk *aclk; /* audio clock */
>> +};
>> +
>> +/**
>> + * stm32_dfsdm_set_osrs - compute filter parameters.
> Naming would suggest it's more specific than this.
> Setting over sampling ratios?
Right, it is a computation not a set.
>> + *
>> + * Enable interface if n_active_ch is not null.
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fast: Fast mode enabled or disabled
>> + * @oversamp: Expected oversampling between filtered sample and SD input stream
>> + */
>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>> +			 unsigned int oversamp)
>> +{
>> +	unsigned int i, d, fosr, iosr;
>> +	u64 res;
>> +	s64 delta;
>> +	unsigned int m = 1;	/* multiplication factor */
>> +	unsigned int p = fl->ford;	/* filter order (ford) */
>> +
>> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
>> +	/*
>> +	 * This function tries to compute filter oversampling and integrator
>> +	 * oversampling, base on oversampling ratio requested by user.
>> +	 *
>> +	 * Decimation d depends on the filter order and the oversampling ratios.
>> +	 * ford: filter order
>> +	 * fosr: filter over sampling ratio
>> +	 * iosr: integrator over sampling ratio
>> +	 */
>> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
>> +		m = 2;
>> +		p = 2;
>> +	}
>> +
>> +	/*
>> +	 * Looks for filter and integrator oversampling ratios which allows
>> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
>> +				d = fosr * (iosr + 3) + 2;
>> +			else
>> +				d = fosr * (iosr - 1 + p) + p;
>> +
>> +			if (d > oversamp)
>> +				break;
>> +			else if (d != oversamp)
>> +				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 > DFSDM_MAX_RES)
>> +					break;
>> +			}
>> +			if (res > DFSDM_MAX_RES)
>> +				continue;
>> +			res = res * (u64)m * (u64)iosr;
>> +			if (res > DFSDM_MAX_RES)
>> +				continue;
>> +
>> +			delta = res - DFSDM_DATA_RES;
>> +
>> +			if (res >= fl->res) {
>> +				fl->res = res;
>> +				fl->fosr = fosr;
>> +				fl->iosr = iosr;
>> +				fl->fast = fast;
>> +				pr_debug("%s: fosr = %d, iosr = %d\n",
>> +					 __func__, fl->fosr, fl->iosr);
>> +			}
>> +
>> +			if (!delta)
>> +				return 0;
>> +		}
>> +	}
>> +
>> +	if (!fl->fosr)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
>> + *
>> + * Enable interface if n_active_ch is not null.
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + */
>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	struct device *dev = &priv->pdev->dev;
>> +	unsigned int clk_div = priv->spi_clk_out_div;
>> +	int ret;
>> +
>> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
>> +		/* Enable clocks */
>> +		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");
>> +				goto disable_clk;
>> +			}
>> +		}
>> +
>> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
>> +		if (ret < 0)
>> +			goto disable_aclk;
>> +
>> +		/* Global enable of DFSDM interface */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>> +					 DFSDM_CHCFGR1_DFSDMEN(1));
>> +		if (ret < 0)
>> +			goto disable_aclk;
>> +	}
>> +
>> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
>> +		atomic_read(&priv->n_active_ch));
>> +
>> +	return 0;
>> +
>> +disable_aclk:
>> +	clk_disable_unprepare(priv->aclk);
>> +disable_clk:
>> +	clk_disable_unprepare(priv->clk);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
>> + *
>> + * Disable interface if n_active_ch is null
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + */
>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	int ret;
>> +
>> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
>> +		/* Global disable of DFSDM interface */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>> +					 DFSDM_CHCFGR1_DFSDMEN(0));
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		/* Stop SPI CLKOUT */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		/* Disable clocks */
>> +		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));
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_channel
>> + *	Start DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: Channel index.
>> + */
>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>> +{
>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>> +				  DFSDM_CHCFGR1_CHEN_MASK,
>> +				  DFSDM_CHCFGR1_CHEN(1));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_channel
>> + *	Stop DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: Channel index.
>> + */
>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>> +{
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>> +			   DFSDM_CHCFGR1_CHEN_MASK,
>> +			   DFSDM_CHCFGR1_CHEN(0));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_chan_configure
>> + *	Configure DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: channel index.
>> + */
>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>> +			       struct stm32_dfsdm_channel *ch)
>> +{
>> +	unsigned int id = ch->id;
>> +	struct regmap *regmap = dfsdm->regmap;
>> +	int ret;
>> +
>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				 DFSDM_CHCFGR1_SITP_MASK,
>> +				 DFSDM_CHCFGR1_SITP(ch->type));
>> +	if (ret < 0)
>> +		return ret;
> Blank line here and in similar places makes it easier for my
> eyes to parse at least...
> I'd also like to see some docs in here, not all of these
> are self explanatory.
I will apply recommendation in my whole code for next time
>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
>> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
>> +	if (ret < 0)
>> +		return ret;
>> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
>> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter index.
>> + */
>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>> +{
>> +	int ret;
>> +
>> +	/* Enable filter */
>> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Start conversion */
>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +				  DFSDM_CR1_RSWSTART_MASK,
>> +				  DFSDM_CR1_RSWSTART(1));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter index.
>> + */
>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>> +{
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +	/* Disable conversion */
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
>> + *	to channel.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: channel index.
>> + * @fl_id: Filter index.
>> + */
>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				 unsigned int ch_id)
>> +{
>> +	struct regmap *regmap = dfsdm->regmap;
>> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
>> +	int ret;
>> +
>> +	/* Average integrator oversampling */
>> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
>> +				 DFSDM_FCR_IOSR(fl->iosr));
>> +
>> +	/* Filter order and Oversampling */
> Please handle each error properly as it happens rather than mudling onwards.
> If there is reason for this odd construction, then document it clearly.
If you mention the checks on ret value that are missing at end of
functions, yes dirty code to fix.

>> +	if (!ret)
>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>> +					 DFSDM_FCR_FOSR_MASK,
>> +					 DFSDM_FCR_FOSR(fl->fosr));
>> +
>> +	if (!ret)
>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>> +					 DFSDM_FCR_FORD_MASK,
>> +					 DFSDM_FCR_FORD(fl->ford));
>> +
>> +	/* If only one channel no scan mode supported for the moment */
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>> +				 DFSDM_CR1_RCH_MASK,
>> +				 DFSDM_CR1_RCH(ch_id));
>> +
>> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>> +					 DFSDM_CR1_RSYNC_MASK,
>> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
>> +}
>> +
>> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
>> +					 struct stm32_dfsdm *dfsdm)
>> +{
>> +	/*
>> +	 * To be able to use buffer consumer interface a trigger is needed.
>> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
>> +	 * sense to define the serial interface ( SPI or manchester) as
>> +	 * trigger source.
> It's not actually the case that you have to have a triggrer.
> There are plenty of drivers (particularly ones with hardware buffering)
> where there is no trigger envolved.  That's not to say it doesn't make sense
> here.
> 
> I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
> somewhat...
>> +	 */
>> +
>> +	struct iio_trigger *trig;
>> +	int ret;
>> +
>> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
>> +	if (!trig)
>> +		return -ENOMEM;
>> +
>> +	trig->dev.parent = pdev->dev.parent;
>> +	trig->ops = &dfsdm_trigger_ops;
>> +
>> +	iio_trigger_set_drvdata(trig, dfsdm);
>> +
>> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
>> +	if (ret)
>> +		return ret;
> Just return ret in all cases.
>> +
>> +	return 0;
>> +}
>> +
>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>> +				 struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec *chan, int chan_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>> +	struct stm32_dfsdm_channel *df_ch;
>> +	const char *of_str;
>> +	int ret, val;
>> +
>> +	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;
>> +	}
>> +
>> +	df_ch =  &dfsdm->ch_list[ch->channel];
> Extra space on the line above.
>> +	df_ch->id = ch->channel;
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-types", chan_idx,
>> +					    &of_str);
>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
>> +	if (ret < 0 || val < 0)
>> +		df_ch->type = 0;
>> +	else
>> +		df_ch->type = val;
>> +
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-clk-src", chan_idx,
>> +					    &of_str);
>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
>> +	if (ret < 0 || val < 0)
>> +		df_ch->src = 0;
>> +	else
>> +		df_ch->src = val;
>> +
>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>> +					 "st,adc-alt-channel", chan_idx,
>> +					 &df_ch->alt_si);
>> +	if (ret < 0)
>> +		df_ch->alt_si = 0;
>> +
>> +	return 0;
>> +}
>> +
>> +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;
>> +	unsigned long clk_freq;
>> +	unsigned int spi_freq, rem;
>> +	int ret;
>> +
>> +	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->dfsdm.phys_base = res->start;
>> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
>> +
>> +	/* Source clock */
>> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
>> +	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");
>> +	if (IS_ERR(priv->aclk))
>> +		priv->aclk = NULL;
>> +
>> +	if (priv->aclk)
>> +		clk_freq = clk_get_rate(priv->aclk);
>> +	else
>> +		clk_freq = clk_get_rate(priv->clk);
>> +
>> +	/* SPI clock freq */
>> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
>> +				   &spi_freq);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
>> +		return ret;
>> +	}
>> +
>> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
>> +	priv->dfsdm.spi_master_freq = spi_freq;
>> +
>> +	if (rem) {
>> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
>> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
>> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
>> +	}
>> +
>> +	return 0;
>> +};
>> +
>> +static const struct of_device_id stm32_dfsdm_of_match[] = {
>> +	{
>> +		.compatible = "st,stm32h7-dfsdm",
>> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
>> +	struct stm32_dfsdm *dfsdm;
>> +	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_dfsdm_dev_data *)of_id->data;
>> +	dfsdm = &priv->dfsdm;
>> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
>> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
>> +	if (!dfsdm->fl_list)
>> +		return -ENOMEM;
>> +
>> +	dfsdm->num_fls = dev_data->num_filters;
>> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
>> +				      sizeof(*dfsdm->ch_list),
>> +				      GFP_KERNEL);
>> +	if (!dfsdm->ch_list)
>> +		return -ENOMEM;
>> +	dfsdm->num_chs = dev_data->num_channels;
>> +
>> +	ret = stm32_dfsdm_parse_of(pdev, priv);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
>> +					    &stm32h7_dfsdm_regmap_cfg);
>> +	if (IS_ERR(dfsdm->regmap)) {
>> +		ret = PTR_ERR(dfsdm->regmap);
>> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
>> +			__func__, ret);
>> +		return ret;
>> +	}
>> +
>> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
>> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
>> +
>> +		fl->id = i;
> I'd like a comment on why this is needed...
 to  be cleaned.
>> +	}
>> +
>> +	platform_set_drvdata(pdev, dfsdm);
>> +
>> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
>> +}
>> +
>> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
>> new file mode 100644
>> index 0000000..bb7d74f
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm.h
>> @@ -0,0 +1,371 @@
>> +/*
>> + * This file is part of STM32 DFSDM 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__H
>> +#define MDF_STM32_DFSDM__H
>> +
>> +#include <linux/bitfield.h>
>> +
>> +#include <linux/iio/iio.h>
>> +/*
>> + * STM32 DFSDM - global register map
>> + * ________________________________________________________
>> + * | Offset |                 Registers block             |
>> + * --------------------------------------------------------
>> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
>> + * --------------------------------------------------------
>> + * | 0x020  |                CHANNEL 1                    |
>> + * --------------------------------------------------------
>> + * | ...    |                .....                        |
>> + * --------------------------------------------------------
>> + * | 0x0E0  |                CHANNEL 7                    |
>> + * --------------------------------------------------------
>> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
>> + * --------------------------------------------------------
>> + * | 0x200  |                FILTER  1                    |
>> + * --------------------------------------------------------
>> + * | 0x300  |                FILTER  2                    |
>> + * --------------------------------------------------------
>> + * | 0x400  |                FILTER  3                    |
>> + * --------------------------------------------------------
>> + */
>> +
>> +/*
>> + * Channels register 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 register 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)
>> +
>> +/* 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,
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
>> + * TODO: complete structure.
> nice :) RFC I guess :)
>> + * @id:		filetr ID,
>> + */
>> +struct stm32_dfsdm_filter {
>> +	unsigned int id;
>> +	unsigned int iosr; /* integrator oversampling */
>> +	unsigned int fosr; /* filter oversampling */
>> +	enum stm32_dfsdm_sinc_order ford;
>> +	u64 res; /* output sample resolution */
>> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
>> +	unsigned int fast; /* filter fast mode */
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
>> + * TODO: complete structure.
>> + * @id:		filetr ID,
> filter
>> + */
>> +struct stm32_dfsdm_channel {
>> +	unsigned int id;   /* id of the channel */
>> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
>> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
>> +	unsigned int alt_si; /* use alternative serial input interface */
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
>> + * @base:	control registers base cpu addr
>> + * @phys_base:	DFSDM IP register physical address.
>> + * @fl_list:	filter resources list
>> + * @num_fl:	number of filter resources available
>> + * @ch_list:	channel resources list
>> + * @num_chs:	number of channel resources available
>> + */
>> +struct stm32_dfsdm {
>> +	void __iomem	*base;
>> +	phys_addr_t	phys_base;
>> +	struct regmap *regmap;
>> +	struct stm32_dfsdm_filter *fl_list;
>> +	unsigned int num_fls;
>> +	struct stm32_dfsdm_channel *ch_list;
>> +	unsigned int num_chs;
>> +	unsigned int spi_master_freq;
>> +};
>> +
>> +struct stm32_dfsdm_str2field {
>> +	const char	*name;
>> +	unsigned int	val;
>> +};
>> +
>> +/* DFSDM channel serial interface type */
>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
>> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
>> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
>> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
>> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
>> +	{ 0, 0},
>> +};
>> +
>> +/* DFSDM channel serial spi clock source */
>> +enum stm32_dfsdm_spi_clk_src {
>> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
>> +};
>> +
>> +/* DFSDM channel clock source */
>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
>> +	/* External SPI clock (CLKIN x) */
>> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
>> +	/* Internal SPI clock (CLKOUT) */
>> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
>> +	/* Internal SPI clock divided by 2 (falling edge) */
>> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
>> +	/* Internal SPI clock divided by 2 (falling edge) */
>> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
>> +	{ 0, 0 },
>> +};
>> +
>> +/* DFSDM Serial interface trigger name  */
>> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
>> +
>> +static inline int stm32_dfsdm_str2val(const char *str,
>> +				      const struct stm32_dfsdm_str2field *list)
>> +{
>> +	const struct stm32_dfsdm_str2field *p = list;
>> +
>> +	for (p = list; p && p->name; p++) {
>> +		if (!strcmp(p->name, str))
>> +			return p->val;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>> +			 unsigned int oversamp);
>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
>> +
>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				 unsigned int ch_id);
>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>> +
>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>> +			       struct stm32_dfsdm_channel *ch);
>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>> +
>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>> +				 struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec *chan, int chan_idx);
>> +
>> +#endif
>>
> 
> --
> 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] 109+ messages in thread

* Re: [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-20 11:24         ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:24 UTC (permalink / raw)
  To: Jonathan Cameron, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

Hello Jonathan

Thanks for your comments
Few answers in-line.

Regards
Arnaud

On 03/19/2017 11:25 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
>> in n bit samples through a low pass filter and an integrator.
>> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Various minor bits inline.
> 
> I'm mostly liking this.  I do slightly wondering if semantically it
> should be the front end that has the channels rather than the
> backend.  Would be fiddly to do though and probably not worth the
> hassle.
DFSDM support the scan mode, so several front ends can be connected to
One filter. In this case not possible to expose channel FE.

> 
> Would love to see it running in a continuous mode in IIO, but
> I guess that can follow along later.
Yes for the rest of the management it should be quite close to the
stm32-adc driver.

> 
> The comment about the trigger has me confused
> - perhaps you could elaborate further on that?
Code associated to the trigger should be part of the [PATCH v3 06/11]
IIO: ADC: add stm32 DFSDM support for PDM microphone, as it concern the
audio part...
I did not found a way to use consumer.h interface to enable DFSDM IIO,
without defining triggered buffer. that's why i defined a trigger and
use it.
But i just saw that my reasoning is wrong. I'm linked to trigger in
stm32-dfsdm-audio.c because i use iio_triggered_buffer_postenable and
iio_triggered_buffer_predisable. As i don't use the callback for buffer
no need to call it...i can call the ASoC callback directly in DMA IRQ.
Still a hack but more logic...

> 
> Jonathan
>> ---
>> V2 -> V3 :
>> 	- Split audio and ADC support in 2 drivers
>> 	- Implement DMA cyclic mode
>> 	- Add SPI bus Trigger for buffer management
>>
>>  drivers/iio/adc/Kconfig            |  26 ++
>>  drivers/iio/adc/Makefile           |   2 +
>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>>  5 files changed, 1477 insertions(+)
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index d411d66..3e0eb11 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -452,6 +452,32 @@ config STM32_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-adc.
>>  
>> +config STM32_DFSDM_CORE
>> +	tristate "STMicroelectronics STM32 dfsdm core"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	help
>> +	  Select this option to enable the  driver for STMicroelectronics
>> +	  STM32 digital filter for sigma delta converter.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-dfsdm-core.
>> +
>> +config STM32_DFSDM_ADC
>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select STM32_DFSDM_CORE
>> +	select REGMAP_MMIO
>> +	select IIO_BUFFER_DMAENGINE
>> +	select IIO_HW_CONSUMER
>> +	help
>> +	  Select this option to support ADCSigma delta modulator for
>> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
>> +
>> +	  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 c68819c..161f271 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>> @@ -0,0 +1,419 @@
>> +/*
>> + * This file is the ADC part of of the STM32 DFSDM driver
>> + *
>> + * Copyright (C) 2017, 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/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +#define DFSDM_TIMEOUT_US 100000
>> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
>> +
>> +struct stm32_dfsdm_adc {
>> +	struct stm32_dfsdm *dfsdm;
>> +	unsigned int fl_id;
>> +	unsigned int ch_id;
>> +
>> +	unsigned int oversamp;
>> +
>> +	struct completion completion;
>> +
>> +	u32 *buffer;
>> +
>> +	/* Hardware consumer structure for Front End IIO */
>> +	struct iio_hw_consumer *hwc;
>> +};
>> +
>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
>> +{
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_dfsdm;
>> +
>> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	return 0;
>> +
>> +stop_channels:
>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>> +stop_dfsdm:
>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>> +
>> +	return ret;
>> +}
>> +
>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
>> +{
>> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
>> +
>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>> +
>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>> +}
>> +
>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>> +				   const struct iio_chan_spec *chan, int *res)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	long timeout;
>> +	int ret;
>> +
>> +	reinit_completion(&adc->completion);
>> +
>> +	adc->buffer = res;
>> +
>> +	/* Unmask IRQ for regular conversion achievement*/
>> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_conv(adc);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
>> +							    DFSDM_TIMEOUT);
> blank line perhaps.
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +
>> +	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", *res);
>> +		ret = IIO_VAL_INT;
>> +	}
>> +
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +
>> +	stm32_dfsdm_stop_conv(adc);
>> +
>> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
>> +	int ret = -EINVAL;
>> +
>> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
>> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
>> +		if (!ret)
>> +			adc->oversamp = val;
>> +	}
> blank line here.
>> +	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);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = iio_hw_consumer_enable(adc->hwc);
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"%s: IIO enable failed (channel %d)\n",
>> +				__func__, chan->channel);
>> +			return ret;
>> +		}
>> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"%s: Conversion failed (channel %d)\n",
>> +				__func__, chan->channel);
>> +			return ret;
>> +		}
>> +
>> +		iio_hw_consumer_disable(adc->hwc);
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		*val = adc->oversamp;
>> +
>> +		return IIO_VAL_INT;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info stm32_dfsdm_info_adc = {
>> +	.read_raw = stm32_dfsdm_read_raw,
>> +	.write_raw = stm32_dfsdm_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	struct stm32_dfsdm_adc *adc = arg;
>> +	struct regmap *regmap = adc->dfsdm->regmap;
>> +	unsigned int status;
>> +
>> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
>> +
>> +	if (status & DFSDM_ISR_REOCF_MASK) {
>> +		/* read the data register clean the IRQ status */
>> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
>> +		complete(&adc->completion);
>> +	}
>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> What's this one?  Might want a comment given it's an irq you basically eat.
Yes  at least an error message that to inform on an overrun.
>> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
>> +				   DFSDM_ICR_CLRROVRF_MASK,
>> +				   DFSDM_ICR_CLRROVRF_MASK);
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +
>> +	return stm32_dfsdm_start_conv(adc);
>> +}
>> +
>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +
>> +	stm32_dfsdm_stop_conv(adc);
> blank line.
>> +	return 0;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>> +	.postenable = &stm32_dfsdm_postenable,
>> +	.predisable = &stm32_dfsdm_predisable,
>> +};
>> +
>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>> +					 struct iio_chan_spec *chan,
>> +					 int ch_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[ch_idx];
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = ch_idx;
>> +
>> +	/*
>> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
>> +	 */
>> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>> +
>> +	ch->scan_type.sign = 'u';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +	adc->ch_id = ch->channel;
>> +
>> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
>> +					  &adc->dfsdm->ch_list[ch->channel]);
>> +}
>> +
>> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
>> +{
>> +	struct iio_chan_spec *channels;
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	unsigned int num_ch;
>> +	int ret, chan_idx;
>> +
>> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
>> +					     "st,adc-channels");
>> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>> +		return num_ch < 0 ? num_ch : -EINVAL;
>> +	}
>> +
>> +	/*
>> +	 * Number of channel per filter is temporary limited to 1.
>> +	 * Restriction should be cleaned with scan mode
>> +	 */
>> +	if (num_ch > 1) {
>> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Bind to SD modulator IIO device */
>> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>> +	if (IS_ERR(adc->hwc))
>> +		return -EPROBE_DEFER;
>> +
>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>> +				GFP_KERNEL);
>> +	if (!channels)
>> +		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 free_hwc;
>> +	}
>> +
>> +	indio_dev->num_channels = num_ch;
>> +	indio_dev->channels = channels;
>> +
>> +	return 0;
>> +
>> +free_hwc:
>> +	iio_hw_consumer_free(adc->hwc);
> Given you have to free this in the error path, I would imagine you will
> need a free somewhere in the main remove path?  Or just create a devm
> version of iio_hw_consumer_alloc.  It will be useful in the long run.
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
>> +	{ .compatible = "st,stm32-dfsdm-adc"},
>> +	{}
>> +};
>> +
>> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct stm32_dfsdm_adc *adc;
>> +	struct device_node *np = dev->of_node;
>> +	struct iio_dev *iio;
>> +	char *name;
>> +	int ret, irq, val;
>> +
>> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
>> +	if (IS_ERR(iio)) {
>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>> +		return PTR_ERR(iio);
>> +	}
>> +
>> +	adc = iio_priv(iio);
>> +	if (IS_ERR(adc)) {
>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>> +		return PTR_ERR(adc);
>> +	}
>> +	adc->dfsdm = dev_get_drvdata(dev->parent);
>> +
>> +	iio->dev.parent = dev;
>> +	iio->dev.of_node = np;
>> +	iio->info = &stm32_dfsdm_info_adc;
>> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
>> +
>> +	platform_set_drvdata(pdev, adc);
>> +
>> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
>> +	if (ret != 0) {
>> +		dev_err(dev, "Missing reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
> not freed.  Maybe devm_kzalloc
>> +	if (!name)
>> +		return -ENOMEM;
>> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
>> +	iio->name = name;
>> +
>> +	/*
>> +	 * In a first step IRQs generated for channels are not treated.
>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>> +	 */
>> +	irq = platform_get_irq(pdev, 0);
>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>> +			       0, pdev->name, adc);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to request IRQ\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set filter order\n");
>> +		return ret;
>> +	}
>> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>> +	if (!ret)
>> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
>> +
>> +	ret = stm32_dfsdm_adc_chan_init(iio);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	init_completion(&adc->completion);
>> +
>> +	return iio_device_register(iio);
>> +}
>> +
>> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	iio_device_unregister(iio);
> If all you have is this in remove, you can probably get away with
> devm_iio_device_register and get rid of the remove entirely.
>> +
>> +	return 0;
>> +}
>> +
>> +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");
>> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
>> new file mode 100644
>> index 0000000..488e456
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
>> @@ -0,0 +1,658 @@
>> +/*
>> + * This file is part the core part STM32 DFSDM 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/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +struct stm32_dfsdm_dev_data {
>> +	unsigned int num_filters;
>> +	unsigned int num_channels;
>> +	const struct regmap_config *regmap_cfg;
>> +};
>> +
>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>> +#define STM32H7_DFSDM_NUM_CHANNELS	8
>> +
>> +#define DFSDM_MAX_INT_OVERSAMPLING 256
>> +
>> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
>> +
>> +#define DFSDM_MAX_RES BIT(31)
>> +#define DFSDM_DATA_RES BIT(23)
>> +
>> +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 = 0x2B8,
>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>> +	.fast_io = true,
>> +};
>> +
>> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
>> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
>> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>> +};
>> +
>> +struct dfsdm_priv {
>> +	struct platform_device *pdev; /* platform device*/
>> +
>> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
>> +
>> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
>> +	atomic_t n_active_ch;	/* number of current active channels */
>> +
>> +	/* Clock */
>> +	struct clk *clk; /* DFSDM clock */
>> +	struct clk *aclk; /* audio clock */
>> +};
>> +
>> +/**
>> + * stm32_dfsdm_set_osrs - compute filter parameters.
> Naming would suggest it's more specific than this.
> Setting over sampling ratios?
Right, it is a computation not a set.
>> + *
>> + * Enable interface if n_active_ch is not null.
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fast: Fast mode enabled or disabled
>> + * @oversamp: Expected oversampling between filtered sample and SD input stream
>> + */
>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>> +			 unsigned int oversamp)
>> +{
>> +	unsigned int i, d, fosr, iosr;
>> +	u64 res;
>> +	s64 delta;
>> +	unsigned int m = 1;	/* multiplication factor */
>> +	unsigned int p = fl->ford;	/* filter order (ford) */
>> +
>> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
>> +	/*
>> +	 * This function tries to compute filter oversampling and integrator
>> +	 * oversampling, base on oversampling ratio requested by user.
>> +	 *
>> +	 * Decimation d depends on the filter order and the oversampling ratios.
>> +	 * ford: filter order
>> +	 * fosr: filter over sampling ratio
>> +	 * iosr: integrator over sampling ratio
>> +	 */
>> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
>> +		m = 2;
>> +		p = 2;
>> +	}
>> +
>> +	/*
>> +	 * Looks for filter and integrator oversampling ratios which allows
>> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
>> +				d = fosr * (iosr + 3) + 2;
>> +			else
>> +				d = fosr * (iosr - 1 + p) + p;
>> +
>> +			if (d > oversamp)
>> +				break;
>> +			else if (d != oversamp)
>> +				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 > DFSDM_MAX_RES)
>> +					break;
>> +			}
>> +			if (res > DFSDM_MAX_RES)
>> +				continue;
>> +			res = res * (u64)m * (u64)iosr;
>> +			if (res > DFSDM_MAX_RES)
>> +				continue;
>> +
>> +			delta = res - DFSDM_DATA_RES;
>> +
>> +			if (res >= fl->res) {
>> +				fl->res = res;
>> +				fl->fosr = fosr;
>> +				fl->iosr = iosr;
>> +				fl->fast = fast;
>> +				pr_debug("%s: fosr = %d, iosr = %d\n",
>> +					 __func__, fl->fosr, fl->iosr);
>> +			}
>> +
>> +			if (!delta)
>> +				return 0;
>> +		}
>> +	}
>> +
>> +	if (!fl->fosr)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
>> + *
>> + * Enable interface if n_active_ch is not null.
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + */
>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	struct device *dev = &priv->pdev->dev;
>> +	unsigned int clk_div = priv->spi_clk_out_div;
>> +	int ret;
>> +
>> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
>> +		/* Enable clocks */
>> +		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");
>> +				goto disable_clk;
>> +			}
>> +		}
>> +
>> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
>> +		if (ret < 0)
>> +			goto disable_aclk;
>> +
>> +		/* Global enable of DFSDM interface */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>> +					 DFSDM_CHCFGR1_DFSDMEN(1));
>> +		if (ret < 0)
>> +			goto disable_aclk;
>> +	}
>> +
>> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
>> +		atomic_read(&priv->n_active_ch));
>> +
>> +	return 0;
>> +
>> +disable_aclk:
>> +	clk_disable_unprepare(priv->aclk);
>> +disable_clk:
>> +	clk_disable_unprepare(priv->clk);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
>> + *
>> + * Disable interface if n_active_ch is null
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + */
>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	int ret;
>> +
>> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
>> +		/* Global disable of DFSDM interface */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>> +					 DFSDM_CHCFGR1_DFSDMEN(0));
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		/* Stop SPI CLKOUT */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		/* Disable clocks */
>> +		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));
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_channel
>> + *	Start DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: Channel index.
>> + */
>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>> +{
>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>> +				  DFSDM_CHCFGR1_CHEN_MASK,
>> +				  DFSDM_CHCFGR1_CHEN(1));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_channel
>> + *	Stop DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: Channel index.
>> + */
>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>> +{
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>> +			   DFSDM_CHCFGR1_CHEN_MASK,
>> +			   DFSDM_CHCFGR1_CHEN(0));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_chan_configure
>> + *	Configure DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: channel index.
>> + */
>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>> +			       struct stm32_dfsdm_channel *ch)
>> +{
>> +	unsigned int id = ch->id;
>> +	struct regmap *regmap = dfsdm->regmap;
>> +	int ret;
>> +
>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				 DFSDM_CHCFGR1_SITP_MASK,
>> +				 DFSDM_CHCFGR1_SITP(ch->type));
>> +	if (ret < 0)
>> +		return ret;
> Blank line here and in similar places makes it easier for my
> eyes to parse at least...
> I'd also like to see some docs in here, not all of these
> are self explanatory.
I will apply recommendation in my whole code for next time
>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
>> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
>> +	if (ret < 0)
>> +		return ret;
>> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
>> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter index.
>> + */
>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>> +{
>> +	int ret;
>> +
>> +	/* Enable filter */
>> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Start conversion */
>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +				  DFSDM_CR1_RSWSTART_MASK,
>> +				  DFSDM_CR1_RSWSTART(1));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter index.
>> + */
>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>> +{
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +	/* Disable conversion */
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
>> + *	to channel.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: channel index.
>> + * @fl_id: Filter index.
>> + */
>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				 unsigned int ch_id)
>> +{
>> +	struct regmap *regmap = dfsdm->regmap;
>> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
>> +	int ret;
>> +
>> +	/* Average integrator oversampling */
>> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
>> +				 DFSDM_FCR_IOSR(fl->iosr));
>> +
>> +	/* Filter order and Oversampling */
> Please handle each error properly as it happens rather than mudling onwards.
> If there is reason for this odd construction, then document it clearly.
If you mention the checks on ret value that are missing at end of
functions, yes dirty code to fix.

>> +	if (!ret)
>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>> +					 DFSDM_FCR_FOSR_MASK,
>> +					 DFSDM_FCR_FOSR(fl->fosr));
>> +
>> +	if (!ret)
>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>> +					 DFSDM_FCR_FORD_MASK,
>> +					 DFSDM_FCR_FORD(fl->ford));
>> +
>> +	/* If only one channel no scan mode supported for the moment */
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>> +				 DFSDM_CR1_RCH_MASK,
>> +				 DFSDM_CR1_RCH(ch_id));
>> +
>> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>> +					 DFSDM_CR1_RSYNC_MASK,
>> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
>> +}
>> +
>> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
>> +					 struct stm32_dfsdm *dfsdm)
>> +{
>> +	/*
>> +	 * To be able to use buffer consumer interface a trigger is needed.
>> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
>> +	 * sense to define the serial interface ( SPI or manchester) as
>> +	 * trigger source.
> It's not actually the case that you have to have a triggrer.
> There are plenty of drivers (particularly ones with hardware buffering)
> where there is no trigger envolved.  That's not to say it doesn't make sense
> here.
> 
> I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
> somewhat...
>> +	 */
>> +
>> +	struct iio_trigger *trig;
>> +	int ret;
>> +
>> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
>> +	if (!trig)
>> +		return -ENOMEM;
>> +
>> +	trig->dev.parent = pdev->dev.parent;
>> +	trig->ops = &dfsdm_trigger_ops;
>> +
>> +	iio_trigger_set_drvdata(trig, dfsdm);
>> +
>> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
>> +	if (ret)
>> +		return ret;
> Just return ret in all cases.
>> +
>> +	return 0;
>> +}
>> +
>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>> +				 struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec *chan, int chan_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>> +	struct stm32_dfsdm_channel *df_ch;
>> +	const char *of_str;
>> +	int ret, val;
>> +
>> +	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;
>> +	}
>> +
>> +	df_ch =  &dfsdm->ch_list[ch->channel];
> Extra space on the line above.
>> +	df_ch->id = ch->channel;
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-types", chan_idx,
>> +					    &of_str);
>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
>> +	if (ret < 0 || val < 0)
>> +		df_ch->type = 0;
>> +	else
>> +		df_ch->type = val;
>> +
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-clk-src", chan_idx,
>> +					    &of_str);
>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
>> +	if (ret < 0 || val < 0)
>> +		df_ch->src = 0;
>> +	else
>> +		df_ch->src = val;
>> +
>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>> +					 "st,adc-alt-channel", chan_idx,
>> +					 &df_ch->alt_si);
>> +	if (ret < 0)
>> +		df_ch->alt_si = 0;
>> +
>> +	return 0;
>> +}
>> +
>> +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;
>> +	unsigned long clk_freq;
>> +	unsigned int spi_freq, rem;
>> +	int ret;
>> +
>> +	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->dfsdm.phys_base = res->start;
>> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
>> +
>> +	/* Source clock */
>> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
>> +	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");
>> +	if (IS_ERR(priv->aclk))
>> +		priv->aclk = NULL;
>> +
>> +	if (priv->aclk)
>> +		clk_freq = clk_get_rate(priv->aclk);
>> +	else
>> +		clk_freq = clk_get_rate(priv->clk);
>> +
>> +	/* SPI clock freq */
>> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
>> +				   &spi_freq);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
>> +		return ret;
>> +	}
>> +
>> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
>> +	priv->dfsdm.spi_master_freq = spi_freq;
>> +
>> +	if (rem) {
>> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
>> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
>> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
>> +	}
>> +
>> +	return 0;
>> +};
>> +
>> +static const struct of_device_id stm32_dfsdm_of_match[] = {
>> +	{
>> +		.compatible = "st,stm32h7-dfsdm",
>> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
>> +	struct stm32_dfsdm *dfsdm;
>> +	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_dfsdm_dev_data *)of_id->data;
>> +	dfsdm = &priv->dfsdm;
>> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
>> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
>> +	if (!dfsdm->fl_list)
>> +		return -ENOMEM;
>> +
>> +	dfsdm->num_fls = dev_data->num_filters;
>> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
>> +				      sizeof(*dfsdm->ch_list),
>> +				      GFP_KERNEL);
>> +	if (!dfsdm->ch_list)
>> +		return -ENOMEM;
>> +	dfsdm->num_chs = dev_data->num_channels;
>> +
>> +	ret = stm32_dfsdm_parse_of(pdev, priv);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
>> +					    &stm32h7_dfsdm_regmap_cfg);
>> +	if (IS_ERR(dfsdm->regmap)) {
>> +		ret = PTR_ERR(dfsdm->regmap);
>> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
>> +			__func__, ret);
>> +		return ret;
>> +	}
>> +
>> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
>> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
>> +
>> +		fl->id = i;
> I'd like a comment on why this is needed...
 to  be cleaned.
>> +	}
>> +
>> +	platform_set_drvdata(pdev, dfsdm);
>> +
>> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
>> +}
>> +
>> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
>> new file mode 100644
>> index 0000000..bb7d74f
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm.h
>> @@ -0,0 +1,371 @@
>> +/*
>> + * This file is part of STM32 DFSDM 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__H
>> +#define MDF_STM32_DFSDM__H
>> +
>> +#include <linux/bitfield.h>
>> +
>> +#include <linux/iio/iio.h>
>> +/*
>> + * STM32 DFSDM - global register map
>> + * ________________________________________________________
>> + * | Offset |                 Registers block             |
>> + * --------------------------------------------------------
>> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
>> + * --------------------------------------------------------
>> + * | 0x020  |                CHANNEL 1                    |
>> + * --------------------------------------------------------
>> + * | ...    |                .....                        |
>> + * --------------------------------------------------------
>> + * | 0x0E0  |                CHANNEL 7                    |
>> + * --------------------------------------------------------
>> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
>> + * --------------------------------------------------------
>> + * | 0x200  |                FILTER  1                    |
>> + * --------------------------------------------------------
>> + * | 0x300  |                FILTER  2                    |
>> + * --------------------------------------------------------
>> + * | 0x400  |                FILTER  3                    |
>> + * --------------------------------------------------------
>> + */
>> +
>> +/*
>> + * Channels register 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 register 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)
>> +
>> +/* 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,
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
>> + * TODO: complete structure.
> nice :) RFC I guess :)
>> + * @id:		filetr ID,
>> + */
>> +struct stm32_dfsdm_filter {
>> +	unsigned int id;
>> +	unsigned int iosr; /* integrator oversampling */
>> +	unsigned int fosr; /* filter oversampling */
>> +	enum stm32_dfsdm_sinc_order ford;
>> +	u64 res; /* output sample resolution */
>> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
>> +	unsigned int fast; /* filter fast mode */
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
>> + * TODO: complete structure.
>> + * @id:		filetr ID,
> filter
>> + */
>> +struct stm32_dfsdm_channel {
>> +	unsigned int id;   /* id of the channel */
>> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
>> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
>> +	unsigned int alt_si; /* use alternative serial input interface */
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
>> + * @base:	control registers base cpu addr
>> + * @phys_base:	DFSDM IP register physical address.
>> + * @fl_list:	filter resources list
>> + * @num_fl:	number of filter resources available
>> + * @ch_list:	channel resources list
>> + * @num_chs:	number of channel resources available
>> + */
>> +struct stm32_dfsdm {
>> +	void __iomem	*base;
>> +	phys_addr_t	phys_base;
>> +	struct regmap *regmap;
>> +	struct stm32_dfsdm_filter *fl_list;
>> +	unsigned int num_fls;
>> +	struct stm32_dfsdm_channel *ch_list;
>> +	unsigned int num_chs;
>> +	unsigned int spi_master_freq;
>> +};
>> +
>> +struct stm32_dfsdm_str2field {
>> +	const char	*name;
>> +	unsigned int	val;
>> +};
>> +
>> +/* DFSDM channel serial interface type */
>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
>> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
>> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
>> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
>> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
>> +	{ 0, 0},
>> +};
>> +
>> +/* DFSDM channel serial spi clock source */
>> +enum stm32_dfsdm_spi_clk_src {
>> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
>> +};
>> +
>> +/* DFSDM channel clock source */
>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
>> +	/* External SPI clock (CLKIN x) */
>> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
>> +	/* Internal SPI clock (CLKOUT) */
>> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
>> +	/* Internal SPI clock divided by 2 (falling edge) */
>> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
>> +	/* Internal SPI clock divided by 2 (falling edge) */
>> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
>> +	{ 0, 0 },
>> +};
>> +
>> +/* DFSDM Serial interface trigger name  */
>> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
>> +
>> +static inline int stm32_dfsdm_str2val(const char *str,
>> +				      const struct stm32_dfsdm_str2field *list)
>> +{
>> +	const struct stm32_dfsdm_str2field *p = list;
>> +
>> +	for (p = list; p && p->name; p++) {
>> +		if (!strcmp(p->name, str))
>> +			return p->val;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>> +			 unsigned int oversamp);
>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
>> +
>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				 unsigned int ch_id);
>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>> +
>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>> +			       struct stm32_dfsdm_channel *ch);
>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>> +
>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>> +				 struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec *chan, int chan_idx);
>> +
>> +#endif
>>
> 
> --
> 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] 109+ messages in thread

* [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-20 11:24         ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:24 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Jonathan

Thanks for your comments
Few answers in-line.

Regards
Arnaud

On 03/19/2017 11:25 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
>> in n bit samples through a low pass filter and an integrator.
>> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Various minor bits inline.
> 
> I'm mostly liking this.  I do slightly wondering if semantically it
> should be the front end that has the channels rather than the
> backend.  Would be fiddly to do though and probably not worth the
> hassle.
DFSDM support the scan mode, so several front ends can be connected to
One filter. In this case not possible to expose channel FE.

> 
> Would love to see it running in a continuous mode in IIO, but
> I guess that can follow along later.
Yes for the rest of the management it should be quite close to the
stm32-adc driver.

> 
> The comment about the trigger has me confused
> - perhaps you could elaborate further on that?
Code associated to the trigger should be part of the [PATCH v3 06/11]
IIO: ADC: add stm32 DFSDM support for PDM microphone, as it concern the
audio part...
I did not found a way to use consumer.h interface to enable DFSDM IIO,
without defining triggered buffer. that's why i defined a trigger and
use it.
But i just saw that my reasoning is wrong. I'm linked to trigger in
stm32-dfsdm-audio.c because i use iio_triggered_buffer_postenable and
iio_triggered_buffer_predisable. As i don't use the callback for buffer
no need to call it...i can call the ASoC callback directly in DMA IRQ.
Still a hack but more logic...

> 
> Jonathan
>> ---
>> V2 -> V3 :
>> 	- Split audio and ADC support in 2 drivers
>> 	- Implement DMA cyclic mode
>> 	- Add SPI bus Trigger for buffer management
>>
>>  drivers/iio/adc/Kconfig            |  26 ++
>>  drivers/iio/adc/Makefile           |   2 +
>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>>  5 files changed, 1477 insertions(+)
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index d411d66..3e0eb11 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -452,6 +452,32 @@ config STM32_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-adc.
>>  
>> +config STM32_DFSDM_CORE
>> +	tristate "STMicroelectronics STM32 dfsdm core"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	help
>> +	  Select this option to enable the  driver for STMicroelectronics
>> +	  STM32 digital filter for sigma delta converter.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-dfsdm-core.
>> +
>> +config STM32_DFSDM_ADC
>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select STM32_DFSDM_CORE
>> +	select REGMAP_MMIO
>> +	select IIO_BUFFER_DMAENGINE
>> +	select IIO_HW_CONSUMER
>> +	help
>> +	  Select this option to support ADCSigma delta modulator for
>> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
>> +
>> +	  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 c68819c..161f271 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>> @@ -0,0 +1,419 @@
>> +/*
>> + * This file is the ADC part of of the STM32 DFSDM driver
>> + *
>> + * Copyright (C) 2017, 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/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +#define DFSDM_TIMEOUT_US 100000
>> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
>> +
>> +struct stm32_dfsdm_adc {
>> +	struct stm32_dfsdm *dfsdm;
>> +	unsigned int fl_id;
>> +	unsigned int ch_id;
>> +
>> +	unsigned int oversamp;
>> +
>> +	struct completion completion;
>> +
>> +	u32 *buffer;
>> +
>> +	/* Hardware consumer structure for Front End IIO */
>> +	struct iio_hw_consumer *hwc;
>> +};
>> +
>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
>> +{
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_dfsdm;
>> +
>> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	return 0;
>> +
>> +stop_channels:
>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>> +stop_dfsdm:
>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>> +
>> +	return ret;
>> +}
>> +
>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
>> +{
>> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
>> +
>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>> +
>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>> +}
>> +
>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>> +				   const struct iio_chan_spec *chan, int *res)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	long timeout;
>> +	int ret;
>> +
>> +	reinit_completion(&adc->completion);
>> +
>> +	adc->buffer = res;
>> +
>> +	/* Unmask IRQ for regular conversion achievement*/
>> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_conv(adc);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
>> +							    DFSDM_TIMEOUT);
> blank line perhaps.
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +
>> +	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", *res);
>> +		ret = IIO_VAL_INT;
>> +	}
>> +
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +
>> +	stm32_dfsdm_stop_conv(adc);
>> +
>> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
>> +	int ret = -EINVAL;
>> +
>> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
>> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
>> +		if (!ret)
>> +			adc->oversamp = val;
>> +	}
> blank line here.
>> +	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);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = iio_hw_consumer_enable(adc->hwc);
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"%s: IIO enable failed (channel %d)\n",
>> +				__func__, chan->channel);
>> +			return ret;
>> +		}
>> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"%s: Conversion failed (channel %d)\n",
>> +				__func__, chan->channel);
>> +			return ret;
>> +		}
>> +
>> +		iio_hw_consumer_disable(adc->hwc);
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		*val = adc->oversamp;
>> +
>> +		return IIO_VAL_INT;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info stm32_dfsdm_info_adc = {
>> +	.read_raw = stm32_dfsdm_read_raw,
>> +	.write_raw = stm32_dfsdm_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	struct stm32_dfsdm_adc *adc = arg;
>> +	struct regmap *regmap = adc->dfsdm->regmap;
>> +	unsigned int status;
>> +
>> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
>> +
>> +	if (status & DFSDM_ISR_REOCF_MASK) {
>> +		/* read the data register clean the IRQ status */
>> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
>> +		complete(&adc->completion);
>> +	}
>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> What's this one?  Might want a comment given it's an irq you basically eat.
Yes  at least an error message that to inform on an overrun.
>> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
>> +				   DFSDM_ICR_CLRROVRF_MASK,
>> +				   DFSDM_ICR_CLRROVRF_MASK);
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +
>> +	return stm32_dfsdm_start_conv(adc);
>> +}
>> +
>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +
>> +	stm32_dfsdm_stop_conv(adc);
> blank line.
>> +	return 0;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>> +	.postenable = &stm32_dfsdm_postenable,
>> +	.predisable = &stm32_dfsdm_predisable,
>> +};
>> +
>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>> +					 struct iio_chan_spec *chan,
>> +					 int ch_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[ch_idx];
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = ch_idx;
>> +
>> +	/*
>> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
>> +	 */
>> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>> +
>> +	ch->scan_type.sign = 'u';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +	adc->ch_id = ch->channel;
>> +
>> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
>> +					  &adc->dfsdm->ch_list[ch->channel]);
>> +}
>> +
>> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
>> +{
>> +	struct iio_chan_spec *channels;
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	unsigned int num_ch;
>> +	int ret, chan_idx;
>> +
>> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
>> +					     "st,adc-channels");
>> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>> +		return num_ch < 0 ? num_ch : -EINVAL;
>> +	}
>> +
>> +	/*
>> +	 * Number of channel per filter is temporary limited to 1.
>> +	 * Restriction should be cleaned with scan mode
>> +	 */
>> +	if (num_ch > 1) {
>> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Bind to SD modulator IIO device */
>> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>> +	if (IS_ERR(adc->hwc))
>> +		return -EPROBE_DEFER;
>> +
>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>> +				GFP_KERNEL);
>> +	if (!channels)
>> +		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 free_hwc;
>> +	}
>> +
>> +	indio_dev->num_channels = num_ch;
>> +	indio_dev->channels = channels;
>> +
>> +	return 0;
>> +
>> +free_hwc:
>> +	iio_hw_consumer_free(adc->hwc);
> Given you have to free this in the error path, I would imagine you will
> need a free somewhere in the main remove path?  Or just create a devm
> version of iio_hw_consumer_alloc.  It will be useful in the long run.
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
>> +	{ .compatible = "st,stm32-dfsdm-adc"},
>> +	{}
>> +};
>> +
>> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct stm32_dfsdm_adc *adc;
>> +	struct device_node *np = dev->of_node;
>> +	struct iio_dev *iio;
>> +	char *name;
>> +	int ret, irq, val;
>> +
>> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
>> +	if (IS_ERR(iio)) {
>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>> +		return PTR_ERR(iio);
>> +	}
>> +
>> +	adc = iio_priv(iio);
>> +	if (IS_ERR(adc)) {
>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>> +		return PTR_ERR(adc);
>> +	}
>> +	adc->dfsdm = dev_get_drvdata(dev->parent);
>> +
>> +	iio->dev.parent = dev;
>> +	iio->dev.of_node = np;
>> +	iio->info = &stm32_dfsdm_info_adc;
>> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
>> +
>> +	platform_set_drvdata(pdev, adc);
>> +
>> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
>> +	if (ret != 0) {
>> +		dev_err(dev, "Missing reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
> not freed.  Maybe devm_kzalloc
>> +	if (!name)
>> +		return -ENOMEM;
>> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
>> +	iio->name = name;
>> +
>> +	/*
>> +	 * In a first step IRQs generated for channels are not treated.
>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>> +	 */
>> +	irq = platform_get_irq(pdev, 0);
>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>> +			       0, pdev->name, adc);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to request IRQ\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set filter order\n");
>> +		return ret;
>> +	}
>> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>> +	if (!ret)
>> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
>> +
>> +	ret = stm32_dfsdm_adc_chan_init(iio);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	init_completion(&adc->completion);
>> +
>> +	return iio_device_register(iio);
>> +}
>> +
>> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	iio_device_unregister(iio);
> If all you have is this in remove, you can probably get away with
> devm_iio_device_register and get rid of the remove entirely.
>> +
>> +	return 0;
>> +}
>> +
>> +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");
>> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
>> new file mode 100644
>> index 0000000..488e456
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
>> @@ -0,0 +1,658 @@
>> +/*
>> + * This file is part the core part STM32 DFSDM 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/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +struct stm32_dfsdm_dev_data {
>> +	unsigned int num_filters;
>> +	unsigned int num_channels;
>> +	const struct regmap_config *regmap_cfg;
>> +};
>> +
>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>> +#define STM32H7_DFSDM_NUM_CHANNELS	8
>> +
>> +#define DFSDM_MAX_INT_OVERSAMPLING 256
>> +
>> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
>> +
>> +#define DFSDM_MAX_RES BIT(31)
>> +#define DFSDM_DATA_RES BIT(23)
>> +
>> +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 = 0x2B8,
>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>> +	.fast_io = true,
>> +};
>> +
>> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
>> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
>> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>> +};
>> +
>> +struct dfsdm_priv {
>> +	struct platform_device *pdev; /* platform device*/
>> +
>> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
>> +
>> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
>> +	atomic_t n_active_ch;	/* number of current active channels */
>> +
>> +	/* Clock */
>> +	struct clk *clk; /* DFSDM clock */
>> +	struct clk *aclk; /* audio clock */
>> +};
>> +
>> +/**
>> + * stm32_dfsdm_set_osrs - compute filter parameters.
> Naming would suggest it's more specific than this.
> Setting over sampling ratios?
Right, it is a computation not a set.
>> + *
>> + * Enable interface if n_active_ch is not null.
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fast: Fast mode enabled or disabled
>> + * @oversamp: Expected oversampling between filtered sample and SD input stream
>> + */
>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>> +			 unsigned int oversamp)
>> +{
>> +	unsigned int i, d, fosr, iosr;
>> +	u64 res;
>> +	s64 delta;
>> +	unsigned int m = 1;	/* multiplication factor */
>> +	unsigned int p = fl->ford;	/* filter order (ford) */
>> +
>> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
>> +	/*
>> +	 * This function tries to compute filter oversampling and integrator
>> +	 * oversampling, base on oversampling ratio requested by user.
>> +	 *
>> +	 * Decimation d depends on the filter order and the oversampling ratios.
>> +	 * ford: filter order
>> +	 * fosr: filter over sampling ratio
>> +	 * iosr: integrator over sampling ratio
>> +	 */
>> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
>> +		m = 2;
>> +		p = 2;
>> +	}
>> +
>> +	/*
>> +	 * Looks for filter and integrator oversampling ratios which allows
>> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
>> +				d = fosr * (iosr + 3) + 2;
>> +			else
>> +				d = fosr * (iosr - 1 + p) + p;
>> +
>> +			if (d > oversamp)
>> +				break;
>> +			else if (d != oversamp)
>> +				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 > DFSDM_MAX_RES)
>> +					break;
>> +			}
>> +			if (res > DFSDM_MAX_RES)
>> +				continue;
>> +			res = res * (u64)m * (u64)iosr;
>> +			if (res > DFSDM_MAX_RES)
>> +				continue;
>> +
>> +			delta = res - DFSDM_DATA_RES;
>> +
>> +			if (res >= fl->res) {
>> +				fl->res = res;
>> +				fl->fosr = fosr;
>> +				fl->iosr = iosr;
>> +				fl->fast = fast;
>> +				pr_debug("%s: fosr = %d, iosr = %d\n",
>> +					 __func__, fl->fosr, fl->iosr);
>> +			}
>> +
>> +			if (!delta)
>> +				return 0;
>> +		}
>> +	}
>> +
>> +	if (!fl->fosr)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
>> + *
>> + * Enable interface if n_active_ch is not null.
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + */
>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	struct device *dev = &priv->pdev->dev;
>> +	unsigned int clk_div = priv->spi_clk_out_div;
>> +	int ret;
>> +
>> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
>> +		/* Enable clocks */
>> +		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");
>> +				goto disable_clk;
>> +			}
>> +		}
>> +
>> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
>> +		if (ret < 0)
>> +			goto disable_aclk;
>> +
>> +		/* Global enable of DFSDM interface */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>> +					 DFSDM_CHCFGR1_DFSDMEN(1));
>> +		if (ret < 0)
>> +			goto disable_aclk;
>> +	}
>> +
>> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
>> +		atomic_read(&priv->n_active_ch));
>> +
>> +	return 0;
>> +
>> +disable_aclk:
>> +	clk_disable_unprepare(priv->aclk);
>> +disable_clk:
>> +	clk_disable_unprepare(priv->clk);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
>> + *
>> + * Disable interface if n_active_ch is null
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + */
>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	int ret;
>> +
>> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
>> +		/* Global disable of DFSDM interface */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>> +					 DFSDM_CHCFGR1_DFSDMEN(0));
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		/* Stop SPI CLKOUT */
>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
>> +		if (ret < 0)
>> +			return ret;
>> +
>> +		/* Disable clocks */
>> +		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));
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_channel
>> + *	Start DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: Channel index.
>> + */
>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>> +{
>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>> +				  DFSDM_CHCFGR1_CHEN_MASK,
>> +				  DFSDM_CHCFGR1_CHEN(1));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_channel
>> + *	Stop DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: Channel index.
>> + */
>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>> +{
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>> +			   DFSDM_CHCFGR1_CHEN_MASK,
>> +			   DFSDM_CHCFGR1_CHEN(0));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_chan_configure
>> + *	Configure DFSDM IP channels and associated interface.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @ch_id: channel index.
>> + */
>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>> +			       struct stm32_dfsdm_channel *ch)
>> +{
>> +	unsigned int id = ch->id;
>> +	struct regmap *regmap = dfsdm->regmap;
>> +	int ret;
>> +
>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				 DFSDM_CHCFGR1_SITP_MASK,
>> +				 DFSDM_CHCFGR1_SITP(ch->type));
>> +	if (ret < 0)
>> +		return ret;
> Blank line here and in similar places makes it easier for my
> eyes to parse at least...
> I'd also like to see some docs in here, not all of these
> are self explanatory.
I will apply recommendation in my whole code for next time
>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
>> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
>> +	if (ret < 0)
>> +		return ret;
>> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
>> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter index.
>> + */
>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>> +{
>> +	int ret;
>> +
>> +	/* Enable filter */
>> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Start conversion */
>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +				  DFSDM_CR1_RSWSTART_MASK,
>> +				  DFSDM_CR1_RSWSTART(1));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter index.
>> + */
>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>> +{
>> +	/* Mask IRQ for regular conversion achievement*/
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>> +	/* Disable conversion */
>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
>> + *	to channel.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: channel index.
>> + * @fl_id: Filter index.
>> + */
>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				 unsigned int ch_id)
>> +{
>> +	struct regmap *regmap = dfsdm->regmap;
>> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
>> +	int ret;
>> +
>> +	/* Average integrator oversampling */
>> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
>> +				 DFSDM_FCR_IOSR(fl->iosr));
>> +
>> +	/* Filter order and Oversampling */
> Please handle each error properly as it happens rather than mudling onwards.
> If there is reason for this odd construction, then document it clearly.
If you mention the checks on ret value that are missing at end of
functions, yes dirty code to fix.

>> +	if (!ret)
>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>> +					 DFSDM_FCR_FOSR_MASK,
>> +					 DFSDM_FCR_FOSR(fl->fosr));
>> +
>> +	if (!ret)
>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>> +					 DFSDM_FCR_FORD_MASK,
>> +					 DFSDM_FCR_FORD(fl->ford));
>> +
>> +	/* If only one channel no scan mode supported for the moment */
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>> +				 DFSDM_CR1_RCH_MASK,
>> +				 DFSDM_CR1_RCH(ch_id));
>> +
>> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>> +					 DFSDM_CR1_RSYNC_MASK,
>> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
>> +}
>> +
>> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
>> +	.owner = THIS_MODULE,
>> +};
>> +
>> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
>> +					 struct stm32_dfsdm *dfsdm)
>> +{
>> +	/*
>> +	 * To be able to use buffer consumer interface a trigger is needed.
>> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
>> +	 * sense to define the serial interface ( SPI or manchester) as
>> +	 * trigger source.
> It's not actually the case that you have to have a triggrer.
> There are plenty of drivers (particularly ones with hardware buffering)
> where there is no trigger envolved.  That's not to say it doesn't make sense
> here.
> 
> I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
> somewhat...
>> +	 */
>> +
>> +	struct iio_trigger *trig;
>> +	int ret;
>> +
>> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
>> +	if (!trig)
>> +		return -ENOMEM;
>> +
>> +	trig->dev.parent = pdev->dev.parent;
>> +	trig->ops = &dfsdm_trigger_ops;
>> +
>> +	iio_trigger_set_drvdata(trig, dfsdm);
>> +
>> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
>> +	if (ret)
>> +		return ret;
> Just return ret in all cases.
>> +
>> +	return 0;
>> +}
>> +
>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>> +				 struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec *chan, int chan_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>> +	struct stm32_dfsdm_channel *df_ch;
>> +	const char *of_str;
>> +	int ret, val;
>> +
>> +	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;
>> +	}
>> +
>> +	df_ch =  &dfsdm->ch_list[ch->channel];
> Extra space on the line above.
>> +	df_ch->id = ch->channel;
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-types", chan_idx,
>> +					    &of_str);
>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
>> +	if (ret < 0 || val < 0)
>> +		df_ch->type = 0;
>> +	else
>> +		df_ch->type = val;
>> +
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-clk-src", chan_idx,
>> +					    &of_str);
>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
>> +	if (ret < 0 || val < 0)
>> +		df_ch->src = 0;
>> +	else
>> +		df_ch->src = val;
>> +
>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>> +					 "st,adc-alt-channel", chan_idx,
>> +					 &df_ch->alt_si);
>> +	if (ret < 0)
>> +		df_ch->alt_si = 0;
>> +
>> +	return 0;
>> +}
>> +
>> +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;
>> +	unsigned long clk_freq;
>> +	unsigned int spi_freq, rem;
>> +	int ret;
>> +
>> +	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->dfsdm.phys_base = res->start;
>> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
>> +
>> +	/* Source clock */
>> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
>> +	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");
>> +	if (IS_ERR(priv->aclk))
>> +		priv->aclk = NULL;
>> +
>> +	if (priv->aclk)
>> +		clk_freq = clk_get_rate(priv->aclk);
>> +	else
>> +		clk_freq = clk_get_rate(priv->clk);
>> +
>> +	/* SPI clock freq */
>> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
>> +				   &spi_freq);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
>> +		return ret;
>> +	}
>> +
>> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
>> +	priv->dfsdm.spi_master_freq = spi_freq;
>> +
>> +	if (rem) {
>> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
>> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
>> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
>> +	}
>> +
>> +	return 0;
>> +};
>> +
>> +static const struct of_device_id stm32_dfsdm_of_match[] = {
>> +	{
>> +		.compatible = "st,stm32h7-dfsdm",
>> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
>> +	struct stm32_dfsdm *dfsdm;
>> +	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_dfsdm_dev_data *)of_id->data;
>> +	dfsdm = &priv->dfsdm;
>> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
>> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
>> +	if (!dfsdm->fl_list)
>> +		return -ENOMEM;
>> +
>> +	dfsdm->num_fls = dev_data->num_filters;
>> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
>> +				      sizeof(*dfsdm->ch_list),
>> +				      GFP_KERNEL);
>> +	if (!dfsdm->ch_list)
>> +		return -ENOMEM;
>> +	dfsdm->num_chs = dev_data->num_channels;
>> +
>> +	ret = stm32_dfsdm_parse_of(pdev, priv);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
>> +					    &stm32h7_dfsdm_regmap_cfg);
>> +	if (IS_ERR(dfsdm->regmap)) {
>> +		ret = PTR_ERR(dfsdm->regmap);
>> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
>> +			__func__, ret);
>> +		return ret;
>> +	}
>> +
>> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
>> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
>> +
>> +		fl->id = i;
> I'd like a comment on why this is needed...
 to  be cleaned.
>> +	}
>> +
>> +	platform_set_drvdata(pdev, dfsdm);
>> +
>> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
>> +}
>> +
>> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
>> new file mode 100644
>> index 0000000..bb7d74f
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm.h
>> @@ -0,0 +1,371 @@
>> +/*
>> + * This file is part of STM32 DFSDM 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__H
>> +#define MDF_STM32_DFSDM__H
>> +
>> +#include <linux/bitfield.h>
>> +
>> +#include <linux/iio/iio.h>
>> +/*
>> + * STM32 DFSDM - global register map
>> + * ________________________________________________________
>> + * | Offset |                 Registers block             |
>> + * --------------------------------------------------------
>> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
>> + * --------------------------------------------------------
>> + * | 0x020  |                CHANNEL 1                    |
>> + * --------------------------------------------------------
>> + * | ...    |                .....                        |
>> + * --------------------------------------------------------
>> + * | 0x0E0  |                CHANNEL 7                    |
>> + * --------------------------------------------------------
>> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
>> + * --------------------------------------------------------
>> + * | 0x200  |                FILTER  1                    |
>> + * --------------------------------------------------------
>> + * | 0x300  |                FILTER  2                    |
>> + * --------------------------------------------------------
>> + * | 0x400  |                FILTER  3                    |
>> + * --------------------------------------------------------
>> + */
>> +
>> +/*
>> + * Channels register 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 register 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)
>> +
>> +/* 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,
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
>> + * TODO: complete structure.
> nice :) RFC I guess :)
>> + * @id:		filetr ID,
>> + */
>> +struct stm32_dfsdm_filter {
>> +	unsigned int id;
>> +	unsigned int iosr; /* integrator oversampling */
>> +	unsigned int fosr; /* filter oversampling */
>> +	enum stm32_dfsdm_sinc_order ford;
>> +	u64 res; /* output sample resolution */
>> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
>> +	unsigned int fast; /* filter fast mode */
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
>> + * TODO: complete structure.
>> + * @id:		filetr ID,
> filter
>> + */
>> +struct stm32_dfsdm_channel {
>> +	unsigned int id;   /* id of the channel */
>> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
>> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
>> +	unsigned int alt_si; /* use alternative serial input interface */
>> +};
>> +
>> +/**
>> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
>> + * @base:	control registers base cpu addr
>> + * @phys_base:	DFSDM IP register physical address.
>> + * @fl_list:	filter resources list
>> + * @num_fl:	number of filter resources available
>> + * @ch_list:	channel resources list
>> + * @num_chs:	number of channel resources available
>> + */
>> +struct stm32_dfsdm {
>> +	void __iomem	*base;
>> +	phys_addr_t	phys_base;
>> +	struct regmap *regmap;
>> +	struct stm32_dfsdm_filter *fl_list;
>> +	unsigned int num_fls;
>> +	struct stm32_dfsdm_channel *ch_list;
>> +	unsigned int num_chs;
>> +	unsigned int spi_master_freq;
>> +};
>> +
>> +struct stm32_dfsdm_str2field {
>> +	const char	*name;
>> +	unsigned int	val;
>> +};
>> +
>> +/* DFSDM channel serial interface type */
>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
>> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
>> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
>> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
>> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
>> +	{ 0, 0},
>> +};
>> +
>> +/* DFSDM channel serial spi clock source */
>> +enum stm32_dfsdm_spi_clk_src {
>> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
>> +};
>> +
>> +/* DFSDM channel clock source */
>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
>> +	/* External SPI clock (CLKIN x) */
>> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
>> +	/* Internal SPI clock (CLKOUT) */
>> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
>> +	/* Internal SPI clock divided by 2 (falling edge) */
>> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
>> +	/* Internal SPI clock divided by 2 (falling edge) */
>> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
>> +	{ 0, 0 },
>> +};
>> +
>> +/* DFSDM Serial interface trigger name  */
>> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
>> +
>> +static inline int stm32_dfsdm_str2val(const char *str,
>> +				      const struct stm32_dfsdm_str2field *list)
>> +{
>> +	const struct stm32_dfsdm_str2field *p = list;
>> +
>> +	for (p = list; p && p->name; p++) {
>> +		if (!strcmp(p->name, str))
>> +			return p->val;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>> +			 unsigned int oversamp);
>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
>> +
>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				 unsigned int ch_id);
>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>> +
>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>> +			       struct stm32_dfsdm_channel *ch);
>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>> +
>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>> +				 struct iio_dev *indio_dev,
>> +				 struct iio_chan_spec *chan, int chan_idx);
>> +
>> +#endif
>>
> 
> --
> 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] 109+ messages in thread

* Re: [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
  2017-03-19 22:38       ` Jonathan Cameron
  (?)
@ 2017-03-20 11:29         ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:29 UTC (permalink / raw)
  To: Jonathan Cameron, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, Olivier MOYSAN, kernel, linux-iio,
	Maxime Coquelin, linux-arm-kernel, Alexandre TORGUE

Please find my comments in-line

Thanks and Regards,
Arnaud

On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add DFSDM driver to handle PDM audio microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> So key element here is that we really need to have a proper way of
> doing DMA buffer consumers from IIO (as you said in your cover letter).
> 
> Not entirely obvious how to do this.  Would like some input from Lars
> if possible.  I'm afraid I don't have an suitable hardware to try anything
> out on (or the time really!).  This will obviously be quite different from
> the single scan stuff you have seen is there at the moment.
I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
DMA. I suppose that problematics are similar. Perhaps the extra
constrains in DFSDM is the in-kernel API used to get the data.

> 
> Whether this whole approach makes sense vs doing the dma in the alsa driver
> isn't clear to me.
> 
> On the plus side, presumably we'll get love dma ADC support for free
> as part of doing it this way ;)
> 
> It's been a while since I looked at the IIO dma buffer stuff at all. Will
> try and refresh my memory sometime this week.

Ok, i will wait your feedback (and Lars's one) before updating my code
for next version.
> 
> Jonathan
>> ---
>>  drivers/iio/adc/Kconfig                   |  13 +
>>  drivers/iio/adc/Makefile                  |   1 +
>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>  4 files changed, 775 insertions(+)
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 3e0eb11..c108933 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-dfsdm-adc.
>>  
>> +config STM32_DFSDM_AUDIO
>> +	tristate "STMicroelectronics STM32 dfsdm audio
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select STM32_DFSDM_CORE
>> +	select REGMAP_MMIO
>> +	select IIO_BUFFER_DMAENGINE
>> +	help
>> +	  Select this option to support Audio PDM micophone for
>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-dfsdm-audio.
>> +
>>  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 161f271..79f975d 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>> new file mode 100644
>> index 0000000..115ef8f
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>> @@ -0,0 +1,720 @@
>> +/*
>> + * This file is the ADC part of of the STM32 DFSDM driver
>> + *
>> + * Copyright (C) 2017, 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/dmaengine.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>> +
>> +struct stm32_dfsdm_audio {
>> +	struct stm32_dfsdm *dfsdm;
>> +	unsigned int fl_id;
>> +	unsigned int ch_id;
>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>> +
>> +	u8 *rx_buf;
>> +	unsigned int bufi; /* Buffer current position */
>> +	unsigned int buf_sz; /* Buffer size */
>> +
>> +	struct dma_chan	*dma_chan;
>> +	dma_addr_t dma_buf;
>> +
>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>> +	void *cb_priv;
>> +};
>> +
>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>> +
>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>> +				    const struct iio_chan_spec *chan, char *buf)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>> +}
>> +
>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>> +				    const struct iio_chan_spec *chan,
>> +				    const char *buf, size_t len)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>> +	unsigned int spi_freq = pdmc->spi_freq;
>> +	unsigned int sample_freq;
>> +	int ret;
>> +
>> +	ret = kstrtoint(buf, 0, &sample_freq);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>> +	if (!sample_freq)
>> +		return -EINVAL;
>> +
>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>> +
>> +	if (spi_freq % sample_freq)
>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>> +			 spi_freq / (spi_freq / sample_freq));
>> +
>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			"Not able to find filter parameter that match!\n");
>> +		return ret;
>> +	}
>> +	pdmc->sample_freq = sample_freq;
>> +
>> +	return len;
>> +}
>> +
>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>> +				      const struct iio_chan_spec *chan,
>> +				      char *buf)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>> +}
>> +
>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>> +				      const struct iio_chan_spec *chan,
>> +				      const char *buf, size_t len)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>> +	unsigned int sample_freq = pdmc->sample_freq;
>> +	unsigned int spi_freq;
>> +	int ret;
>> +
>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>> +		return -EPERM;
>> +
>> +	ret = kstrtoint(buf, 0, &spi_freq);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>> +	if (!spi_freq)
>> +		return -EINVAL;
>> +
>> +	if (sample_freq) {
>> +		if (spi_freq % sample_freq)
>> +			dev_warn(&indio_dev->dev,
>> +				 "Sampling rate not accurate (%d)\n",
>> +				 spi_freq / (spi_freq / sample_freq));
>> +
>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"No filter parameters that match!\n");
>> +			return ret;
>> +		}
>> +	}
>> +	pdmc->spi_freq = spi_freq;
>> +
>> +	return len;
>> +}
>> +
>> +/*
>> + * Define external info for SPI Frequency and audio sampling rate that can be
>> + * configured by ASoC driver through consumer.h API
>> + */
>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>> +	/* filter oversampling: Post filter oversampling ratio */
>> +	{
>> +		.name = "audio_sampling_rate",
>> +		.shared = IIO_SHARED_BY_TYPE,
>> +		.read = dfsdm_audio_get_rate,
>> +		.write = dfsdm_audio_set_rate,
>> +	},
>> +	/* data_right_bit_shift : Filter output data shifting */
>> +	{
>> +		.name = "spi_clk_freq",
>> +		.shared = IIO_SHARED_BY_TYPE,
>> +		.read = dfsdm_audio_get_spiclk,
>> +		.write = dfsdm_audio_set_spiclk,
>> +	},
>> +	{},
>> +};
>> +
>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>> +{
>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_dfsdm;
>> +
>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>> +					   pdmc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	/* Enable DMA transfer*/
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Enable conversion triggered by SPI clock*/
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	return 0;
>> +
>> +stop_channels:
>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>> +stop_dfsdm:
>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>> +
>> +	return ret;
>> +}
>> +
>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>> +{
>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>> +
>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>> +
>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>> +}
>> +
>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>> +				     unsigned int val)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>> +
>> +	/*
>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>> +	 * There should be :
>> +	 * - always one buffer (period) DMA is working on
>> +	 * - one buffer (period) driver pushed to ASoC side ().
>> +	 */
>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>> +	pdmc->buf_sz = watermark * 2;
>> +
>> +	return 0;
>> +}
>> +
>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>> +				 struct iio_trigger *trig)
>> +{
>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>> +		return 0;
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>> +	.driver_module = THIS_MODULE,
>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = arg;
>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>> +	unsigned int status;
>> +
>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>> +
>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>> +				   DFSDM_ICR_CLRROVRF_MASK,
>> +				   DFSDM_ICR_CLRROVRF_MASK);
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>> +{
>> +	struct dma_tx_state state;
>> +	enum dma_status status;
>> +
>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>> +				     pdmc->dma_chan->cookie,
>> +				     &state);
>> +	if (status == DMA_IN_PROGRESS) {
>> +		/* Residue is size in bytes from end of buffer */
>> +		unsigned int i = pdmc->buf_sz - state.residue;
>> +		unsigned int size;
>> +
>> +		/* Return available bytes */
>> +		if (i >= pdmc->bufi)
>> +			size = i - pdmc->bufi;
>> +		else
>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>> +
>> +		return size;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>> +{
>> +	struct iio_dev *indio_dev = data;
>> +
>> +	iio_trigger_poll_chained(indio_dev->trig);
> This shouldn't be going through the trigger infrastructure at all really.
> I'm not 100% sure what doing so is gaining you...
Yes i will clean trigger part and call directly the ASoC callback here.
>> +}
>> +
>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct dma_async_tx_descriptor *desc;
>> +	dma_cookie_t cookie;
>> +	int ret;
>> +
>> +	if (!pdmc->dma_chan)
>> +		return -EINVAL;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>> +
>> +	/* Prepare a DMA cyclic transaction */
>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>> +					 pdmc->dma_buf,
>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>> +					 DMA_DEV_TO_MEM,
>> +					 DMA_PREP_INTERRUPT);
>> +	if (!desc)
>> +		return -EBUSY;
>> +
>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>> +	desc->callback_param = indio_dev;
>> +
>> +	cookie = dmaengine_submit(desc);
>> +	ret = dma_submit_error(cookie);
>> +	if (ret) {
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +		return ret;
>> +	}
>> +
>> +	/* Issue pending DMA requests */
>> +	dma_async_issue_pending(pdmc->dma_chan);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	/* Reset pdmc buffer index */
>> +	pdmc->bufi = 0;
>> +
>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>> +		goto err_stop_conv;
>> +	}
>> +
>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>> +		goto err_stop_dma;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_stop_dma:
>> +	if (pdmc->dma_chan)
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +err_stop_conv:
>> +	stm32_dfsdm_stop_conv(pdmc);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>> +	if (ret < 0)
>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>> +
>> +	if (pdmc->dma_chan)
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +
>> +	stm32_dfsdm_stop_conv(pdmc);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>> +	.postenable = &stm32_dfsdm_postenable,
>> +	.predisable = &stm32_dfsdm_predisable,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>> +{
> OK. So now I kind of understand what the trigger provided in the adc driver
> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
> a hack.  We need fullblown DMA buffer consumers to do this right.
Yes exactly.
> 
> Probably best person to comment on that is Lars. 
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *indio_dev = pf->indio_dev;
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	size_t old_pos;
>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>> +
>> +	/*
>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>> +	 * an interface to push data samples per samples.
>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>> +	 * and interface is hacked using a private callback registered by ASoC.
>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>> +	 * support in IIO.
>> +	 */
>> +
>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>> +		pdmc->bufi, available);
>> +	old_pos = pdmc->bufi;
>> +	while (available >= indio_dev->scan_bytes) {
>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>> +
>> +		/* Mask 8 LSB that contains the channel ID */
>> +		*buffer &= 0xFFFFFF00;
>> +		available -= indio_dev->scan_bytes;
>> +		pdmc->bufi += indio_dev->scan_bytes;
>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>> +			if (pdmc->cb)
>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>> +			pdmc->bufi = 0;
>> +			old_pos = 0;
>> +		}
>> +	}
>> +	if (pdmc->cb)
>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>> +				pdmc->cb_priv);
>> +
>> +	iio_trigger_notify_done(indio_dev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_get_buff_cb - register a callback
>> + *	that will be called when DMA transfer period is achieved.
>> + *
>> + * @iio_dev: Handle to IIO device.
>> + * @cb: pointer to callback function.
>> + *	@data: pointer to data buffer
>> + *	@size: size in byte of the data buffer
>> + *	@private: pointer to consumer private structure
>> + * @private: pointer to consumer private structure
>> + */
>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>> +			    int (*cb)(const void *data, size_t size,
>> +				      void *private),
>> +			    void *private)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc;
>> +
>> +	if (!iio_dev)
>> +		return -EINVAL;
>> +	pdmc = iio_priv(iio_dev);
>> +
>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>> +		return -EINVAL;
>> +
>> +	pdmc->cb = cb;
>> +	pdmc->cb_priv = private;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>> +
>> +/**
>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>> + *
>> + * @iio_dev: Handle to IIO device.
>> + */
>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc;
>> +
>> +	if (!iio_dev)
>> +		return -EINVAL;
>> +	pdmc = iio_priv(iio_dev);
>> +
>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>> +		return -EINVAL;
>> +	pdmc->cb = NULL;
>> +	pdmc->cb_priv = NULL;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>> +
>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>> +{
>> +	struct iio_chan_spec *ch;
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>> +	if (!ch)
>> +		return -ENOMEM;
>> +
>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = 0;
>> +	ch->ext_info = dfsdm_adc_ext_info;
>> +
>> +	ch->scan_type.sign = 's';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +
>> +	pdmc->ch_id = ch->channel;
>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>> +
>> +	indio_dev->num_channels = 1;
>> +	indio_dev->channels = ch;
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>> +	{}
>> +};
>> +
>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct dma_slave_config config;
>> +	int ret;
>> +
>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>> +	if (!pdmc->dma_chan)
>> +		return -EINVAL;
>> +
>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>> +					 DFSDM_DMA_BUFFER_SIZE,
>> +					 &pdmc->dma_buf, GFP_KERNEL);
>> +	if (!pdmc->rx_buf) {
>> +		ret = -ENOMEM;
>> +		goto err_release;
>> +	}
>> +
>> +	/* Configure DMA channel to read data register */
>> +	memset(&config, 0, sizeof(config));
>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +
>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>> +	if (ret)
>> +		goto err_free;
>> +
>> +	return 0;
>> +
>> +err_free:
>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>> +			  pdmc->rx_buf, pdmc->dma_buf);
>> +err_release:
>> +	dma_release_channel(pdmc->dma_chan);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct stm32_dfsdm_audio *pdmc;
>> +	struct device_node *np = dev->of_node;
>> +	struct iio_dev *iio;
>> +	char *name;
>> +	int ret, irq, val;
>> +
>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>> +	if (IS_ERR(iio)) {
>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>> +		return PTR_ERR(iio);
>> +	}
>> +
>> +	pdmc = iio_priv(iio);
>> +	if (IS_ERR(pdmc)) {
>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>> +		return PTR_ERR(pdmc);
>> +	}
>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>> +
>> +	iio->name = np->name;
>> +	iio->dev.parent = dev;
>> +	iio->dev.of_node = np;
>> +	iio->info = &stm32_dfsdm_info_pdmc;
>> +	iio->modes = INDIO_DIRECT_MODE;
>> +
>> +	platform_set_drvdata(pdev, pdmc);
>> +
>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>> +	if (ret != 0) {
>> +		dev_err(dev, "Missing reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>> +	if (!name)
>> +		return -ENOMEM;
>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>> +	iio->name = name;
>> +
>> +	/*
>> +	 * In a first step IRQs generated for channels are not treated.
>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>> +	 */
>> +	irq = platform_get_irq(pdev, 0);
>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>> +			       0, pdev->name, pdmc);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to request IRQ\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set filter order\n");
>> +		return ret;
>> +	}
>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>> +	if (!ret)
>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>> +
>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "DMA request failed\n");
>> +		return ret;
>> +	}
>> +
>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>> +
>> +	ret = iio_triggered_buffer_setup(iio,
>> +					 &iio_pollfunc_store_time,
>> +					 &stm32_dfsdm_audio_trigger_handler,
>> +					 &stm32_dfsdm_buffer_setup_ops);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>> +		goto err_dma_disable;
>> +	}
>> +
>> +	ret = iio_device_register(iio);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>> +		goto err_buffer_cleanup;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_buffer_cleanup:
>> +	iio_triggered_buffer_cleanup(iio);
>> +
>> +err_dma_disable:
>> +	if (pdmc->dma_chan) {
>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>> +				  DFSDM_DMA_BUFFER_SIZE,
>> +				  pdmc->rx_buf, pdmc->dma_buf);
>> +		dma_release_channel(pdmc->dma_chan);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>> +
>> +	iio_device_unregister(iio);
> Same as in the other driver. Can just use the devm_iio_device_register
> version and drop remove.
oops missing the previous comment... sorry.
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>> +	.driver = {
>> +		.name = "stm32-dfsdm-audio",
>> +		.of_match_table = stm32_dfsdm_audio_match,
>> +	},
>> +	.probe = stm32_dfsdm_audio_probe,
>> +	.remove = stm32_dfsdm_audio_remove,
>> +};
>> +module_platform_driver(stm32_dfsdm_audio_driver);
>> +
>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>> new file mode 100644
>> index 0000000..9b41b28
>> --- /dev/null
>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>> @@ -0,0 +1,41 @@
>> +/*
>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>> + *
>> + * 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 STM32_DFSDM_AUDIO_H
>> +#define STM32_DFSDM_AUDIO_H
>> +
>> +/**
>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>> + * @dev:	Pointer to client device.
>> + * @indio_dev:	Device on which the channel exists.
>> + * @cb:		Callback function.
>> + *		@data:  pointer to data buffer
>> + *		@size: size of the data buffer in bytes
>> + * @private:	Private data passed to callback.
>> + *
>> + */
>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>> +			    int (*cb)(const void *data, size_t size,
>> +				      void *private),
>> +			    void *private);
>> +/**
>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>> + * @indio_dev:	Device on which the channel exists.
>> + */
>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>> +
>> +#endif
>>
> 

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

* Re: [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-20 11:29         ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:29 UTC (permalink / raw)
  To: Jonathan Cameron, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

Please find my comments in-line

Thanks and Regards,
Arnaud

On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add DFSDM driver to handle PDM audio microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> So key element here is that we really need to have a proper way of
> doing DMA buffer consumers from IIO (as you said in your cover letter).
> 
> Not entirely obvious how to do this.  Would like some input from Lars
> if possible.  I'm afraid I don't have an suitable hardware to try anything
> out on (or the time really!).  This will obviously be quite different from
> the single scan stuff you have seen is there at the moment.
I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
DMA. I suppose that problematics are similar. Perhaps the extra
constrains in DFSDM is the in-kernel API used to get the data.

> 
> Whether this whole approach makes sense vs doing the dma in the alsa driver
> isn't clear to me.
> 
> On the plus side, presumably we'll get love dma ADC support for free
> as part of doing it this way ;)
> 
> It's been a while since I looked at the IIO dma buffer stuff at all. Will
> try and refresh my memory sometime this week.

Ok, i will wait your feedback (and Lars's one) before updating my code
for next version.
> 
> Jonathan
>> ---
>>  drivers/iio/adc/Kconfig                   |  13 +
>>  drivers/iio/adc/Makefile                  |   1 +
>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>  4 files changed, 775 insertions(+)
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 3e0eb11..c108933 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-dfsdm-adc.
>>  
>> +config STM32_DFSDM_AUDIO
>> +	tristate "STMicroelectronics STM32 dfsdm audio
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select STM32_DFSDM_CORE
>> +	select REGMAP_MMIO
>> +	select IIO_BUFFER_DMAENGINE
>> +	help
>> +	  Select this option to support Audio PDM micophone for
>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-dfsdm-audio.
>> +
>>  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 161f271..79f975d 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>> new file mode 100644
>> index 0000000..115ef8f
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>> @@ -0,0 +1,720 @@
>> +/*
>> + * This file is the ADC part of of the STM32 DFSDM driver
>> + *
>> + * Copyright (C) 2017, 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/dmaengine.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>> +
>> +struct stm32_dfsdm_audio {
>> +	struct stm32_dfsdm *dfsdm;
>> +	unsigned int fl_id;
>> +	unsigned int ch_id;
>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>> +
>> +	u8 *rx_buf;
>> +	unsigned int bufi; /* Buffer current position */
>> +	unsigned int buf_sz; /* Buffer size */
>> +
>> +	struct dma_chan	*dma_chan;
>> +	dma_addr_t dma_buf;
>> +
>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>> +	void *cb_priv;
>> +};
>> +
>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>> +
>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>> +				    const struct iio_chan_spec *chan, char *buf)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>> +}
>> +
>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>> +				    const struct iio_chan_spec *chan,
>> +				    const char *buf, size_t len)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>> +	unsigned int spi_freq = pdmc->spi_freq;
>> +	unsigned int sample_freq;
>> +	int ret;
>> +
>> +	ret = kstrtoint(buf, 0, &sample_freq);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>> +	if (!sample_freq)
>> +		return -EINVAL;
>> +
>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>> +
>> +	if (spi_freq % sample_freq)
>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>> +			 spi_freq / (spi_freq / sample_freq));
>> +
>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			"Not able to find filter parameter that match!\n");
>> +		return ret;
>> +	}
>> +	pdmc->sample_freq = sample_freq;
>> +
>> +	return len;
>> +}
>> +
>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>> +				      const struct iio_chan_spec *chan,
>> +				      char *buf)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>> +}
>> +
>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>> +				      const struct iio_chan_spec *chan,
>> +				      const char *buf, size_t len)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>> +	unsigned int sample_freq = pdmc->sample_freq;
>> +	unsigned int spi_freq;
>> +	int ret;
>> +
>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>> +		return -EPERM;
>> +
>> +	ret = kstrtoint(buf, 0, &spi_freq);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>> +	if (!spi_freq)
>> +		return -EINVAL;
>> +
>> +	if (sample_freq) {
>> +		if (spi_freq % sample_freq)
>> +			dev_warn(&indio_dev->dev,
>> +				 "Sampling rate not accurate (%d)\n",
>> +				 spi_freq / (spi_freq / sample_freq));
>> +
>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"No filter parameters that match!\n");
>> +			return ret;
>> +		}
>> +	}
>> +	pdmc->spi_freq = spi_freq;
>> +
>> +	return len;
>> +}
>> +
>> +/*
>> + * Define external info for SPI Frequency and audio sampling rate that can be
>> + * configured by ASoC driver through consumer.h API
>> + */
>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>> +	/* filter oversampling: Post filter oversampling ratio */
>> +	{
>> +		.name = "audio_sampling_rate",
>> +		.shared = IIO_SHARED_BY_TYPE,
>> +		.read = dfsdm_audio_get_rate,
>> +		.write = dfsdm_audio_set_rate,
>> +	},
>> +	/* data_right_bit_shift : Filter output data shifting */
>> +	{
>> +		.name = "spi_clk_freq",
>> +		.shared = IIO_SHARED_BY_TYPE,
>> +		.read = dfsdm_audio_get_spiclk,
>> +		.write = dfsdm_audio_set_spiclk,
>> +	},
>> +	{},
>> +};
>> +
>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>> +{
>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_dfsdm;
>> +
>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>> +					   pdmc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	/* Enable DMA transfer*/
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Enable conversion triggered by SPI clock*/
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	return 0;
>> +
>> +stop_channels:
>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>> +stop_dfsdm:
>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>> +
>> +	return ret;
>> +}
>> +
>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>> +{
>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>> +
>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>> +
>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>> +}
>> +
>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>> +				     unsigned int val)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>> +
>> +	/*
>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>> +	 * There should be :
>> +	 * - always one buffer (period) DMA is working on
>> +	 * - one buffer (period) driver pushed to ASoC side ().
>> +	 */
>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>> +	pdmc->buf_sz = watermark * 2;
>> +
>> +	return 0;
>> +}
>> +
>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>> +				 struct iio_trigger *trig)
>> +{
>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>> +		return 0;
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>> +	.driver_module = THIS_MODULE,
>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = arg;
>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>> +	unsigned int status;
>> +
>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>> +
>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>> +				   DFSDM_ICR_CLRROVRF_MASK,
>> +				   DFSDM_ICR_CLRROVRF_MASK);
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>> +{
>> +	struct dma_tx_state state;
>> +	enum dma_status status;
>> +
>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>> +				     pdmc->dma_chan->cookie,
>> +				     &state);
>> +	if (status == DMA_IN_PROGRESS) {
>> +		/* Residue is size in bytes from end of buffer */
>> +		unsigned int i = pdmc->buf_sz - state.residue;
>> +		unsigned int size;
>> +
>> +		/* Return available bytes */
>> +		if (i >= pdmc->bufi)
>> +			size = i - pdmc->bufi;
>> +		else
>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>> +
>> +		return size;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>> +{
>> +	struct iio_dev *indio_dev = data;
>> +
>> +	iio_trigger_poll_chained(indio_dev->trig);
> This shouldn't be going through the trigger infrastructure at all really.
> I'm not 100% sure what doing so is gaining you...
Yes i will clean trigger part and call directly the ASoC callback here.
>> +}
>> +
>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct dma_async_tx_descriptor *desc;
>> +	dma_cookie_t cookie;
>> +	int ret;
>> +
>> +	if (!pdmc->dma_chan)
>> +		return -EINVAL;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>> +
>> +	/* Prepare a DMA cyclic transaction */
>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>> +					 pdmc->dma_buf,
>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>> +					 DMA_DEV_TO_MEM,
>> +					 DMA_PREP_INTERRUPT);
>> +	if (!desc)
>> +		return -EBUSY;
>> +
>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>> +	desc->callback_param = indio_dev;
>> +
>> +	cookie = dmaengine_submit(desc);
>> +	ret = dma_submit_error(cookie);
>> +	if (ret) {
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +		return ret;
>> +	}
>> +
>> +	/* Issue pending DMA requests */
>> +	dma_async_issue_pending(pdmc->dma_chan);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	/* Reset pdmc buffer index */
>> +	pdmc->bufi = 0;
>> +
>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>> +		goto err_stop_conv;
>> +	}
>> +
>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>> +		goto err_stop_dma;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_stop_dma:
>> +	if (pdmc->dma_chan)
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +err_stop_conv:
>> +	stm32_dfsdm_stop_conv(pdmc);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>> +	if (ret < 0)
>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>> +
>> +	if (pdmc->dma_chan)
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +
>> +	stm32_dfsdm_stop_conv(pdmc);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>> +	.postenable = &stm32_dfsdm_postenable,
>> +	.predisable = &stm32_dfsdm_predisable,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>> +{
> OK. So now I kind of understand what the trigger provided in the adc driver
> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
> a hack.  We need fullblown DMA buffer consumers to do this right.
Yes exactly.
> 
> Probably best person to comment on that is Lars. 
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *indio_dev = pf->indio_dev;
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	size_t old_pos;
>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>> +
>> +	/*
>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>> +	 * an interface to push data samples per samples.
>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>> +	 * and interface is hacked using a private callback registered by ASoC.
>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>> +	 * support in IIO.
>> +	 */
>> +
>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>> +		pdmc->bufi, available);
>> +	old_pos = pdmc->bufi;
>> +	while (available >= indio_dev->scan_bytes) {
>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>> +
>> +		/* Mask 8 LSB that contains the channel ID */
>> +		*buffer &= 0xFFFFFF00;
>> +		available -= indio_dev->scan_bytes;
>> +		pdmc->bufi += indio_dev->scan_bytes;
>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>> +			if (pdmc->cb)
>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>> +			pdmc->bufi = 0;
>> +			old_pos = 0;
>> +		}
>> +	}
>> +	if (pdmc->cb)
>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>> +				pdmc->cb_priv);
>> +
>> +	iio_trigger_notify_done(indio_dev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_get_buff_cb - register a callback
>> + *	that will be called when DMA transfer period is achieved.
>> + *
>> + * @iio_dev: Handle to IIO device.
>> + * @cb: pointer to callback function.
>> + *	@data: pointer to data buffer
>> + *	@size: size in byte of the data buffer
>> + *	@private: pointer to consumer private structure
>> + * @private: pointer to consumer private structure
>> + */
>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>> +			    int (*cb)(const void *data, size_t size,
>> +				      void *private),
>> +			    void *private)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc;
>> +
>> +	if (!iio_dev)
>> +		return -EINVAL;
>> +	pdmc = iio_priv(iio_dev);
>> +
>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>> +		return -EINVAL;
>> +
>> +	pdmc->cb = cb;
>> +	pdmc->cb_priv = private;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>> +
>> +/**
>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>> + *
>> + * @iio_dev: Handle to IIO device.
>> + */
>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc;
>> +
>> +	if (!iio_dev)
>> +		return -EINVAL;
>> +	pdmc = iio_priv(iio_dev);
>> +
>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>> +		return -EINVAL;
>> +	pdmc->cb = NULL;
>> +	pdmc->cb_priv = NULL;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>> +
>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>> +{
>> +	struct iio_chan_spec *ch;
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>> +	if (!ch)
>> +		return -ENOMEM;
>> +
>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = 0;
>> +	ch->ext_info = dfsdm_adc_ext_info;
>> +
>> +	ch->scan_type.sign = 's';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +
>> +	pdmc->ch_id = ch->channel;
>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>> +
>> +	indio_dev->num_channels = 1;
>> +	indio_dev->channels = ch;
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>> +	{}
>> +};
>> +
>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct dma_slave_config config;
>> +	int ret;
>> +
>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>> +	if (!pdmc->dma_chan)
>> +		return -EINVAL;
>> +
>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>> +					 DFSDM_DMA_BUFFER_SIZE,
>> +					 &pdmc->dma_buf, GFP_KERNEL);
>> +	if (!pdmc->rx_buf) {
>> +		ret = -ENOMEM;
>> +		goto err_release;
>> +	}
>> +
>> +	/* Configure DMA channel to read data register */
>> +	memset(&config, 0, sizeof(config));
>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +
>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>> +	if (ret)
>> +		goto err_free;
>> +
>> +	return 0;
>> +
>> +err_free:
>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>> +			  pdmc->rx_buf, pdmc->dma_buf);
>> +err_release:
>> +	dma_release_channel(pdmc->dma_chan);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct stm32_dfsdm_audio *pdmc;
>> +	struct device_node *np = dev->of_node;
>> +	struct iio_dev *iio;
>> +	char *name;
>> +	int ret, irq, val;
>> +
>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>> +	if (IS_ERR(iio)) {
>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>> +		return PTR_ERR(iio);
>> +	}
>> +
>> +	pdmc = iio_priv(iio);
>> +	if (IS_ERR(pdmc)) {
>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>> +		return PTR_ERR(pdmc);
>> +	}
>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>> +
>> +	iio->name = np->name;
>> +	iio->dev.parent = dev;
>> +	iio->dev.of_node = np;
>> +	iio->info = &stm32_dfsdm_info_pdmc;
>> +	iio->modes = INDIO_DIRECT_MODE;
>> +
>> +	platform_set_drvdata(pdev, pdmc);
>> +
>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>> +	if (ret != 0) {
>> +		dev_err(dev, "Missing reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>> +	if (!name)
>> +		return -ENOMEM;
>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>> +	iio->name = name;
>> +
>> +	/*
>> +	 * In a first step IRQs generated for channels are not treated.
>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>> +	 */
>> +	irq = platform_get_irq(pdev, 0);
>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>> +			       0, pdev->name, pdmc);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to request IRQ\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set filter order\n");
>> +		return ret;
>> +	}
>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>> +	if (!ret)
>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>> +
>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "DMA request failed\n");
>> +		return ret;
>> +	}
>> +
>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>> +
>> +	ret = iio_triggered_buffer_setup(iio,
>> +					 &iio_pollfunc_store_time,
>> +					 &stm32_dfsdm_audio_trigger_handler,
>> +					 &stm32_dfsdm_buffer_setup_ops);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>> +		goto err_dma_disable;
>> +	}
>> +
>> +	ret = iio_device_register(iio);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>> +		goto err_buffer_cleanup;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_buffer_cleanup:
>> +	iio_triggered_buffer_cleanup(iio);
>> +
>> +err_dma_disable:
>> +	if (pdmc->dma_chan) {
>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>> +				  DFSDM_DMA_BUFFER_SIZE,
>> +				  pdmc->rx_buf, pdmc->dma_buf);
>> +		dma_release_channel(pdmc->dma_chan);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>> +
>> +	iio_device_unregister(iio);
> Same as in the other driver. Can just use the devm_iio_device_register
> version and drop remove.
oops missing the previous comment... sorry.
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>> +	.driver = {
>> +		.name = "stm32-dfsdm-audio",
>> +		.of_match_table = stm32_dfsdm_audio_match,
>> +	},
>> +	.probe = stm32_dfsdm_audio_probe,
>> +	.remove = stm32_dfsdm_audio_remove,
>> +};
>> +module_platform_driver(stm32_dfsdm_audio_driver);
>> +
>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>> new file mode 100644
>> index 0000000..9b41b28
>> --- /dev/null
>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>> @@ -0,0 +1,41 @@
>> +/*
>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>> + *
>> + * 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 STM32_DFSDM_AUDIO_H
>> +#define STM32_DFSDM_AUDIO_H
>> +
>> +/**
>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>> + * @dev:	Pointer to client device.
>> + * @indio_dev:	Device on which the channel exists.
>> + * @cb:		Callback function.
>> + *		@data:  pointer to data buffer
>> + *		@size: size of the data buffer in bytes
>> + * @private:	Private data passed to callback.
>> + *
>> + */
>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>> +			    int (*cb)(const void *data, size_t size,
>> +				      void *private),
>> +			    void *private);
>> +/**
>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>> + * @indio_dev:	Device on which the channel exists.
>> + */
>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>> +
>> +#endif
>>
> 

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

* [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-20 11:29         ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:29 UTC (permalink / raw)
  To: linux-arm-kernel

Please find my comments in-line

Thanks and Regards,
Arnaud

On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add DFSDM driver to handle PDM audio microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> So key element here is that we really need to have a proper way of
> doing DMA buffer consumers from IIO (as you said in your cover letter).
> 
> Not entirely obvious how to do this.  Would like some input from Lars
> if possible.  I'm afraid I don't have an suitable hardware to try anything
> out on (or the time really!).  This will obviously be quite different from
> the single scan stuff you have seen is there at the moment.
I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
DMA. I suppose that problematics are similar. Perhaps the extra
constrains in DFSDM is the in-kernel API used to get the data.

> 
> Whether this whole approach makes sense vs doing the dma in the alsa driver
> isn't clear to me.
> 
> On the plus side, presumably we'll get love dma ADC support for free
> as part of doing it this way ;)
> 
> It's been a while since I looked at the IIO dma buffer stuff at all. Will
> try and refresh my memory sometime this week.

Ok, i will wait your feedback (and Lars's one) before updating my code
for next version.
> 
> Jonathan
>> ---
>>  drivers/iio/adc/Kconfig                   |  13 +
>>  drivers/iio/adc/Makefile                  |   1 +
>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>  4 files changed, 775 insertions(+)
>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 3e0eb11..c108933 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-dfsdm-adc.
>>  
>> +config STM32_DFSDM_AUDIO
>> +	tristate "STMicroelectronics STM32 dfsdm audio
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select STM32_DFSDM_CORE
>> +	select REGMAP_MMIO
>> +	select IIO_BUFFER_DMAENGINE
>> +	help
>> +	  Select this option to support Audio PDM micophone for
>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-dfsdm-audio.
>> +
>>  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 161f271..79f975d 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>> new file mode 100644
>> index 0000000..115ef8f
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>> @@ -0,0 +1,720 @@
>> +/*
>> + * This file is the ADC part of of the STM32 DFSDM driver
>> + *
>> + * Copyright (C) 2017, 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/dmaengine.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>> +
>> +struct stm32_dfsdm_audio {
>> +	struct stm32_dfsdm *dfsdm;
>> +	unsigned int fl_id;
>> +	unsigned int ch_id;
>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>> +
>> +	u8 *rx_buf;
>> +	unsigned int bufi; /* Buffer current position */
>> +	unsigned int buf_sz; /* Buffer size */
>> +
>> +	struct dma_chan	*dma_chan;
>> +	dma_addr_t dma_buf;
>> +
>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>> +	void *cb_priv;
>> +};
>> +
>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>> +
>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>> +				    const struct iio_chan_spec *chan, char *buf)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>> +}
>> +
>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>> +				    const struct iio_chan_spec *chan,
>> +				    const char *buf, size_t len)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>> +	unsigned int spi_freq = pdmc->spi_freq;
>> +	unsigned int sample_freq;
>> +	int ret;
>> +
>> +	ret = kstrtoint(buf, 0, &sample_freq);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>> +	if (!sample_freq)
>> +		return -EINVAL;
>> +
>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>> +
>> +	if (spi_freq % sample_freq)
>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>> +			 spi_freq / (spi_freq / sample_freq));
>> +
>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			"Not able to find filter parameter that match!\n");
>> +		return ret;
>> +	}
>> +	pdmc->sample_freq = sample_freq;
>> +
>> +	return len;
>> +}
>> +
>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>> +				      const struct iio_chan_spec *chan,
>> +				      char *buf)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +
>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>> +}
>> +
>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>> +				      const struct iio_chan_spec *chan,
>> +				      const char *buf, size_t len)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>> +	unsigned int sample_freq = pdmc->sample_freq;
>> +	unsigned int spi_freq;
>> +	int ret;
>> +
>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>> +		return -EPERM;
>> +
>> +	ret = kstrtoint(buf, 0, &spi_freq);
>> +	if (ret)
>> +		return ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>> +	if (!spi_freq)
>> +		return -EINVAL;
>> +
>> +	if (sample_freq) {
>> +		if (spi_freq % sample_freq)
>> +			dev_warn(&indio_dev->dev,
>> +				 "Sampling rate not accurate (%d)\n",
>> +				 spi_freq / (spi_freq / sample_freq));
>> +
>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev,
>> +				"No filter parameters that match!\n");
>> +			return ret;
>> +		}
>> +	}
>> +	pdmc->spi_freq = spi_freq;
>> +
>> +	return len;
>> +}
>> +
>> +/*
>> + * Define external info for SPI Frequency and audio sampling rate that can be
>> + * configured by ASoC driver through consumer.h API
>> + */
>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>> +	/* filter oversampling: Post filter oversampling ratio */
>> +	{
>> +		.name = "audio_sampling_rate",
>> +		.shared = IIO_SHARED_BY_TYPE,
>> +		.read = dfsdm_audio_get_rate,
>> +		.write = dfsdm_audio_set_rate,
>> +	},
>> +	/* data_right_bit_shift : Filter output data shifting */
>> +	{
>> +		.name = "spi_clk_freq",
>> +		.shared = IIO_SHARED_BY_TYPE,
>> +		.read = dfsdm_audio_get_spiclk,
>> +		.write = dfsdm_audio_set_spiclk,
>> +	},
>> +	{},
>> +};
>> +
>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>> +{
>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>> +	int ret;
>> +
>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_dfsdm;
>> +
>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>> +					   pdmc->ch_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	/* Enable DMA transfer*/
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Enable conversion triggered by SPI clock*/
>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>> +	if (ret < 0)
>> +		goto stop_channels;
>> +
>> +	return 0;
>> +
>> +stop_channels:
>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>> +stop_dfsdm:
>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>> +
>> +	return ret;
>> +}
>> +
>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>> +{
>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>> +
>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>> +
>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>> +}
>> +
>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>> +				     unsigned int val)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>> +
>> +	/*
>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>> +	 * There should be :
>> +	 * - always one buffer (period) DMA is working on
>> +	 * - one buffer (period) driver pushed to ASoC side ().
>> +	 */
>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>> +	pdmc->buf_sz = watermark * 2;
>> +
>> +	return 0;
>> +}
>> +
>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>> +				 struct iio_trigger *trig)
>> +{
>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>> +		return 0;
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>> +	.driver_module = THIS_MODULE,
>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = arg;
>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>> +	unsigned int status;
>> +
>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>> +
>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>> +				   DFSDM_ICR_CLRROVRF_MASK,
>> +				   DFSDM_ICR_CLRROVRF_MASK);
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>> +{
>> +	struct dma_tx_state state;
>> +	enum dma_status status;
>> +
>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>> +				     pdmc->dma_chan->cookie,
>> +				     &state);
>> +	if (status == DMA_IN_PROGRESS) {
>> +		/* Residue is size in bytes from end of buffer */
>> +		unsigned int i = pdmc->buf_sz - state.residue;
>> +		unsigned int size;
>> +
>> +		/* Return available bytes */
>> +		if (i >= pdmc->bufi)
>> +			size = i - pdmc->bufi;
>> +		else
>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>> +
>> +		return size;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>> +{
>> +	struct iio_dev *indio_dev = data;
>> +
>> +	iio_trigger_poll_chained(indio_dev->trig);
> This shouldn't be going through the trigger infrastructure at all really.
> I'm not 100% sure what doing so is gaining you...
Yes i will clean trigger part and call directly the ASoC callback here.
>> +}
>> +
>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct dma_async_tx_descriptor *desc;
>> +	dma_cookie_t cookie;
>> +	int ret;
>> +
>> +	if (!pdmc->dma_chan)
>> +		return -EINVAL;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>> +
>> +	/* Prepare a DMA cyclic transaction */
>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>> +					 pdmc->dma_buf,
>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>> +					 DMA_DEV_TO_MEM,
>> +					 DMA_PREP_INTERRUPT);
>> +	if (!desc)
>> +		return -EBUSY;
>> +
>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>> +	desc->callback_param = indio_dev;
>> +
>> +	cookie = dmaengine_submit(desc);
>> +	ret = dma_submit_error(cookie);
>> +	if (ret) {
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +		return ret;
>> +	}
>> +
>> +	/* Issue pending DMA requests */
>> +	dma_async_issue_pending(pdmc->dma_chan);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	/* Reset pdmc buffer index */
>> +	pdmc->bufi = 0;
>> +
>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>> +		goto err_stop_conv;
>> +	}
>> +
>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>> +		goto err_stop_dma;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_stop_dma:
>> +	if (pdmc->dma_chan)
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +err_stop_conv:
>> +	stm32_dfsdm_stop_conv(pdmc);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>> +	if (ret < 0)
>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>> +
>> +	if (pdmc->dma_chan)
>> +		dmaengine_terminate_all(pdmc->dma_chan);
>> +
>> +	stm32_dfsdm_stop_conv(pdmc);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>> +	.postenable = &stm32_dfsdm_postenable,
>> +	.predisable = &stm32_dfsdm_predisable,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>> +{
> OK. So now I kind of understand what the trigger provided in the adc driver
> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
> a hack.  We need fullblown DMA buffer consumers to do this right.
Yes exactly.
> 
> Probably best person to comment on that is Lars. 
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *indio_dev = pf->indio_dev;
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	size_t old_pos;
>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>> +
>> +	/*
>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>> +	 * an interface to push data samples per samples.
>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>> +	 * and interface is hacked using a private callback registered by ASoC.
>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>> +	 * support in IIO.
>> +	 */
>> +
>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>> +		pdmc->bufi, available);
>> +	old_pos = pdmc->bufi;
>> +	while (available >= indio_dev->scan_bytes) {
>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>> +
>> +		/* Mask 8 LSB that contains the channel ID */
>> +		*buffer &= 0xFFFFFF00;
>> +		available -= indio_dev->scan_bytes;
>> +		pdmc->bufi += indio_dev->scan_bytes;
>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>> +			if (pdmc->cb)
>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>> +			pdmc->bufi = 0;
>> +			old_pos = 0;
>> +		}
>> +	}
>> +	if (pdmc->cb)
>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>> +				pdmc->cb_priv);
>> +
>> +	iio_trigger_notify_done(indio_dev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_get_buff_cb - register a callback
>> + *	that will be called when DMA transfer period is achieved.
>> + *
>> + * @iio_dev: Handle to IIO device.
>> + * @cb: pointer to callback function.
>> + *	@data: pointer to data buffer
>> + *	@size: size in byte of the data buffer
>> + *	@private: pointer to consumer private structure
>> + * @private: pointer to consumer private structure
>> + */
>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>> +			    int (*cb)(const void *data, size_t size,
>> +				      void *private),
>> +			    void *private)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc;
>> +
>> +	if (!iio_dev)
>> +		return -EINVAL;
>> +	pdmc = iio_priv(iio_dev);
>> +
>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>> +		return -EINVAL;
>> +
>> +	pdmc->cb = cb;
>> +	pdmc->cb_priv = private;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>> +
>> +/**
>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>> + *
>> + * @iio_dev: Handle to IIO device.
>> + */
>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc;
>> +
>> +	if (!iio_dev)
>> +		return -EINVAL;
>> +	pdmc = iio_priv(iio_dev);
>> +
>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>> +		return -EINVAL;
>> +	pdmc->cb = NULL;
>> +	pdmc->cb_priv = NULL;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>> +
>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>> +{
>> +	struct iio_chan_spec *ch;
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>> +	if (!ch)
>> +		return -ENOMEM;
>> +
>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = 0;
>> +	ch->ext_info = dfsdm_adc_ext_info;
>> +
>> +	ch->scan_type.sign = 's';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +
>> +	pdmc->ch_id = ch->channel;
>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>> +
>> +	indio_dev->num_channels = 1;
>> +	indio_dev->channels = ch;
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>> +	{}
>> +};
>> +
>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>> +	struct dma_slave_config config;
>> +	int ret;
>> +
>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>> +	if (!pdmc->dma_chan)
>> +		return -EINVAL;
>> +
>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>> +					 DFSDM_DMA_BUFFER_SIZE,
>> +					 &pdmc->dma_buf, GFP_KERNEL);
>> +	if (!pdmc->rx_buf) {
>> +		ret = -ENOMEM;
>> +		goto err_release;
>> +	}
>> +
>> +	/* Configure DMA channel to read data register */
>> +	memset(&config, 0, sizeof(config));
>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +
>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>> +	if (ret)
>> +		goto err_free;
>> +
>> +	return 0;
>> +
>> +err_free:
>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>> +			  pdmc->rx_buf, pdmc->dma_buf);
>> +err_release:
>> +	dma_release_channel(pdmc->dma_chan);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct stm32_dfsdm_audio *pdmc;
>> +	struct device_node *np = dev->of_node;
>> +	struct iio_dev *iio;
>> +	char *name;
>> +	int ret, irq, val;
>> +
>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>> +	if (IS_ERR(iio)) {
>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>> +		return PTR_ERR(iio);
>> +	}
>> +
>> +	pdmc = iio_priv(iio);
>> +	if (IS_ERR(pdmc)) {
>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>> +		return PTR_ERR(pdmc);
>> +	}
>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>> +
>> +	iio->name = np->name;
>> +	iio->dev.parent = dev;
>> +	iio->dev.of_node = np;
>> +	iio->info = &stm32_dfsdm_info_pdmc;
>> +	iio->modes = INDIO_DIRECT_MODE;
>> +
>> +	platform_set_drvdata(pdev, pdmc);
>> +
>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>> +	if (ret != 0) {
>> +		dev_err(dev, "Missing reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>> +	if (!name)
>> +		return -ENOMEM;
>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>> +	iio->name = name;
>> +
>> +	/*
>> +	 * In a first step IRQs generated for channels are not treated.
>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>> +	 */
>> +	irq = platform_get_irq(pdev, 0);
>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>> +			       0, pdev->name, pdmc);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to request IRQ\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set filter order\n");
>> +		return ret;
>> +	}
>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>> +
>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>> +	if (!ret)
>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>> +
>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "DMA request failed\n");
>> +		return ret;
>> +	}
>> +
>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>> +
>> +	ret = iio_triggered_buffer_setup(iio,
>> +					 &iio_pollfunc_store_time,
>> +					 &stm32_dfsdm_audio_trigger_handler,
>> +					 &stm32_dfsdm_buffer_setup_ops);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>> +		goto err_dma_disable;
>> +	}
>> +
>> +	ret = iio_device_register(iio);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>> +		goto err_buffer_cleanup;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_buffer_cleanup:
>> +	iio_triggered_buffer_cleanup(iio);
>> +
>> +err_dma_disable:
>> +	if (pdmc->dma_chan) {
>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>> +				  DFSDM_DMA_BUFFER_SIZE,
>> +				  pdmc->rx_buf, pdmc->dma_buf);
>> +		dma_release_channel(pdmc->dma_chan);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>> +
>> +	iio_device_unregister(iio);
> Same as in the other driver. Can just use the devm_iio_device_register
> version and drop remove.
oops missing the previous comment... sorry.
>> +
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>> +	.driver = {
>> +		.name = "stm32-dfsdm-audio",
>> +		.of_match_table = stm32_dfsdm_audio_match,
>> +	},
>> +	.probe = stm32_dfsdm_audio_probe,
>> +	.remove = stm32_dfsdm_audio_remove,
>> +};
>> +module_platform_driver(stm32_dfsdm_audio_driver);
>> +
>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>> new file mode 100644
>> index 0000000..9b41b28
>> --- /dev/null
>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>> @@ -0,0 +1,41 @@
>> +/*
>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>> + *
>> + * 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 STM32_DFSDM_AUDIO_H
>> +#define STM32_DFSDM_AUDIO_H
>> +
>> +/**
>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>> + * @dev:	Pointer to client device.
>> + * @indio_dev:	Device on which the channel exists.
>> + * @cb:		Callback function.
>> + *		@data:  pointer to data buffer
>> + *		@size: size of the data buffer in bytes
>> + * @private:	Private data passed to callback.
>> + *
>> + */
>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>> +			    int (*cb)(const void *data, size_t size,
>> +				      void *private),
>> +			    void *private);
>> +/**
>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>> + * @indio_dev:	Device on which the channel exists.
>> + */
>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>> +
>> +#endif
>>
> 

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

* Re: [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
  2017-03-19 22:44       ` Jonathan Cameron
  (?)
@ 2017-03-20 11:30         ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:30 UTC (permalink / raw)
  To: Jonathan Cameron, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, Olivier MOYSAN, kernel, linux-iio,
	Maxime Coquelin, linux-arm-kernel, Alexandre TORGUE



On 03/19/2017 11:44 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add iio consumer API to set buffer size and watermark according
>> to sysfs API.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Hmm. Not keen on the length one.  Setting a requested watermark
> is fair enough.  There is no actually buffer in these cases though
> so setting it's length is downright odd..
Length and watermark are configurable from user land through sysfs.
Seems to me logic to also propose it in inkern API...
But I can clean length , no problem.

> 
> Guess this is part of the hacks we need to clean up by doing
> the dma buffer consumer stuff right...
Yes all is linked :-).

> 
> Jonathan
>> ---
>>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>>  include/linux/iio/consumer.h                | 13 +++++++++++++
>>  2 files changed, 25 insertions(+)
>>
>> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
>> index b8f550e..43c066a 100644
>> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
>> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
>> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>  }
>>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>>  
>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
>> +				      size_t length, size_t watermark)
>> +{
>> +	if (!length || length < watermark)
>> +		return -EINVAL;
>> +	cb_buff->buffer.watermark = watermark;
>> +	cb_buff->buffer.length = length;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
>> +
>>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>>  {
>>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
>> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
>> index cb44771..0f6e94d 100644
>> --- a/include/linux/iio/consumer.h
>> +++ b/include/linux/iio/consumer.h
>> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>  						       void *private),
>>  					     void *private);
>>  /**
>> + * iio_channel_cb_set_buffer_size() - set the buffer length.
>> + * @cb_buffer:		The callback buffer from whom we want the channel
>> + *			information.
>> + * @length: buffer length in bytes
>> + * @watermark: buffer watermark in bytes
>> + *
>> + * This function allows to configure the buffer length. The watermark if
>> + * forced to half of the buffer.
>> + */
>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
>> +				     size_t length, size_t watermark);
>> +
>> +/**
>>   * iio_channel_release_all_cb() - release and unregister the callback.
>>   * @cb_buffer:		The callback buffer that was allocated.
>>   */
>>
> 
> --
> 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] 109+ messages in thread

* Re: [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-20 11:30         ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:30 UTC (permalink / raw)
  To: Jonathan Cameron, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN



On 03/19/2017 11:44 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add iio consumer API to set buffer size and watermark according
>> to sysfs API.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Hmm. Not keen on the length one.  Setting a requested watermark
> is fair enough.  There is no actually buffer in these cases though
> so setting it's length is downright odd..
Length and watermark are configurable from user land through sysfs.
Seems to me logic to also propose it in inkern API...
But I can clean length , no problem.

> 
> Guess this is part of the hacks we need to clean up by doing
> the dma buffer consumer stuff right...
Yes all is linked :-).

> 
> Jonathan
>> ---
>>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>>  include/linux/iio/consumer.h                | 13 +++++++++++++
>>  2 files changed, 25 insertions(+)
>>
>> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
>> index b8f550e..43c066a 100644
>> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
>> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
>> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>  }
>>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>>  
>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
>> +				      size_t length, size_t watermark)
>> +{
>> +	if (!length || length < watermark)
>> +		return -EINVAL;
>> +	cb_buff->buffer.watermark = watermark;
>> +	cb_buff->buffer.length = length;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
>> +
>>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>>  {
>>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
>> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
>> index cb44771..0f6e94d 100644
>> --- a/include/linux/iio/consumer.h
>> +++ b/include/linux/iio/consumer.h
>> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>  						       void *private),
>>  					     void *private);
>>  /**
>> + * iio_channel_cb_set_buffer_size() - set the buffer length.
>> + * @cb_buffer:		The callback buffer from whom we want the channel
>> + *			information.
>> + * @length: buffer length in bytes
>> + * @watermark: buffer watermark in bytes
>> + *
>> + * This function allows to configure the buffer length. The watermark if
>> + * forced to half of the buffer.
>> + */
>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
>> +				     size_t length, size_t watermark);
>> +
>> +/**
>>   * iio_channel_release_all_cb() - release and unregister the callback.
>>   * @cb_buffer:		The callback buffer that was allocated.
>>   */
>>
> 
> --
> 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] 109+ messages in thread

* [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-20 11:30         ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-20 11:30 UTC (permalink / raw)
  To: linux-arm-kernel



On 03/19/2017 11:44 PM, Jonathan Cameron wrote:
> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>> Add iio consumer API to set buffer size and watermark according
>> to sysfs API.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Hmm. Not keen on the length one.  Setting a requested watermark
> is fair enough.  There is no actually buffer in these cases though
> so setting it's length is downright odd..
Length and watermark are configurable from user land through sysfs.
Seems to me logic to also propose it in inkern API...
But I can clean length , no problem.

> 
> Guess this is part of the hacks we need to clean up by doing
> the dma buffer consumer stuff right...
Yes all is linked :-).

> 
> Jonathan
>> ---
>>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>>  include/linux/iio/consumer.h                | 13 +++++++++++++
>>  2 files changed, 25 insertions(+)
>>
>> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
>> index b8f550e..43c066a 100644
>> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
>> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
>> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>  }
>>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>>  
>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
>> +				      size_t length, size_t watermark)
>> +{
>> +	if (!length || length < watermark)
>> +		return -EINVAL;
>> +	cb_buff->buffer.watermark = watermark;
>> +	cb_buff->buffer.length = length;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
>> +
>>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>>  {
>>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
>> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
>> index cb44771..0f6e94d 100644
>> --- a/include/linux/iio/consumer.h
>> +++ b/include/linux/iio/consumer.h
>> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>  						       void *private),
>>  					     void *private);
>>  /**
>> + * iio_channel_cb_set_buffer_size() - set the buffer length.
>> + * @cb_buffer:		The callback buffer from whom we want the channel
>> + *			information.
>> + * @length: buffer length in bytes
>> + * @watermark: buffer watermark in bytes
>> + *
>> + * This function allows to configure the buffer length. The watermark if
>> + * forced to half of the buffer.
>> + */
>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
>> +				     size_t length, size_t watermark);
>> +
>> +/**
>>   * iio_channel_release_all_cb() - release and unregister the callback.
>>   * @cb_buffer:		The callback buffer that was allocated.
>>   */
>>
> 
> --
> 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] 109+ messages in thread

* Re: [PATCH v3 02/11] IIO: Add DT bindings for sigma delta adc modulator
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-24 14:21       ` Rob Herring
  -1 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:21 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

On Fri, Mar 17, 2017 at 03:08:15PM +0100, Arnaud Pouliquen wrote:
> Add documentation of device tree bindings to support
> sigma delta modulator in IIO framework.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> ---
> V2 -> V3 :
> 	-Rename to suppress "simple"
>         - add "ads1201" compatibility
>  
>  .../devicetree/bindings/iio/adc/sigma-delta-modulator.txt   | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> new file mode 100644
> index 0000000..27f04a3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> @@ -0,0 +1,13 @@
> +Device-Tree bindings for sigma delta modulator
> +
> +Required properties:
> +- compatible: should be "ads1201", "sd-modulator". "sd-modulator" can be use
> +	as a generic SD modulator if modulator not specified in compatible list.
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +
> +Example node:
> +
> +	ads1202: simple_sd_adc@0 {

adc@...

With that,

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +	};
> -- 
> 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	[flat|nested] 109+ messages in thread

* Re: [PATCH v3 02/11] IIO: Add DT bindings for sigma delta adc modulator
@ 2017-03-24 14:21       ` Rob Herring
  0 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:21 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, devicetree,
	linux-arm-kernel, linux-iio, alsa-devel, kernel, Maxime Coquelin,
	Alexandre Torgue, olivier moysan

On Fri, Mar 17, 2017 at 03:08:15PM +0100, Arnaud Pouliquen wrote:
> Add documentation of device tree bindings to support
> sigma delta modulator in IIO framework.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2 -> V3 :
> 	-Rename to suppress "simple"
>         - add "ads1201" compatibility
>  
>  .../devicetree/bindings/iio/adc/sigma-delta-modulator.txt   | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> new file mode 100644
> index 0000000..27f04a3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> @@ -0,0 +1,13 @@
> +Device-Tree bindings for sigma delta modulator
> +
> +Required properties:
> +- compatible: should be "ads1201", "sd-modulator". "sd-modulator" can be use
> +	as a generic SD modulator if modulator not specified in compatible list.
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +
> +Example node:
> +
> +	ads1202: simple_sd_adc@0 {

adc@...

With that,

Acked-by: Rob Herring <robh@kernel.org>

> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +	};
> -- 
> 1.9.1
> 

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

* [PATCH v3 02/11] IIO: Add DT bindings for sigma delta adc modulator
@ 2017-03-24 14:21       ` Rob Herring
  0 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Mar 17, 2017 at 03:08:15PM +0100, Arnaud Pouliquen wrote:
> Add documentation of device tree bindings to support
> sigma delta modulator in IIO framework.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2 -> V3 :
> 	-Rename to suppress "simple"
>         - add "ads1201" compatibility
>  
>  .../devicetree/bindings/iio/adc/sigma-delta-modulator.txt   | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> new file mode 100644
> index 0000000..27f04a3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/sigma-delta-modulator.txt
> @@ -0,0 +1,13 @@
> +Device-Tree bindings for sigma delta modulator
> +
> +Required properties:
> +- compatible: should be "ads1201", "sd-modulator". "sd-modulator" can be use
> +	as a generic SD modulator if modulator not specified in compatible list.
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +
> +Example node:
> +
> +	ads1202: simple_sd_adc at 0 {

adc at ...

With that,

Acked-by: Rob Herring <robh@kernel.org>

> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +	};
> -- 
> 1.9.1
> 

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

* Re: [PATCH v3 04/11] IIO: add DT bindings for stm32 DFSDM filter
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-24 14:37     ` Rob Herring
  -1 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:37 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	olivier moysan, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Mark Brown, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre Torgue

On Fri, Mar 17, 2017 at 03:08:17PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describes Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2->V3:
>    Fixes based on V2 comments
> 
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 +++++++++++++++++++++
>  1 file changed, 120 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt

Acked-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v3 04/11] IIO: add DT bindings for stm32 DFSDM filter
@ 2017-03-24 14:37     ` Rob Herring
  0 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:37 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, devicetree,
	linux-arm-kernel, linux-iio, alsa-devel, kernel, Maxime Coquelin,
	Alexandre Torgue, olivier moysan

On Fri, Mar 17, 2017 at 03:08:17PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describes Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2->V3:
>    Fixes based on V2 comments
> 
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 +++++++++++++++++++++
>  1 file changed, 120 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt

Acked-by: Rob Herring <robh@kernel.org>

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

* [PATCH v3 04/11] IIO: add DT bindings for stm32 DFSDM filter
@ 2017-03-24 14:37     ` Rob Herring
  0 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:37 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Mar 17, 2017 at 03:08:17PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describes Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2->V3:
>    Fixes based on V2 comments
> 
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 120 +++++++++++++++++++++
>  1 file changed, 120 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt

Acked-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-24 14:46     ` Rob Herring
  -1 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:46 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	olivier moysan, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Mark Brown, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre Torgue

On Fri, Mar 17, 2017 at 03:08:21PM +0100, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for audio DMIC codec.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  Documentation/devicetree/bindings/sound/dmic.txt | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/dmic.txt b/Documentation/devicetree/bindings/sound/dmic.txt
> new file mode 100644
> index 0000000..eb2a9d4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/dmic.txt
> @@ -0,0 +1,11 @@
> +Device-Tree bindings for dmic codec

Please define what is and isn't a DMIC here. What's the interface?

> +
> +Required properties:
> +	- compatible: should be "dmic-codec".

DMICs don't have part numbers?

> +
> +Example node:
> +
> +	dmic_audio: dmic_audio@0 {

Don't use '_' and there's no reg property, so there should be no unit 
address.

> +		compatible = "dmic-codec";
> +		status = "okay";

Don't show status in examples.

> +	};
> -- 
> 1.9.1
> 

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

* Re: [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
@ 2017-03-24 14:46     ` Rob Herring
  0 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:46 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, devicetree,
	linux-arm-kernel, linux-iio, alsa-devel, kernel, Maxime Coquelin,
	Alexandre Torgue, olivier moysan

On Fri, Mar 17, 2017 at 03:08:21PM +0100, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for audio DMIC codec.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  Documentation/devicetree/bindings/sound/dmic.txt | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/dmic.txt b/Documentation/devicetree/bindings/sound/dmic.txt
> new file mode 100644
> index 0000000..eb2a9d4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/dmic.txt
> @@ -0,0 +1,11 @@
> +Device-Tree bindings for dmic codec

Please define what is and isn't a DMIC here. What's the interface?

> +
> +Required properties:
> +	- compatible: should be "dmic-codec".

DMICs don't have part numbers?

> +
> +Example node:
> +
> +	dmic_audio: dmic_audio@0 {

Don't use '_' and there's no reg property, so there should be no unit 
address.

> +		compatible = "dmic-codec";
> +		status = "okay";

Don't show status in examples.

> +	};
> -- 
> 1.9.1
> 

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

* [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
@ 2017-03-24 14:46     ` Rob Herring
  0 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Mar 17, 2017 at 03:08:21PM +0100, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for audio DMIC codec.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  Documentation/devicetree/bindings/sound/dmic.txt | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/dmic.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/dmic.txt b/Documentation/devicetree/bindings/sound/dmic.txt
> new file mode 100644
> index 0000000..eb2a9d4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/dmic.txt
> @@ -0,0 +1,11 @@
> +Device-Tree bindings for dmic codec

Please define what is and isn't a DMIC here. What's the interface?

> +
> +Required properties:
> +	- compatible: should be "dmic-codec".

DMICs don't have part numbers?

> +
> +Example node:
> +
> +	dmic_audio: dmic_audio at 0 {

Don't use '_' and there's no reg property, so there should be no unit 
address.

> +		compatible = "dmic-codec";
> +		status = "okay";

Don't show status in examples.

> +	};
> -- 
> 1.9.1
> 

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

* Re: [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-03-24 14:52     ` Rob Herring
  -1 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:52 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	olivier moysan, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Mark Brown, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre Torgue

On Fri, Mar 17, 2017 at 03:08:23PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describes audio settings to support
> Digital Filter for pulse density modulation(PDM) microphone.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2->V3:
>    Fixes based on V2 comments
> 
>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> new file mode 100644
> index 0000000..ab610bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> @@ -0,0 +1,40 @@
> +STMicroelectronics audio DFSDM DT bindings
> +
> +This driver supports audio PDM microphone capture through Digital Filter format
> +Sigma Delta modulators (DFSDM).
> +
> +Required properties:
> +  - compatible: "st,stm32h7-adfsdm".
> +
> +  - #sound-dai-cells : Must be equal to 0
> +
> +  - io-channels : phandle to iio dfsdm instance node.
> +
> +
> +Example of a simple sound card using audio DFSDM node.
> +
> +	dmic0: dmic_@0 {

Drop the '_' and unit address.

> +		compatible = "dmic-codec";
> +		#sound-dai-cells = <0>;
> +	};
> +
> +	asoc-pdm@0 {

asoc is a Linux term. Drop the unit address.

> +		compatible = "st,stm32h7-adfsdm";

Is this a separate block from the ADC? A drawing of the h/w blocks and 
connections would help.

> +		#sound-dai-cells = <0>;
> +		io-channels = <&dfsdm_adc0 0>;
> +	};
> +
> +	sound_dfsdm_pdm {

sound-card {

> + 		compatible = "simple-audio-card";
> + 		simple-audio-card,name = "dfsdm_pdm";
> +
> + 		dfsdm0_mic0: simple-audio-card,dai-link@0 {

I'd suggest moving to the graph card.

> + 			format = "pdm";
> + 			cpu {
> + 				sound-dai = <&asoc_pdm1>;

This phandle doesn't point to anything.

> + 			};
> + 			dmic0_codec: codec {
> + 				sound-dai = <&dmic0>;
> + 			};
> + 		};
> +	};
> \ No newline at end of file

^^^

> -- 
> 1.9.1
> 

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

* Re: [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
@ 2017-03-24 14:52     ` Rob Herring
  0 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:52 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, devicetree,
	linux-arm-kernel, linux-iio, alsa-devel, kernel, Maxime Coquelin,
	Alexandre Torgue, olivier moysan

On Fri, Mar 17, 2017 at 03:08:23PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describes audio settings to support
> Digital Filter for pulse density modulation(PDM) microphone.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2->V3:
>    Fixes based on V2 comments
> 
>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> new file mode 100644
> index 0000000..ab610bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> @@ -0,0 +1,40 @@
> +STMicroelectronics audio DFSDM DT bindings
> +
> +This driver supports audio PDM microphone capture through Digital Filter format
> +Sigma Delta modulators (DFSDM).
> +
> +Required properties:
> +  - compatible: "st,stm32h7-adfsdm".
> +
> +  - #sound-dai-cells : Must be equal to 0
> +
> +  - io-channels : phandle to iio dfsdm instance node.
> +
> +
> +Example of a simple sound card using audio DFSDM node.
> +
> +	dmic0: dmic_@0 {

Drop the '_' and unit address.

> +		compatible = "dmic-codec";
> +		#sound-dai-cells = <0>;
> +	};
> +
> +	asoc-pdm@0 {

asoc is a Linux term. Drop the unit address.

> +		compatible = "st,stm32h7-adfsdm";

Is this a separate block from the ADC? A drawing of the h/w blocks and 
connections would help.

> +		#sound-dai-cells = <0>;
> +		io-channels = <&dfsdm_adc0 0>;
> +	};
> +
> +	sound_dfsdm_pdm {

sound-card {

> + 		compatible = "simple-audio-card";
> + 		simple-audio-card,name = "dfsdm_pdm";
> +
> + 		dfsdm0_mic0: simple-audio-card,dai-link@0 {

I'd suggest moving to the graph card.

> + 			format = "pdm";
> + 			cpu {
> + 				sound-dai = <&asoc_pdm1>;

This phandle doesn't point to anything.

> + 			};
> + 			dmic0_codec: codec {
> + 				sound-dai = <&dmic0>;
> + 			};
> + 		};
> +	};
> \ No newline at end of file

^^^

> -- 
> 1.9.1
> 

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

* [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
@ 2017-03-24 14:52     ` Rob Herring
  0 siblings, 0 replies; 109+ messages in thread
From: Rob Herring @ 2017-03-24 14:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Mar 17, 2017 at 03:08:23PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describes audio settings to support
> Digital Filter for pulse density modulation(PDM) microphone.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2->V3:
>    Fixes based on V2 comments
> 
>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> new file mode 100644
> index 0000000..ab610bc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
> @@ -0,0 +1,40 @@
> +STMicroelectronics audio DFSDM DT bindings
> +
> +This driver supports audio PDM microphone capture through Digital Filter format
> +Sigma Delta modulators (DFSDM).
> +
> +Required properties:
> +  - compatible: "st,stm32h7-adfsdm".
> +
> +  - #sound-dai-cells : Must be equal to 0
> +
> +  - io-channels : phandle to iio dfsdm instance node.
> +
> +
> +Example of a simple sound card using audio DFSDM node.
> +
> +	dmic0: dmic_ at 0 {

Drop the '_' and unit address.

> +		compatible = "dmic-codec";
> +		#sound-dai-cells = <0>;
> +	};
> +
> +	asoc-pdm at 0 {

asoc is a Linux term. Drop the unit address.

> +		compatible = "st,stm32h7-adfsdm";

Is this a separate block from the ADC? A drawing of the h/w blocks and 
connections would help.

> +		#sound-dai-cells = <0>;
> +		io-channels = <&dfsdm_adc0 0>;
> +	};
> +
> +	sound_dfsdm_pdm {

sound-card {

> + 		compatible = "simple-audio-card";
> + 		simple-audio-card,name = "dfsdm_pdm";
> +
> + 		dfsdm0_mic0: simple-audio-card,dai-link at 0 {

I'd suggest moving to the graph card.

> + 			format = "pdm";
> + 			cpu {
> + 				sound-dai = <&asoc_pdm1>;

This phandle doesn't point to anything.

> + 			};
> + 			dmic0_codec: codec {
> + 				sound-dai = <&dmic0>;
> + 			};
> + 		};
> +	};
> \ No newline at end of file

^^^

> -- 
> 1.9.1
> 

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

* Re: [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
  2017-03-20 11:24         ` Arnaud Pouliquen
  (?)
@ 2017-03-25 15:53           ` Jonathan Cameron
  -1 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 15:53 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, Olivier MOYSAN, kernel, linux-iio,
	Maxime Coquelin, linux-arm-kernel, Alexandre TORGUE

On 20/03/17 11:24, Arnaud Pouliquen wrote:
> Hello Jonathan
> 
> Thanks for your comments
> Few answers in-line.
> 
> Regards
> Arnaud
> 
> On 03/19/2017 11:25 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
>>> in n bit samples through a low pass filter and an integrator.
>>> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Various minor bits inline.
>>
>> I'm mostly liking this.  I do slightly wondering if semantically it
>> should be the front end that has the channels rather than the
>> backend.  Would be fiddly to do though and probably not worth the
>> hassle.
> DFSDM support the scan mode, so several front ends can be connected to
> One filter. In this case not possible to expose channel FE.
It still could but would admittedly get really fiddly and require demuxing
the scan...
> 
>>
>> Would love to see it running in a continuous mode in IIO, but
>> I guess that can follow along later.
> Yes for the rest of the management it should be quite close to the
> stm32-adc driver.
> 
>>
>> The comment about the trigger has me confused
>> - perhaps you could elaborate further on that?
> Code associated to the trigger should be part of the [PATCH v3 06/11]
> IIO: ADC: add stm32 DFSDM support for PDM microphone, as it concern the
> audio part...
> I did not found a way to use consumer.h interface to enable DFSDM IIO,
> without defining triggered buffer. that's why i defined a trigger and
> use it.
> But i just saw that my reasoning is wrong. I'm linked to trigger in
> stm32-dfsdm-audio.c because i use iio_triggered_buffer_postenable and
> iio_triggered_buffer_predisable.
This used to be more obvious until we put those boiler plate functions in
to avoid lots of replication.  Pretty much everything should be optional.
> As i don't use the callback for buffer
> no need to call it...i can call the ASoC callback directly in DMA IRQ.
> Still a hack but more logic...
Cool.  We definitely need to clean this up long term, but perhaps not
as part of this initially at least!

I hate holding drivers up for internal stuff that we can change in
our own good time!
> 
>>
>> Jonathan
>>> ---
>>> V2 -> V3 :
>>> 	- Split audio and ADC support in 2 drivers
>>> 	- Implement DMA cyclic mode
>>> 	- Add SPI bus Trigger for buffer management
>>>
>>>  drivers/iio/adc/Kconfig            |  26 ++
>>>  drivers/iio/adc/Makefile           |   2 +
>>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>>>  5 files changed, 1477 insertions(+)
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index d411d66..3e0eb11 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -452,6 +452,32 @@ config STM32_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-adc.
>>>  
>>> +config STM32_DFSDM_CORE
>>> +	tristate "STMicroelectronics STM32 dfsdm core"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select REGMAP
>>> +	select REGMAP_MMIO
>>> +	help
>>> +	  Select this option to enable the  driver for STMicroelectronics
>>> +	  STM32 digital filter for sigma delta converter.
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-dfsdm-core.
>>> +
>>> +config STM32_DFSDM_ADC
>>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select STM32_DFSDM_CORE
>>> +	select REGMAP_MMIO
>>> +	select IIO_BUFFER_DMAENGINE
>>> +	select IIO_HW_CONSUMER
>>> +	help
>>> +	  Select this option to support ADCSigma delta modulator for
>>> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
>>> +
>>> +	  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 c68819c..161f271 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>>> @@ -0,0 +1,419 @@
>>> +/*
>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, 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/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +#define DFSDM_TIMEOUT_US 100000
>>> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
>>> +
>>> +struct stm32_dfsdm_adc {
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	unsigned int fl_id;
>>> +	unsigned int ch_id;
>>> +
>>> +	unsigned int oversamp;
>>> +
>>> +	struct completion completion;
>>> +
>>> +	u32 *buffer;
>>> +
>>> +	/* Hardware consumer structure for Front End IIO */
>>> +	struct iio_hw_consumer *hwc;
>>> +};
>>> +
>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_dfsdm;
>>> +
>>> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	return 0;
>>> +
>>> +stop_channels:
>>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>>> +stop_dfsdm:
>>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
>>> +
>>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>>> +
>>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>>> +}
>>> +
>>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>>> +				   const struct iio_chan_spec *chan, int *res)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	long timeout;
>>> +	int ret;
>>> +
>>> +	reinit_completion(&adc->completion);
>>> +
>>> +	adc->buffer = res;
>>> +
>>> +	/* Unmask IRQ for regular conversion achievement*/
>>> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_conv(adc);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
>>> +							    DFSDM_TIMEOUT);
>> blank line perhaps.
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +
>>> +	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", *res);
>>> +		ret = IIO_VAL_INT;
>>> +	}
>>> +
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +
>>> +	stm32_dfsdm_stop_conv(adc);
>>> +
>>> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
>>> +	int ret = -EINVAL;
>>> +
>>> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
>>> +		if (!ret)
>>> +			adc->oversamp = val;
>>> +	}
>> blank line here.
>>> +	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);
>>> +	int ret;
>>> +
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_RAW:
>>> +		ret = iio_hw_consumer_enable(adc->hwc);
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"%s: IIO enable failed (channel %d)\n",
>>> +				__func__, chan->channel);
>>> +			return ret;
>>> +		}
>>> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"%s: Conversion failed (channel %d)\n",
>>> +				__func__, chan->channel);
>>> +			return ret;
>>> +		}
>>> +
>>> +		iio_hw_consumer_disable(adc->hwc);
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		*val = adc->oversamp;
>>> +
>>> +		return IIO_VAL_INT;
>>> +	}
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info stm32_dfsdm_info_adc = {
>>> +	.read_raw = stm32_dfsdm_read_raw,
>>> +	.write_raw = stm32_dfsdm_write_raw,
>>> +	.driver_module = THIS_MODULE,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = arg;
>>> +	struct regmap *regmap = adc->dfsdm->regmap;
>>> +	unsigned int status;
>>> +
>>> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
>>> +
>>> +	if (status & DFSDM_ISR_REOCF_MASK) {
>>> +		/* read the data register clean the IRQ status */
>>> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
>>> +		complete(&adc->completion);
>>> +	}
>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>> What's this one?  Might want a comment given it's an irq you basically eat.
> Yes  at least an error message that to inform on an overrun.
>>> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>> +	}
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +
>>> +	return stm32_dfsdm_start_conv(adc);
>>> +}
>>> +
>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +
>>> +	stm32_dfsdm_stop_conv(adc);
>> blank line.
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>> +	.postenable = &stm32_dfsdm_postenable,
>>> +	.predisable = &stm32_dfsdm_predisable,
>>> +};
>>> +
>>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>>> +					 struct iio_chan_spec *chan,
>>> +					 int ch_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[ch_idx];
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = ch_idx;
>>> +
>>> +	/*
>>> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>>> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
>>> +	 */
>>> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>>> +
>>> +	ch->scan_type.sign = 'u';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +	adc->ch_id = ch->channel;
>>> +
>>> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
>>> +					  &adc->dfsdm->ch_list[ch->channel]);
>>> +}
>>> +
>>> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
>>> +{
>>> +	struct iio_chan_spec *channels;
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	unsigned int num_ch;
>>> +	int ret, chan_idx;
>>> +
>>> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
>>> +					     "st,adc-channels");
>>> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
>>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>>> +		return num_ch < 0 ? num_ch : -EINVAL;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Number of channel per filter is temporary limited to 1.
>>> +	 * Restriction should be cleaned with scan mode
>>> +	 */
>>> +	if (num_ch > 1) {
>>> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	/* Bind to SD modulator IIO device */
>>> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>>> +	if (IS_ERR(adc->hwc))
>>> +		return -EPROBE_DEFER;
>>> +
>>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>>> +				GFP_KERNEL);
>>> +	if (!channels)
>>> +		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 free_hwc;
>>> +	}
>>> +
>>> +	indio_dev->num_channels = num_ch;
>>> +	indio_dev->channels = channels;
>>> +
>>> +	return 0;
>>> +
>>> +free_hwc:
>>> +	iio_hw_consumer_free(adc->hwc);
>> Given you have to free this in the error path, I would imagine you will
>> need a free somewhere in the main remove path?  Or just create a devm
>> version of iio_hw_consumer_alloc.  It will be useful in the long run.
>>> +	return ret;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
>>> +	{ .compatible = "st,stm32-dfsdm-adc"},
>>> +	{}
>>> +};
>>> +
>>> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>>> +{
>>> +	struct device *dev = &pdev->dev;
>>> +	struct stm32_dfsdm_adc *adc;
>>> +	struct device_node *np = dev->of_node;
>>> +	struct iio_dev *iio;
>>> +	char *name;
>>> +	int ret, irq, val;
>>> +
>>> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
>>> +	if (IS_ERR(iio)) {
>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>> +		return PTR_ERR(iio);
>>> +	}
>>> +
>>> +	adc = iio_priv(iio);
>>> +	if (IS_ERR(adc)) {
>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>> +		return PTR_ERR(adc);
>>> +	}
>>> +	adc->dfsdm = dev_get_drvdata(dev->parent);
>>> +
>>> +	iio->dev.parent = dev;
>>> +	iio->dev.of_node = np;
>>> +	iio->info = &stm32_dfsdm_info_adc;
>>> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
>>> +
>>> +	platform_set_drvdata(pdev, adc);
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
>>> +	if (ret != 0) {
>>> +		dev_err(dev, "Missing reg property\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
>> not freed.  Maybe devm_kzalloc
>>> +	if (!name)
>>> +		return -ENOMEM;
>>> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
>>> +	iio->name = name;
>>> +
>>> +	/*
>>> +	 * In a first step IRQs generated for channels are not treated.
>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>> +	 */
>>> +	irq = platform_get_irq(pdev, 0);
>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>> +			       0, pdev->name, adc);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to request IRQ\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to set filter order\n");
>>> +		return ret;
>>> +	}
>>> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>> +	if (!ret)
>>> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
>>> +
>>> +	ret = stm32_dfsdm_adc_chan_init(iio);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	init_completion(&adc->completion);
>>> +
>>> +	return iio_device_register(iio);
>>> +}
>>> +
>>> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	iio_device_unregister(iio);
>> If all you have is this in remove, you can probably get away with
>> devm_iio_device_register and get rid of the remove entirely.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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");
>>> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
>>> new file mode 100644
>>> index 0000000..488e456
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
>>> @@ -0,0 +1,658 @@
>>> +/*
>>> + * This file is part the core part STM32 DFSDM 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/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +struct stm32_dfsdm_dev_data {
>>> +	unsigned int num_filters;
>>> +	unsigned int num_channels;
>>> +	const struct regmap_config *regmap_cfg;
>>> +};
>>> +
>>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>>> +#define STM32H7_DFSDM_NUM_CHANNELS	8
>>> +
>>> +#define DFSDM_MAX_INT_OVERSAMPLING 256
>>> +
>>> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
>>> +
>>> +#define DFSDM_MAX_RES BIT(31)
>>> +#define DFSDM_DATA_RES BIT(23)
>>> +
>>> +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 = 0x2B8,
>>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>>> +	.fast_io = true,
>>> +};
>>> +
>>> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
>>> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
>>> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
>>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>>> +};
>>> +
>>> +struct dfsdm_priv {
>>> +	struct platform_device *pdev; /* platform device*/
>>> +
>>> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
>>> +
>>> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
>>> +	atomic_t n_active_ch;	/* number of current active channels */
>>> +
>>> +	/* Clock */
>>> +	struct clk *clk; /* DFSDM clock */
>>> +	struct clk *aclk; /* audio clock */
>>> +};
>>> +
>>> +/**
>>> + * stm32_dfsdm_set_osrs - compute filter parameters.
>> Naming would suggest it's more specific than this.
>> Setting over sampling ratios?
> Right, it is a computation not a set.
>>> + *
>>> + * Enable interface if n_active_ch is not null.
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fast: Fast mode enabled or disabled
>>> + * @oversamp: Expected oversampling between filtered sample and SD input stream
>>> + */
>>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>>> +			 unsigned int oversamp)
>>> +{
>>> +	unsigned int i, d, fosr, iosr;
>>> +	u64 res;
>>> +	s64 delta;
>>> +	unsigned int m = 1;	/* multiplication factor */
>>> +	unsigned int p = fl->ford;	/* filter order (ford) */
>>> +
>>> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
>>> +	/*
>>> +	 * This function tries to compute filter oversampling and integrator
>>> +	 * oversampling, base on oversampling ratio requested by user.
>>> +	 *
>>> +	 * Decimation d depends on the filter order and the oversampling ratios.
>>> +	 * ford: filter order
>>> +	 * fosr: filter over sampling ratio
>>> +	 * iosr: integrator over sampling ratio
>>> +	 */
>>> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
>>> +		m = 2;
>>> +		p = 2;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Looks for filter and integrator oversampling ratios which allows
>>> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
>>> +				d = fosr * (iosr + 3) + 2;
>>> +			else
>>> +				d = fosr * (iosr - 1 + p) + p;
>>> +
>>> +			if (d > oversamp)
>>> +				break;
>>> +			else if (d != oversamp)
>>> +				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 > DFSDM_MAX_RES)
>>> +					break;
>>> +			}
>>> +			if (res > DFSDM_MAX_RES)
>>> +				continue;
>>> +			res = res * (u64)m * (u64)iosr;
>>> +			if (res > DFSDM_MAX_RES)
>>> +				continue;
>>> +
>>> +			delta = res - DFSDM_DATA_RES;
>>> +
>>> +			if (res >= fl->res) {
>>> +				fl->res = res;
>>> +				fl->fosr = fosr;
>>> +				fl->iosr = iosr;
>>> +				fl->fast = fast;
>>> +				pr_debug("%s: fosr = %d, iosr = %d\n",
>>> +					 __func__, fl->fosr, fl->iosr);
>>> +			}
>>> +
>>> +			if (!delta)
>>> +				return 0;
>>> +		}
>>> +	}
>>> +
>>> +	if (!fl->fosr)
>>> +		return -EINVAL;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
>>> + *
>>> + * Enable interface if n_active_ch is not null.
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + */
>>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	struct device *dev = &priv->pdev->dev;
>>> +	unsigned int clk_div = priv->spi_clk_out_div;
>>> +	int ret;
>>> +
>>> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
>>> +		/* Enable clocks */
>>> +		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");
>>> +				goto disable_clk;
>>> +			}
>>> +		}
>>> +
>>> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>>> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
>>> +		if (ret < 0)
>>> +			goto disable_aclk;
>>> +
>>> +		/* Global enable of DFSDM interface */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>>> +					 DFSDM_CHCFGR1_DFSDMEN(1));
>>> +		if (ret < 0)
>>> +			goto disable_aclk;
>>> +	}
>>> +
>>> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
>>> +		atomic_read(&priv->n_active_ch));
>>> +
>>> +	return 0;
>>> +
>>> +disable_aclk:
>>> +	clk_disable_unprepare(priv->aclk);
>>> +disable_clk:
>>> +	clk_disable_unprepare(priv->clk);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
>>> + *
>>> + * Disable interface if n_active_ch is null
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + */
>>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	int ret;
>>> +
>>> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
>>> +		/* Global disable of DFSDM interface */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>>> +					 DFSDM_CHCFGR1_DFSDMEN(0));
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		/* Stop SPI CLKOUT */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>>> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		/* Disable clocks */
>>> +		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));
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_channel
>>> + *	Start DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: Channel index.
>>> + */
>>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>>> +{
>>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>>> +				  DFSDM_CHCFGR1_CHEN_MASK,
>>> +				  DFSDM_CHCFGR1_CHEN(1));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_channel
>>> + *	Stop DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: Channel index.
>>> + */
>>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>>> +{
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>>> +			   DFSDM_CHCFGR1_CHEN_MASK,
>>> +			   DFSDM_CHCFGR1_CHEN(0));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_chan_configure
>>> + *	Configure DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: channel index.
>>> + */
>>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>>> +			       struct stm32_dfsdm_channel *ch)
>>> +{
>>> +	unsigned int id = ch->id;
>>> +	struct regmap *regmap = dfsdm->regmap;
>>> +	int ret;
>>> +
>>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				 DFSDM_CHCFGR1_SITP_MASK,
>>> +				 DFSDM_CHCFGR1_SITP(ch->type));
>>> +	if (ret < 0)
>>> +		return ret;
>> Blank line here and in similar places makes it easier for my
>> eyes to parse at least...
>> I'd also like to see some docs in here, not all of these
>> are self explanatory.
> I will apply recommendation in my whole code for next time
>>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
>>> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
>>> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter index.
>>> + */
>>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>>> +{
>>> +	int ret;
>>> +
>>> +	/* Enable filter */
>>> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	/* Start conversion */
>>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +				  DFSDM_CR1_RSWSTART_MASK,
>>> +				  DFSDM_CR1_RSWSTART(1));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter index.
>>> + */
>>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>>> +{
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +	/* Disable conversion */
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
>>> + *	to channel.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: channel index.
>>> + * @fl_id: Filter index.
>>> + */
>>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				 unsigned int ch_id)
>>> +{
>>> +	struct regmap *regmap = dfsdm->regmap;
>>> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
>>> +	int ret;
>>> +
>>> +	/* Average integrator oversampling */
>>> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
>>> +				 DFSDM_FCR_IOSR(fl->iosr));
>>> +
>>> +	/* Filter order and Oversampling */
>> Please handle each error properly as it happens rather than mudling onwards.
>> If there is reason for this odd construction, then document it clearly.
> If you mention the checks on ret value that are missing at end of
> functions, yes dirty code to fix.
> 
>>> +	if (!ret)
>>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>>> +					 DFSDM_FCR_FOSR_MASK,
>>> +					 DFSDM_FCR_FOSR(fl->fosr));
>>> +
>>> +	if (!ret)
>>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>>> +					 DFSDM_FCR_FORD_MASK,
>>> +					 DFSDM_FCR_FORD(fl->ford));
>>> +
>>> +	/* If only one channel no scan mode supported for the moment */
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>>> +				 DFSDM_CR1_RCH_MASK,
>>> +				 DFSDM_CR1_RCH(ch_id));
>>> +
>>> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>>> +					 DFSDM_CR1_RSYNC_MASK,
>>> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
>>> +}
>>> +
>>> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
>>> +	.owner = THIS_MODULE,
>>> +};
>>> +
>>> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
>>> +					 struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	/*
>>> +	 * To be able to use buffer consumer interface a trigger is needed.
>>> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
>>> +	 * sense to define the serial interface ( SPI or manchester) as
>>> +	 * trigger source.
>> It's not actually the case that you have to have a triggrer.
>> There are plenty of drivers (particularly ones with hardware buffering)
>> where there is no trigger envolved.  That's not to say it doesn't make sense
>> here.
>>
>> I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
>> somewhat...
>>> +	 */
>>> +
>>> +	struct iio_trigger *trig;
>>> +	int ret;
>>> +
>>> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
>>> +	if (!trig)
>>> +		return -ENOMEM;
>>> +
>>> +	trig->dev.parent = pdev->dev.parent;
>>> +	trig->ops = &dfsdm_trigger_ops;
>>> +
>>> +	iio_trigger_set_drvdata(trig, dfsdm);
>>> +
>>> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
>>> +	if (ret)
>>> +		return ret;
>> Just return ret in all cases.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>>> +				 struct iio_dev *indio_dev,
>>> +				 struct iio_chan_spec *chan, int chan_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>>> +	struct stm32_dfsdm_channel *df_ch;
>>> +	const char *of_str;
>>> +	int ret, val;
>>> +
>>> +	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;
>>> +	}
>>> +
>>> +	df_ch =  &dfsdm->ch_list[ch->channel];
>> Extra space on the line above.
>>> +	df_ch->id = ch->channel;
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-types", chan_idx,
>>> +					    &of_str);
>>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
>>> +	if (ret < 0 || val < 0)
>>> +		df_ch->type = 0;
>>> +	else
>>> +		df_ch->type = val;
>>> +
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-clk-src", chan_idx,
>>> +					    &of_str);
>>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
>>> +	if (ret < 0 || val < 0)
>>> +		df_ch->src = 0;
>>> +	else
>>> +		df_ch->src = val;
>>> +
>>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> +					 "st,adc-alt-channel", chan_idx,
>>> +					 &df_ch->alt_si);
>>> +	if (ret < 0)
>>> +		df_ch->alt_si = 0;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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;
>>> +	unsigned long clk_freq;
>>> +	unsigned int spi_freq, rem;
>>> +	int ret;
>>> +
>>> +	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->dfsdm.phys_base = res->start;
>>> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
>>> +
>>> +	/* Source clock */
>>> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
>>> +	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");
>>> +	if (IS_ERR(priv->aclk))
>>> +		priv->aclk = NULL;
>>> +
>>> +	if (priv->aclk)
>>> +		clk_freq = clk_get_rate(priv->aclk);
>>> +	else
>>> +		clk_freq = clk_get_rate(priv->clk);
>>> +
>>> +	/* SPI clock freq */
>>> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
>>> +				   &spi_freq);
>>> +	if (ret < 0) {
>>> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
>>> +	priv->dfsdm.spi_master_freq = spi_freq;
>>> +
>>> +	if (rem) {
>>> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
>>> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
>>> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
>>> +	}
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +static const struct of_device_id stm32_dfsdm_of_match[] = {
>>> +	{
>>> +		.compatible = "st,stm32h7-dfsdm",
>>> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	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_dfsdm_dev_data *)of_id->data;
>>> +	dfsdm = &priv->dfsdm;
>>> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
>>> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
>>> +	if (!dfsdm->fl_list)
>>> +		return -ENOMEM;
>>> +
>>> +	dfsdm->num_fls = dev_data->num_filters;
>>> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
>>> +				      sizeof(*dfsdm->ch_list),
>>> +				      GFP_KERNEL);
>>> +	if (!dfsdm->ch_list)
>>> +		return -ENOMEM;
>>> +	dfsdm->num_chs = dev_data->num_channels;
>>> +
>>> +	ret = stm32_dfsdm_parse_of(pdev, priv);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
>>> +					    &stm32h7_dfsdm_regmap_cfg);
>>> +	if (IS_ERR(dfsdm->regmap)) {
>>> +		ret = PTR_ERR(dfsdm->regmap);
>>> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
>>> +			__func__, ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
>>> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
>>> +
>>> +		fl->id = i;
>> I'd like a comment on why this is needed...
>  to  be cleaned.
>>> +	}
>>> +
>>> +	platform_set_drvdata(pdev, dfsdm);
>>> +
>>> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
>>> +}
>>> +
>>> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
>>> new file mode 100644
>>> index 0000000..bb7d74f
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm.h
>>> @@ -0,0 +1,371 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM 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__H
>>> +#define MDF_STM32_DFSDM__H
>>> +
>>> +#include <linux/bitfield.h>
>>> +
>>> +#include <linux/iio/iio.h>
>>> +/*
>>> + * STM32 DFSDM - global register map
>>> + * ________________________________________________________
>>> + * | Offset |                 Registers block             |
>>> + * --------------------------------------------------------
>>> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
>>> + * --------------------------------------------------------
>>> + * | 0x020  |                CHANNEL 1                    |
>>> + * --------------------------------------------------------
>>> + * | ...    |                .....                        |
>>> + * --------------------------------------------------------
>>> + * | 0x0E0  |                CHANNEL 7                    |
>>> + * --------------------------------------------------------
>>> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
>>> + * --------------------------------------------------------
>>> + * | 0x200  |                FILTER  1                    |
>>> + * --------------------------------------------------------
>>> + * | 0x300  |                FILTER  2                    |
>>> + * --------------------------------------------------------
>>> + * | 0x400  |                FILTER  3                    |
>>> + * --------------------------------------------------------
>>> + */
>>> +
>>> +/*
>>> + * Channels register 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 register 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)
>>> +
>>> +/* 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,
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
>>> + * TODO: complete structure.
>> nice :) RFC I guess :)
>>> + * @id:		filetr ID,
>>> + */
>>> +struct stm32_dfsdm_filter {
>>> +	unsigned int id;
>>> +	unsigned int iosr; /* integrator oversampling */
>>> +	unsigned int fosr; /* filter oversampling */
>>> +	enum stm32_dfsdm_sinc_order ford;
>>> +	u64 res; /* output sample resolution */
>>> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
>>> +	unsigned int fast; /* filter fast mode */
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
>>> + * TODO: complete structure.
>>> + * @id:		filetr ID,
>> filter
>>> + */
>>> +struct stm32_dfsdm_channel {
>>> +	unsigned int id;   /* id of the channel */
>>> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
>>> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
>>> +	unsigned int alt_si; /* use alternative serial input interface */
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
>>> + * @base:	control registers base cpu addr
>>> + * @phys_base:	DFSDM IP register physical address.
>>> + * @fl_list:	filter resources list
>>> + * @num_fl:	number of filter resources available
>>> + * @ch_list:	channel resources list
>>> + * @num_chs:	number of channel resources available
>>> + */
>>> +struct stm32_dfsdm {
>>> +	void __iomem	*base;
>>> +	phys_addr_t	phys_base;
>>> +	struct regmap *regmap;
>>> +	struct stm32_dfsdm_filter *fl_list;
>>> +	unsigned int num_fls;
>>> +	struct stm32_dfsdm_channel *ch_list;
>>> +	unsigned int num_chs;
>>> +	unsigned int spi_master_freq;
>>> +};
>>> +
>>> +struct stm32_dfsdm_str2field {
>>> +	const char	*name;
>>> +	unsigned int	val;
>>> +};
>>> +
>>> +/* DFSDM channel serial interface type */
>>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
>>> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
>>> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
>>> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
>>> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
>>> +	{ 0, 0},
>>> +};
>>> +
>>> +/* DFSDM channel serial spi clock source */
>>> +enum stm32_dfsdm_spi_clk_src {
>>> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
>>> +};
>>> +
>>> +/* DFSDM channel clock source */
>>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
>>> +	/* External SPI clock (CLKIN x) */
>>> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
>>> +	/* Internal SPI clock (CLKOUT) */
>>> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
>>> +	/* Internal SPI clock divided by 2 (falling edge) */
>>> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
>>> +	/* Internal SPI clock divided by 2 (falling edge) */
>>> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
>>> +	{ 0, 0 },
>>> +};
>>> +
>>> +/* DFSDM Serial interface trigger name  */
>>> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
>>> +
>>> +static inline int stm32_dfsdm_str2val(const char *str,
>>> +				      const struct stm32_dfsdm_str2field *list)
>>> +{
>>> +	const struct stm32_dfsdm_str2field *p = list;
>>> +
>>> +	for (p = list; p && p->name; p++) {
>>> +		if (!strcmp(p->name, str))
>>> +			return p->val;
>>> +	}
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>>> +			 unsigned int oversamp);
>>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
>>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
>>> +
>>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				 unsigned int ch_id);
>>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>>> +
>>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>>> +			       struct stm32_dfsdm_channel *ch);
>>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>>> +
>>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>>> +				 struct iio_dev *indio_dev,
>>> +				 struct iio_chan_spec *chan, int chan_idx);
>>> +
>>> +#endif
>>>
>>
>> --
>> 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] 109+ messages in thread

* Re: [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-25 15:53           ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 15:53 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

On 20/03/17 11:24, Arnaud Pouliquen wrote:
> Hello Jonathan
> 
> Thanks for your comments
> Few answers in-line.
> 
> Regards
> Arnaud
> 
> On 03/19/2017 11:25 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
>>> in n bit samples through a low pass filter and an integrator.
>>> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Various minor bits inline.
>>
>> I'm mostly liking this.  I do slightly wondering if semantically it
>> should be the front end that has the channels rather than the
>> backend.  Would be fiddly to do though and probably not worth the
>> hassle.
> DFSDM support the scan mode, so several front ends can be connected to
> One filter. In this case not possible to expose channel FE.
It still could but would admittedly get really fiddly and require demuxing
the scan...
> 
>>
>> Would love to see it running in a continuous mode in IIO, but
>> I guess that can follow along later.
> Yes for the rest of the management it should be quite close to the
> stm32-adc driver.
> 
>>
>> The comment about the trigger has me confused
>> - perhaps you could elaborate further on that?
> Code associated to the trigger should be part of the [PATCH v3 06/11]
> IIO: ADC: add stm32 DFSDM support for PDM microphone, as it concern the
> audio part...
> I did not found a way to use consumer.h interface to enable DFSDM IIO,
> without defining triggered buffer. that's why i defined a trigger and
> use it.
> But i just saw that my reasoning is wrong. I'm linked to trigger in
> stm32-dfsdm-audio.c because i use iio_triggered_buffer_postenable and
> iio_triggered_buffer_predisable.
This used to be more obvious until we put those boiler plate functions in
to avoid lots of replication.  Pretty much everything should be optional.
> As i don't use the callback for buffer
> no need to call it...i can call the ASoC callback directly in DMA IRQ.
> Still a hack but more logic...
Cool.  We definitely need to clean this up long term, but perhaps not
as part of this initially at least!

I hate holding drivers up for internal stuff that we can change in
our own good time!
> 
>>
>> Jonathan
>>> ---
>>> V2 -> V3 :
>>> 	- Split audio and ADC support in 2 drivers
>>> 	- Implement DMA cyclic mode
>>> 	- Add SPI bus Trigger for buffer management
>>>
>>>  drivers/iio/adc/Kconfig            |  26 ++
>>>  drivers/iio/adc/Makefile           |   2 +
>>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>>>  5 files changed, 1477 insertions(+)
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index d411d66..3e0eb11 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -452,6 +452,32 @@ config STM32_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-adc.
>>>  
>>> +config STM32_DFSDM_CORE
>>> +	tristate "STMicroelectronics STM32 dfsdm core"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select REGMAP
>>> +	select REGMAP_MMIO
>>> +	help
>>> +	  Select this option to enable the  driver for STMicroelectronics
>>> +	  STM32 digital filter for sigma delta converter.
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-dfsdm-core.
>>> +
>>> +config STM32_DFSDM_ADC
>>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select STM32_DFSDM_CORE
>>> +	select REGMAP_MMIO
>>> +	select IIO_BUFFER_DMAENGINE
>>> +	select IIO_HW_CONSUMER
>>> +	help
>>> +	  Select this option to support ADCSigma delta modulator for
>>> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
>>> +
>>> +	  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 c68819c..161f271 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>>> @@ -0,0 +1,419 @@
>>> +/*
>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, 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/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +#define DFSDM_TIMEOUT_US 100000
>>> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
>>> +
>>> +struct stm32_dfsdm_adc {
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	unsigned int fl_id;
>>> +	unsigned int ch_id;
>>> +
>>> +	unsigned int oversamp;
>>> +
>>> +	struct completion completion;
>>> +
>>> +	u32 *buffer;
>>> +
>>> +	/* Hardware consumer structure for Front End IIO */
>>> +	struct iio_hw_consumer *hwc;
>>> +};
>>> +
>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_dfsdm;
>>> +
>>> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	return 0;
>>> +
>>> +stop_channels:
>>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>>> +stop_dfsdm:
>>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
>>> +
>>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>>> +
>>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>>> +}
>>> +
>>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>>> +				   const struct iio_chan_spec *chan, int *res)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	long timeout;
>>> +	int ret;
>>> +
>>> +	reinit_completion(&adc->completion);
>>> +
>>> +	adc->buffer = res;
>>> +
>>> +	/* Unmask IRQ for regular conversion achievement*/
>>> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_conv(adc);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
>>> +							    DFSDM_TIMEOUT);
>> blank line perhaps.
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +
>>> +	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", *res);
>>> +		ret = IIO_VAL_INT;
>>> +	}
>>> +
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +
>>> +	stm32_dfsdm_stop_conv(adc);
>>> +
>>> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
>>> +	int ret = -EINVAL;
>>> +
>>> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
>>> +		if (!ret)
>>> +			adc->oversamp = val;
>>> +	}
>> blank line here.
>>> +	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);
>>> +	int ret;
>>> +
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_RAW:
>>> +		ret = iio_hw_consumer_enable(adc->hwc);
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"%s: IIO enable failed (channel %d)\n",
>>> +				__func__, chan->channel);
>>> +			return ret;
>>> +		}
>>> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"%s: Conversion failed (channel %d)\n",
>>> +				__func__, chan->channel);
>>> +			return ret;
>>> +		}
>>> +
>>> +		iio_hw_consumer_disable(adc->hwc);
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		*val = adc->oversamp;
>>> +
>>> +		return IIO_VAL_INT;
>>> +	}
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info stm32_dfsdm_info_adc = {
>>> +	.read_raw = stm32_dfsdm_read_raw,
>>> +	.write_raw = stm32_dfsdm_write_raw,
>>> +	.driver_module = THIS_MODULE,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = arg;
>>> +	struct regmap *regmap = adc->dfsdm->regmap;
>>> +	unsigned int status;
>>> +
>>> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
>>> +
>>> +	if (status & DFSDM_ISR_REOCF_MASK) {
>>> +		/* read the data register clean the IRQ status */
>>> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
>>> +		complete(&adc->completion);
>>> +	}
>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>> What's this one?  Might want a comment given it's an irq you basically eat.
> Yes  at least an error message that to inform on an overrun.
>>> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>> +	}
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +
>>> +	return stm32_dfsdm_start_conv(adc);
>>> +}
>>> +
>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +
>>> +	stm32_dfsdm_stop_conv(adc);
>> blank line.
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>> +	.postenable = &stm32_dfsdm_postenable,
>>> +	.predisable = &stm32_dfsdm_predisable,
>>> +};
>>> +
>>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>>> +					 struct iio_chan_spec *chan,
>>> +					 int ch_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[ch_idx];
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = ch_idx;
>>> +
>>> +	/*
>>> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>>> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
>>> +	 */
>>> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>>> +
>>> +	ch->scan_type.sign = 'u';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +	adc->ch_id = ch->channel;
>>> +
>>> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
>>> +					  &adc->dfsdm->ch_list[ch->channel]);
>>> +}
>>> +
>>> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
>>> +{
>>> +	struct iio_chan_spec *channels;
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	unsigned int num_ch;
>>> +	int ret, chan_idx;
>>> +
>>> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
>>> +					     "st,adc-channels");
>>> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
>>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>>> +		return num_ch < 0 ? num_ch : -EINVAL;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Number of channel per filter is temporary limited to 1.
>>> +	 * Restriction should be cleaned with scan mode
>>> +	 */
>>> +	if (num_ch > 1) {
>>> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	/* Bind to SD modulator IIO device */
>>> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>>> +	if (IS_ERR(adc->hwc))
>>> +		return -EPROBE_DEFER;
>>> +
>>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>>> +				GFP_KERNEL);
>>> +	if (!channels)
>>> +		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 free_hwc;
>>> +	}
>>> +
>>> +	indio_dev->num_channels = num_ch;
>>> +	indio_dev->channels = channels;
>>> +
>>> +	return 0;
>>> +
>>> +free_hwc:
>>> +	iio_hw_consumer_free(adc->hwc);
>> Given you have to free this in the error path, I would imagine you will
>> need a free somewhere in the main remove path?  Or just create a devm
>> version of iio_hw_consumer_alloc.  It will be useful in the long run.
>>> +	return ret;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
>>> +	{ .compatible = "st,stm32-dfsdm-adc"},
>>> +	{}
>>> +};
>>> +
>>> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>>> +{
>>> +	struct device *dev = &pdev->dev;
>>> +	struct stm32_dfsdm_adc *adc;
>>> +	struct device_node *np = dev->of_node;
>>> +	struct iio_dev *iio;
>>> +	char *name;
>>> +	int ret, irq, val;
>>> +
>>> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
>>> +	if (IS_ERR(iio)) {
>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>> +		return PTR_ERR(iio);
>>> +	}
>>> +
>>> +	adc = iio_priv(iio);
>>> +	if (IS_ERR(adc)) {
>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>> +		return PTR_ERR(adc);
>>> +	}
>>> +	adc->dfsdm = dev_get_drvdata(dev->parent);
>>> +
>>> +	iio->dev.parent = dev;
>>> +	iio->dev.of_node = np;
>>> +	iio->info = &stm32_dfsdm_info_adc;
>>> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
>>> +
>>> +	platform_set_drvdata(pdev, adc);
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
>>> +	if (ret != 0) {
>>> +		dev_err(dev, "Missing reg property\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
>> not freed.  Maybe devm_kzalloc
>>> +	if (!name)
>>> +		return -ENOMEM;
>>> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
>>> +	iio->name = name;
>>> +
>>> +	/*
>>> +	 * In a first step IRQs generated for channels are not treated.
>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>> +	 */
>>> +	irq = platform_get_irq(pdev, 0);
>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>> +			       0, pdev->name, adc);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to request IRQ\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to set filter order\n");
>>> +		return ret;
>>> +	}
>>> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>> +	if (!ret)
>>> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
>>> +
>>> +	ret = stm32_dfsdm_adc_chan_init(iio);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	init_completion(&adc->completion);
>>> +
>>> +	return iio_device_register(iio);
>>> +}
>>> +
>>> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	iio_device_unregister(iio);
>> If all you have is this in remove, you can probably get away with
>> devm_iio_device_register and get rid of the remove entirely.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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");
>>> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
>>> new file mode 100644
>>> index 0000000..488e456
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
>>> @@ -0,0 +1,658 @@
>>> +/*
>>> + * This file is part the core part STM32 DFSDM 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/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +struct stm32_dfsdm_dev_data {
>>> +	unsigned int num_filters;
>>> +	unsigned int num_channels;
>>> +	const struct regmap_config *regmap_cfg;
>>> +};
>>> +
>>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>>> +#define STM32H7_DFSDM_NUM_CHANNELS	8
>>> +
>>> +#define DFSDM_MAX_INT_OVERSAMPLING 256
>>> +
>>> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
>>> +
>>> +#define DFSDM_MAX_RES BIT(31)
>>> +#define DFSDM_DATA_RES BIT(23)
>>> +
>>> +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 = 0x2B8,
>>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>>> +	.fast_io = true,
>>> +};
>>> +
>>> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
>>> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
>>> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
>>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>>> +};
>>> +
>>> +struct dfsdm_priv {
>>> +	struct platform_device *pdev; /* platform device*/
>>> +
>>> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
>>> +
>>> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
>>> +	atomic_t n_active_ch;	/* number of current active channels */
>>> +
>>> +	/* Clock */
>>> +	struct clk *clk; /* DFSDM clock */
>>> +	struct clk *aclk; /* audio clock */
>>> +};
>>> +
>>> +/**
>>> + * stm32_dfsdm_set_osrs - compute filter parameters.
>> Naming would suggest it's more specific than this.
>> Setting over sampling ratios?
> Right, it is a computation not a set.
>>> + *
>>> + * Enable interface if n_active_ch is not null.
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fast: Fast mode enabled or disabled
>>> + * @oversamp: Expected oversampling between filtered sample and SD input stream
>>> + */
>>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>>> +			 unsigned int oversamp)
>>> +{
>>> +	unsigned int i, d, fosr, iosr;
>>> +	u64 res;
>>> +	s64 delta;
>>> +	unsigned int m = 1;	/* multiplication factor */
>>> +	unsigned int p = fl->ford;	/* filter order (ford) */
>>> +
>>> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
>>> +	/*
>>> +	 * This function tries to compute filter oversampling and integrator
>>> +	 * oversampling, base on oversampling ratio requested by user.
>>> +	 *
>>> +	 * Decimation d depends on the filter order and the oversampling ratios.
>>> +	 * ford: filter order
>>> +	 * fosr: filter over sampling ratio
>>> +	 * iosr: integrator over sampling ratio
>>> +	 */
>>> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
>>> +		m = 2;
>>> +		p = 2;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Looks for filter and integrator oversampling ratios which allows
>>> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
>>> +				d = fosr * (iosr + 3) + 2;
>>> +			else
>>> +				d = fosr * (iosr - 1 + p) + p;
>>> +
>>> +			if (d > oversamp)
>>> +				break;
>>> +			else if (d != oversamp)
>>> +				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 > DFSDM_MAX_RES)
>>> +					break;
>>> +			}
>>> +			if (res > DFSDM_MAX_RES)
>>> +				continue;
>>> +			res = res * (u64)m * (u64)iosr;
>>> +			if (res > DFSDM_MAX_RES)
>>> +				continue;
>>> +
>>> +			delta = res - DFSDM_DATA_RES;
>>> +
>>> +			if (res >= fl->res) {
>>> +				fl->res = res;
>>> +				fl->fosr = fosr;
>>> +				fl->iosr = iosr;
>>> +				fl->fast = fast;
>>> +				pr_debug("%s: fosr = %d, iosr = %d\n",
>>> +					 __func__, fl->fosr, fl->iosr);
>>> +			}
>>> +
>>> +			if (!delta)
>>> +				return 0;
>>> +		}
>>> +	}
>>> +
>>> +	if (!fl->fosr)
>>> +		return -EINVAL;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
>>> + *
>>> + * Enable interface if n_active_ch is not null.
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + */
>>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	struct device *dev = &priv->pdev->dev;
>>> +	unsigned int clk_div = priv->spi_clk_out_div;
>>> +	int ret;
>>> +
>>> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
>>> +		/* Enable clocks */
>>> +		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");
>>> +				goto disable_clk;
>>> +			}
>>> +		}
>>> +
>>> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>>> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
>>> +		if (ret < 0)
>>> +			goto disable_aclk;
>>> +
>>> +		/* Global enable of DFSDM interface */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>>> +					 DFSDM_CHCFGR1_DFSDMEN(1));
>>> +		if (ret < 0)
>>> +			goto disable_aclk;
>>> +	}
>>> +
>>> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
>>> +		atomic_read(&priv->n_active_ch));
>>> +
>>> +	return 0;
>>> +
>>> +disable_aclk:
>>> +	clk_disable_unprepare(priv->aclk);
>>> +disable_clk:
>>> +	clk_disable_unprepare(priv->clk);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
>>> + *
>>> + * Disable interface if n_active_ch is null
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + */
>>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	int ret;
>>> +
>>> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
>>> +		/* Global disable of DFSDM interface */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>>> +					 DFSDM_CHCFGR1_DFSDMEN(0));
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		/* Stop SPI CLKOUT */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>>> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		/* Disable clocks */
>>> +		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));
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_channel
>>> + *	Start DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: Channel index.
>>> + */
>>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>>> +{
>>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>>> +				  DFSDM_CHCFGR1_CHEN_MASK,
>>> +				  DFSDM_CHCFGR1_CHEN(1));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_channel
>>> + *	Stop DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: Channel index.
>>> + */
>>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>>> +{
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>>> +			   DFSDM_CHCFGR1_CHEN_MASK,
>>> +			   DFSDM_CHCFGR1_CHEN(0));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_chan_configure
>>> + *	Configure DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: channel index.
>>> + */
>>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>>> +			       struct stm32_dfsdm_channel *ch)
>>> +{
>>> +	unsigned int id = ch->id;
>>> +	struct regmap *regmap = dfsdm->regmap;
>>> +	int ret;
>>> +
>>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				 DFSDM_CHCFGR1_SITP_MASK,
>>> +				 DFSDM_CHCFGR1_SITP(ch->type));
>>> +	if (ret < 0)
>>> +		return ret;
>> Blank line here and in similar places makes it easier for my
>> eyes to parse at least...
>> I'd also like to see some docs in here, not all of these
>> are self explanatory.
> I will apply recommendation in my whole code for next time
>>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
>>> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
>>> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter index.
>>> + */
>>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>>> +{
>>> +	int ret;
>>> +
>>> +	/* Enable filter */
>>> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	/* Start conversion */
>>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +				  DFSDM_CR1_RSWSTART_MASK,
>>> +				  DFSDM_CR1_RSWSTART(1));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter index.
>>> + */
>>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>>> +{
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +	/* Disable conversion */
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
>>> + *	to channel.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: channel index.
>>> + * @fl_id: Filter index.
>>> + */
>>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				 unsigned int ch_id)
>>> +{
>>> +	struct regmap *regmap = dfsdm->regmap;
>>> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
>>> +	int ret;
>>> +
>>> +	/* Average integrator oversampling */
>>> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
>>> +				 DFSDM_FCR_IOSR(fl->iosr));
>>> +
>>> +	/* Filter order and Oversampling */
>> Please handle each error properly as it happens rather than mudling onwards.
>> If there is reason for this odd construction, then document it clearly.
> If you mention the checks on ret value that are missing at end of
> functions, yes dirty code to fix.
> 
>>> +	if (!ret)
>>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>>> +					 DFSDM_FCR_FOSR_MASK,
>>> +					 DFSDM_FCR_FOSR(fl->fosr));
>>> +
>>> +	if (!ret)
>>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>>> +					 DFSDM_FCR_FORD_MASK,
>>> +					 DFSDM_FCR_FORD(fl->ford));
>>> +
>>> +	/* If only one channel no scan mode supported for the moment */
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>>> +				 DFSDM_CR1_RCH_MASK,
>>> +				 DFSDM_CR1_RCH(ch_id));
>>> +
>>> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>>> +					 DFSDM_CR1_RSYNC_MASK,
>>> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
>>> +}
>>> +
>>> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
>>> +	.owner = THIS_MODULE,
>>> +};
>>> +
>>> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
>>> +					 struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	/*
>>> +	 * To be able to use buffer consumer interface a trigger is needed.
>>> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
>>> +	 * sense to define the serial interface ( SPI or manchester) as
>>> +	 * trigger source.
>> It's not actually the case that you have to have a triggrer.
>> There are plenty of drivers (particularly ones with hardware buffering)
>> where there is no trigger envolved.  That's not to say it doesn't make sense
>> here.
>>
>> I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
>> somewhat...
>>> +	 */
>>> +
>>> +	struct iio_trigger *trig;
>>> +	int ret;
>>> +
>>> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
>>> +	if (!trig)
>>> +		return -ENOMEM;
>>> +
>>> +	trig->dev.parent = pdev->dev.parent;
>>> +	trig->ops = &dfsdm_trigger_ops;
>>> +
>>> +	iio_trigger_set_drvdata(trig, dfsdm);
>>> +
>>> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
>>> +	if (ret)
>>> +		return ret;
>> Just return ret in all cases.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>>> +				 struct iio_dev *indio_dev,
>>> +				 struct iio_chan_spec *chan, int chan_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>>> +	struct stm32_dfsdm_channel *df_ch;
>>> +	const char *of_str;
>>> +	int ret, val;
>>> +
>>> +	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;
>>> +	}
>>> +
>>> +	df_ch =  &dfsdm->ch_list[ch->channel];
>> Extra space on the line above.
>>> +	df_ch->id = ch->channel;
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-types", chan_idx,
>>> +					    &of_str);
>>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
>>> +	if (ret < 0 || val < 0)
>>> +		df_ch->type = 0;
>>> +	else
>>> +		df_ch->type = val;
>>> +
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-clk-src", chan_idx,
>>> +					    &of_str);
>>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
>>> +	if (ret < 0 || val < 0)
>>> +		df_ch->src = 0;
>>> +	else
>>> +		df_ch->src = val;
>>> +
>>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> +					 "st,adc-alt-channel", chan_idx,
>>> +					 &df_ch->alt_si);
>>> +	if (ret < 0)
>>> +		df_ch->alt_si = 0;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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;
>>> +	unsigned long clk_freq;
>>> +	unsigned int spi_freq, rem;
>>> +	int ret;
>>> +
>>> +	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->dfsdm.phys_base = res->start;
>>> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
>>> +
>>> +	/* Source clock */
>>> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
>>> +	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");
>>> +	if (IS_ERR(priv->aclk))
>>> +		priv->aclk = NULL;
>>> +
>>> +	if (priv->aclk)
>>> +		clk_freq = clk_get_rate(priv->aclk);
>>> +	else
>>> +		clk_freq = clk_get_rate(priv->clk);
>>> +
>>> +	/* SPI clock freq */
>>> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
>>> +				   &spi_freq);
>>> +	if (ret < 0) {
>>> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
>>> +	priv->dfsdm.spi_master_freq = spi_freq;
>>> +
>>> +	if (rem) {
>>> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
>>> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
>>> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
>>> +	}
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +static const struct of_device_id stm32_dfsdm_of_match[] = {
>>> +	{
>>> +		.compatible = "st,stm32h7-dfsdm",
>>> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	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_dfsdm_dev_data *)of_id->data;
>>> +	dfsdm = &priv->dfsdm;
>>> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
>>> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
>>> +	if (!dfsdm->fl_list)
>>> +		return -ENOMEM;
>>> +
>>> +	dfsdm->num_fls = dev_data->num_filters;
>>> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
>>> +				      sizeof(*dfsdm->ch_list),
>>> +				      GFP_KERNEL);
>>> +	if (!dfsdm->ch_list)
>>> +		return -ENOMEM;
>>> +	dfsdm->num_chs = dev_data->num_channels;
>>> +
>>> +	ret = stm32_dfsdm_parse_of(pdev, priv);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
>>> +					    &stm32h7_dfsdm_regmap_cfg);
>>> +	if (IS_ERR(dfsdm->regmap)) {
>>> +		ret = PTR_ERR(dfsdm->regmap);
>>> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
>>> +			__func__, ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
>>> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
>>> +
>>> +		fl->id = i;
>> I'd like a comment on why this is needed...
>  to  be cleaned.
>>> +	}
>>> +
>>> +	platform_set_drvdata(pdev, dfsdm);
>>> +
>>> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
>>> +}
>>> +
>>> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
>>> new file mode 100644
>>> index 0000000..bb7d74f
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm.h
>>> @@ -0,0 +1,371 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM 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__H
>>> +#define MDF_STM32_DFSDM__H
>>> +
>>> +#include <linux/bitfield.h>
>>> +
>>> +#include <linux/iio/iio.h>
>>> +/*
>>> + * STM32 DFSDM - global register map
>>> + * ________________________________________________________
>>> + * | Offset |                 Registers block             |
>>> + * --------------------------------------------------------
>>> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
>>> + * --------------------------------------------------------
>>> + * | 0x020  |                CHANNEL 1                    |
>>> + * --------------------------------------------------------
>>> + * | ...    |                .....                        |
>>> + * --------------------------------------------------------
>>> + * | 0x0E0  |                CHANNEL 7                    |
>>> + * --------------------------------------------------------
>>> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
>>> + * --------------------------------------------------------
>>> + * | 0x200  |                FILTER  1                    |
>>> + * --------------------------------------------------------
>>> + * | 0x300  |                FILTER  2                    |
>>> + * --------------------------------------------------------
>>> + * | 0x400  |                FILTER  3                    |
>>> + * --------------------------------------------------------
>>> + */
>>> +
>>> +/*
>>> + * Channels register 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 register 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)
>>> +
>>> +/* 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,
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
>>> + * TODO: complete structure.
>> nice :) RFC I guess :)
>>> + * @id:		filetr ID,
>>> + */
>>> +struct stm32_dfsdm_filter {
>>> +	unsigned int id;
>>> +	unsigned int iosr; /* integrator oversampling */
>>> +	unsigned int fosr; /* filter oversampling */
>>> +	enum stm32_dfsdm_sinc_order ford;
>>> +	u64 res; /* output sample resolution */
>>> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
>>> +	unsigned int fast; /* filter fast mode */
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
>>> + * TODO: complete structure.
>>> + * @id:		filetr ID,
>> filter
>>> + */
>>> +struct stm32_dfsdm_channel {
>>> +	unsigned int id;   /* id of the channel */
>>> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
>>> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
>>> +	unsigned int alt_si; /* use alternative serial input interface */
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
>>> + * @base:	control registers base cpu addr
>>> + * @phys_base:	DFSDM IP register physical address.
>>> + * @fl_list:	filter resources list
>>> + * @num_fl:	number of filter resources available
>>> + * @ch_list:	channel resources list
>>> + * @num_chs:	number of channel resources available
>>> + */
>>> +struct stm32_dfsdm {
>>> +	void __iomem	*base;
>>> +	phys_addr_t	phys_base;
>>> +	struct regmap *regmap;
>>> +	struct stm32_dfsdm_filter *fl_list;
>>> +	unsigned int num_fls;
>>> +	struct stm32_dfsdm_channel *ch_list;
>>> +	unsigned int num_chs;
>>> +	unsigned int spi_master_freq;
>>> +};
>>> +
>>> +struct stm32_dfsdm_str2field {
>>> +	const char	*name;
>>> +	unsigned int	val;
>>> +};
>>> +
>>> +/* DFSDM channel serial interface type */
>>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
>>> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
>>> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
>>> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
>>> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
>>> +	{ 0, 0},
>>> +};
>>> +
>>> +/* DFSDM channel serial spi clock source */
>>> +enum stm32_dfsdm_spi_clk_src {
>>> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
>>> +};
>>> +
>>> +/* DFSDM channel clock source */
>>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
>>> +	/* External SPI clock (CLKIN x) */
>>> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
>>> +	/* Internal SPI clock (CLKOUT) */
>>> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
>>> +	/* Internal SPI clock divided by 2 (falling edge) */
>>> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
>>> +	/* Internal SPI clock divided by 2 (falling edge) */
>>> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
>>> +	{ 0, 0 },
>>> +};
>>> +
>>> +/* DFSDM Serial interface trigger name  */
>>> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
>>> +
>>> +static inline int stm32_dfsdm_str2val(const char *str,
>>> +				      const struct stm32_dfsdm_str2field *list)
>>> +{
>>> +	const struct stm32_dfsdm_str2field *p = list;
>>> +
>>> +	for (p = list; p && p->name; p++) {
>>> +		if (!strcmp(p->name, str))
>>> +			return p->val;
>>> +	}
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>>> +			 unsigned int oversamp);
>>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
>>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
>>> +
>>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				 unsigned int ch_id);
>>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>>> +
>>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>>> +			       struct stm32_dfsdm_channel *ch);
>>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>>> +
>>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>>> +				 struct iio_dev *indio_dev,
>>> +				 struct iio_chan_spec *chan, int chan_idx);
>>> +
>>> +#endif
>>>
>>
>> --
>> 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] 109+ messages in thread

* [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
@ 2017-03-25 15:53           ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 15:53 UTC (permalink / raw)
  To: linux-arm-kernel

On 20/03/17 11:24, Arnaud Pouliquen wrote:
> Hello Jonathan
> 
> Thanks for your comments
> Few answers in-line.
> 
> Regards
> Arnaud
> 
> On 03/19/2017 11:25 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
>>> in n bit samples through a low pass filter and an integrator.
>>> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Various minor bits inline.
>>
>> I'm mostly liking this.  I do slightly wondering if semantically it
>> should be the front end that has the channels rather than the
>> backend.  Would be fiddly to do though and probably not worth the
>> hassle.
> DFSDM support the scan mode, so several front ends can be connected to
> One filter. In this case not possible to expose channel FE.
It still could but would admittedly get really fiddly and require demuxing
the scan...
> 
>>
>> Would love to see it running in a continuous mode in IIO, but
>> I guess that can follow along later.
> Yes for the rest of the management it should be quite close to the
> stm32-adc driver.
> 
>>
>> The comment about the trigger has me confused
>> - perhaps you could elaborate further on that?
> Code associated to the trigger should be part of the [PATCH v3 06/11]
> IIO: ADC: add stm32 DFSDM support for PDM microphone, as it concern the
> audio part...
> I did not found a way to use consumer.h interface to enable DFSDM IIO,
> without defining triggered buffer. that's why i defined a trigger and
> use it.
> But i just saw that my reasoning is wrong. I'm linked to trigger in
> stm32-dfsdm-audio.c because i use iio_triggered_buffer_postenable and
> iio_triggered_buffer_predisable.
This used to be more obvious until we put those boiler plate functions in
to avoid lots of replication.  Pretty much everything should be optional.
> As i don't use the callback for buffer
> no need to call it...i can call the ASoC callback directly in DMA IRQ.
> Still a hack but more logic...
Cool.  We definitely need to clean this up long term, but perhaps not
as part of this initially at least!

I hate holding drivers up for internal stuff that we can change in
our own good time!
> 
>>
>> Jonathan
>>> ---
>>> V2 -> V3 :
>>> 	- Split audio and ADC support in 2 drivers
>>> 	- Implement DMA cyclic mode
>>> 	- Add SPI bus Trigger for buffer management
>>>
>>>  drivers/iio/adc/Kconfig            |  26 ++
>>>  drivers/iio/adc/Makefile           |   2 +
>>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 419 +++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm.h      | 372 +++++++++++++++++++++
>>>  5 files changed, 1477 insertions(+)
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index d411d66..3e0eb11 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -452,6 +452,32 @@ config STM32_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-adc.
>>>  
>>> +config STM32_DFSDM_CORE
>>> +	tristate "STMicroelectronics STM32 dfsdm core"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select REGMAP
>>> +	select REGMAP_MMIO
>>> +	help
>>> +	  Select this option to enable the  driver for STMicroelectronics
>>> +	  STM32 digital filter for sigma delta converter.
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-dfsdm-core.
>>> +
>>> +config STM32_DFSDM_ADC
>>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select STM32_DFSDM_CORE
>>> +	select REGMAP_MMIO
>>> +	select IIO_BUFFER_DMAENGINE
>>> +	select IIO_HW_CONSUMER
>>> +	help
>>> +	  Select this option to support ADCSigma delta modulator for
>>> +	  STMicroelectronics STM32 digital filter for sigma delta converter.
>>> +
>>> +	  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 c68819c..161f271 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -43,6 +43,8 @@ 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_STM32_DFSDM_CORE) += stm32-dfsdm-core.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..ebcb3b4
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>>> @@ -0,0 +1,419 @@
>>> +/*
>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, 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/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +#define DFSDM_TIMEOUT_US 100000
>>> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
>>> +
>>> +struct stm32_dfsdm_adc {
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	unsigned int fl_id;
>>> +	unsigned int ch_id;
>>> +
>>> +	unsigned int oversamp;
>>> +
>>> +	struct completion completion;
>>> +
>>> +	u32 *buffer;
>>> +
>>> +	/* Hardware consumer structure for Front End IIO */
>>> +	struct iio_hw_consumer *hwc;
>>> +};
>>> +
>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_dfsdm;
>>> +
>>> +	ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	return 0;
>>> +
>>> +stop_channels:
>>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>>> +stop_dfsdm:
>>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
>>> +
>>> +	stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>>> +
>>> +	stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>>> +}
>>> +
>>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>>> +				   const struct iio_chan_spec *chan, int *res)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	long timeout;
>>> +	int ret;
>>> +
>>> +	reinit_completion(&adc->completion);
>>> +
>>> +	adc->buffer = res;
>>> +
>>> +	/* Unmask IRQ for regular conversion achievement*/
>>> +	ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +				 DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_conv(adc);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
>>> +							    DFSDM_TIMEOUT);
>> blank line perhaps.
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +
>>> +	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", *res);
>>> +		ret = IIO_VAL_INT;
>>> +	}
>>> +
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +
>>> +	stm32_dfsdm_stop_conv(adc);
>>> +
>>> +	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_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
>>> +	int ret = -EINVAL;
>>> +
>>> +	if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, val);
>>> +		if (!ret)
>>> +			adc->oversamp = val;
>>> +	}
>> blank line here.
>>> +	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);
>>> +	int ret;
>>> +
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_RAW:
>>> +		ret = iio_hw_consumer_enable(adc->hwc);
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"%s: IIO enable failed (channel %d)\n",
>>> +				__func__, chan->channel);
>>> +			return ret;
>>> +		}
>>> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"%s: Conversion failed (channel %d)\n",
>>> +				__func__, chan->channel);
>>> +			return ret;
>>> +		}
>>> +
>>> +		iio_hw_consumer_disable(adc->hwc);
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		*val = adc->oversamp;
>>> +
>>> +		return IIO_VAL_INT;
>>> +	}
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info stm32_dfsdm_info_adc = {
>>> +	.read_raw = stm32_dfsdm_read_raw,
>>> +	.write_raw = stm32_dfsdm_write_raw,
>>> +	.driver_module = THIS_MODULE,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = arg;
>>> +	struct regmap *regmap = adc->dfsdm->regmap;
>>> +	unsigned int status;
>>> +
>>> +	regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
>>> +
>>> +	if (status & DFSDM_ISR_REOCF_MASK) {
>>> +		/* read the data register clean the IRQ status */
>>> +		regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
>>> +		complete(&adc->completion);
>>> +	}
>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>> What's this one?  Might want a comment given it's an irq you basically eat.
> Yes  at least an error message that to inform on an overrun.
>>> +		regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>> +	}
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +
>>> +	return stm32_dfsdm_start_conv(adc);
>>> +}
>>> +
>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +
>>> +	stm32_dfsdm_stop_conv(adc);
>> blank line.
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>> +	.postenable = &stm32_dfsdm_postenable,
>>> +	.predisable = &stm32_dfsdm_predisable,
>>> +};
>>> +
>>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>>> +					 struct iio_chan_spec *chan,
>>> +					 int ch_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[ch_idx];
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = ch_idx;
>>> +
>>> +	/*
>>> +	 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>>> +	 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
>>> +	 */
>>> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +				 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>>> +
>>> +	ch->scan_type.sign = 'u';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +	adc->ch_id = ch->channel;
>>> +
>>> +	return stm32_dfsdm_chan_configure(adc->dfsdm,
>>> +					  &adc->dfsdm->ch_list[ch->channel]);
>>> +}
>>> +
>>> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
>>> +{
>>> +	struct iio_chan_spec *channels;
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	unsigned int num_ch;
>>> +	int ret, chan_idx;
>>> +
>>> +	num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
>>> +					     "st,adc-channels");
>>> +	if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
>>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>>> +		return num_ch < 0 ? num_ch : -EINVAL;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Number of channel per filter is temporary limited to 1.
>>> +	 * Restriction should be cleaned with scan mode
>>> +	 */
>>> +	if (num_ch > 1) {
>>> +		dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	/* Bind to SD modulator IIO device */
>>> +	adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>>> +	if (IS_ERR(adc->hwc))
>>> +		return -EPROBE_DEFER;
>>> +
>>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>>> +				GFP_KERNEL);
>>> +	if (!channels)
>>> +		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 free_hwc;
>>> +	}
>>> +
>>> +	indio_dev->num_channels = num_ch;
>>> +	indio_dev->channels = channels;
>>> +
>>> +	return 0;
>>> +
>>> +free_hwc:
>>> +	iio_hw_consumer_free(adc->hwc);
>> Given you have to free this in the error path, I would imagine you will
>> need a free somewhere in the main remove path?  Or just create a devm
>> version of iio_hw_consumer_alloc.  It will be useful in the long run.
>>> +	return ret;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
>>> +	{ .compatible = "st,stm32-dfsdm-adc"},
>>> +	{}
>>> +};
>>> +
>>> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>>> +{
>>> +	struct device *dev = &pdev->dev;
>>> +	struct stm32_dfsdm_adc *adc;
>>> +	struct device_node *np = dev->of_node;
>>> +	struct iio_dev *iio;
>>> +	char *name;
>>> +	int ret, irq, val;
>>> +
>>> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
>>> +	if (IS_ERR(iio)) {
>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>> +		return PTR_ERR(iio);
>>> +	}
>>> +
>>> +	adc = iio_priv(iio);
>>> +	if (IS_ERR(adc)) {
>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>> +		return PTR_ERR(adc);
>>> +	}
>>> +	adc->dfsdm = dev_get_drvdata(dev->parent);
>>> +
>>> +	iio->dev.parent = dev;
>>> +	iio->dev.of_node = np;
>>> +	iio->info = &stm32_dfsdm_info_adc;
>>> +	iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
>>> +
>>> +	platform_set_drvdata(pdev, adc);
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
>>> +	if (ret != 0) {
>>> +		dev_err(dev, "Missing reg property\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
>> not freed.  Maybe devm_kzalloc
>>> +	if (!name)
>>> +		return -ENOMEM;
>>> +	snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
>>> +	iio->name = name;
>>> +
>>> +	/*
>>> +	 * In a first step IRQs generated for channels are not treated.
>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>> +	 */
>>> +	irq = platform_get_irq(pdev, 0);
>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>> +			       0, pdev->name, adc);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to request IRQ\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to set filter order\n");
>>> +		return ret;
>>> +	}
>>> +	adc->dfsdm->fl_list[adc->fl_id].ford = val;
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>> +	if (!ret)
>>> +		adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
>>> +
>>> +	ret = stm32_dfsdm_adc_chan_init(iio);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	init_completion(&adc->completion);
>>> +
>>> +	return iio_device_register(iio);
>>> +}
>>> +
>>> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
>>> +{
>>> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	iio_device_unregister(iio);
>> If all you have is this in remove, you can probably get away with
>> devm_iio_device_register and get rid of the remove entirely.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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");
>>> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
>>> new file mode 100644
>>> index 0000000..488e456
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
>>> @@ -0,0 +1,658 @@
>>> +/*
>>> + * This file is part the core part STM32 DFSDM 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/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +struct stm32_dfsdm_dev_data {
>>> +	unsigned int num_filters;
>>> +	unsigned int num_channels;
>>> +	const struct regmap_config *regmap_cfg;
>>> +};
>>> +
>>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>>> +#define STM32H7_DFSDM_NUM_CHANNELS	8
>>> +
>>> +#define DFSDM_MAX_INT_OVERSAMPLING 256
>>> +
>>> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
>>> +
>>> +#define DFSDM_MAX_RES BIT(31)
>>> +#define DFSDM_DATA_RES BIT(23)
>>> +
>>> +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 = 0x2B8,
>>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>>> +	.fast_io = true,
>>> +};
>>> +
>>> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
>>> +	.num_filters = STM32H7_DFSDM_NUM_FILTERS,
>>> +	.num_channels = STM32H7_DFSDM_NUM_CHANNELS,
>>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>>> +};
>>> +
>>> +struct dfsdm_priv {
>>> +	struct platform_device *pdev; /* platform device*/
>>> +
>>> +	struct stm32_dfsdm dfsdm; /* common data exported for all instances */
>>> +
>>> +	unsigned int spi_clk_out_div; /* SPI clkout divider value */
>>> +	atomic_t n_active_ch;	/* number of current active channels */
>>> +
>>> +	/* Clock */
>>> +	struct clk *clk; /* DFSDM clock */
>>> +	struct clk *aclk; /* audio clock */
>>> +};
>>> +
>>> +/**
>>> + * stm32_dfsdm_set_osrs - compute filter parameters.
>> Naming would suggest it's more specific than this.
>> Setting over sampling ratios?
> Right, it is a computation not a set.
>>> + *
>>> + * Enable interface if n_active_ch is not null.
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fast: Fast mode enabled or disabled
>>> + * @oversamp: Expected oversampling between filtered sample and SD input stream
>>> + */
>>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>>> +			 unsigned int oversamp)
>>> +{
>>> +	unsigned int i, d, fosr, iosr;
>>> +	u64 res;
>>> +	s64 delta;
>>> +	unsigned int m = 1;	/* multiplication factor */
>>> +	unsigned int p = fl->ford;	/* filter order (ford) */
>>> +
>>> +	pr_debug("%s: Requested oversampling: %d\n",  __func__, oversamp);
>>> +	/*
>>> +	 * This function tries to compute filter oversampling and integrator
>>> +	 * oversampling, base on oversampling ratio requested by user.
>>> +	 *
>>> +	 * Decimation d depends on the filter order and the oversampling ratios.
>>> +	 * ford: filter order
>>> +	 * fosr: filter over sampling ratio
>>> +	 * iosr: integrator over sampling ratio
>>> +	 */
>>> +	if (fl->ford == DFSDM_FASTSINC_ORDER) {
>>> +		m = 2;
>>> +		p = 2;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Looks for filter and integrator oversampling ratios which allows
>>> +	 * 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 (fl->ford == DFSDM_FASTSINC_ORDER)
>>> +				d = fosr * (iosr + 3) + 2;
>>> +			else
>>> +				d = fosr * (iosr - 1 + p) + p;
>>> +
>>> +			if (d > oversamp)
>>> +				break;
>>> +			else if (d != oversamp)
>>> +				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 > DFSDM_MAX_RES)
>>> +					break;
>>> +			}
>>> +			if (res > DFSDM_MAX_RES)
>>> +				continue;
>>> +			res = res * (u64)m * (u64)iosr;
>>> +			if (res > DFSDM_MAX_RES)
>>> +				continue;
>>> +
>>> +			delta = res - DFSDM_DATA_RES;
>>> +
>>> +			if (res >= fl->res) {
>>> +				fl->res = res;
>>> +				fl->fosr = fosr;
>>> +				fl->iosr = iosr;
>>> +				fl->fast = fast;
>>> +				pr_debug("%s: fosr = %d, iosr = %d\n",
>>> +					 __func__, fl->fosr, fl->iosr);
>>> +			}
>>> +
>>> +			if (!delta)
>>> +				return 0;
>>> +		}
>>> +	}
>>> +
>>> +	if (!fl->fosr)
>>> +		return -EINVAL;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
>>> + *
>>> + * Enable interface if n_active_ch is not null.
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + */
>>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	struct device *dev = &priv->pdev->dev;
>>> +	unsigned int clk_div = priv->spi_clk_out_div;
>>> +	int ret;
>>> +
>>> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
>>> +		/* Enable clocks */
>>> +		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");
>>> +				goto disable_clk;
>>> +			}
>>> +		}
>>> +
>>> +		/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>>> +					 DFSDM_CHCFGR1_CKOUTDIV(clk_div));
>>> +		if (ret < 0)
>>> +			goto disable_aclk;
>>> +
>>> +		/* Global enable of DFSDM interface */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>>> +					 DFSDM_CHCFGR1_DFSDMEN(1));
>>> +		if (ret < 0)
>>> +			goto disable_aclk;
>>> +	}
>>> +
>>> +	dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
>>> +		atomic_read(&priv->n_active_ch));
>>> +
>>> +	return 0;
>>> +
>>> +disable_aclk:
>>> +	clk_disable_unprepare(priv->aclk);
>>> +disable_clk:
>>> +	clk_disable_unprepare(priv->clk);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
>>> + *
>>> + * Disable interface if n_active_ch is null
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + */
>>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	int ret;
>>> +
>>> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
>>> +		/* Global disable of DFSDM interface */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
>>> +					 DFSDM_CHCFGR1_DFSDMEN(0));
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		/* Stop SPI CLKOUT */
>>> +		ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
>>> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
>>> +		if (ret < 0)
>>> +			return ret;
>>> +
>>> +		/* Disable clocks */
>>> +		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));
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_channel
>>> + *	Start DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: Channel index.
>>> + */
>>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>>> +{
>>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>>> +				  DFSDM_CHCFGR1_CHEN_MASK,
>>> +				  DFSDM_CHCFGR1_CHEN(1));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_channel
>>> + *	Stop DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: Channel index.
>>> + */
>>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>>> +{
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>>> +			   DFSDM_CHCFGR1_CHEN_MASK,
>>> +			   DFSDM_CHCFGR1_CHEN(0));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_chan_configure
>>> + *	Configure DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: channel index.
>>> + */
>>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>>> +			       struct stm32_dfsdm_channel *ch)
>>> +{
>>> +	unsigned int id = ch->id;
>>> +	struct regmap *regmap = dfsdm->regmap;
>>> +	int ret;
>>> +
>>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				 DFSDM_CHCFGR1_SITP_MASK,
>>> +				 DFSDM_CHCFGR1_SITP(ch->type));
>>> +	if (ret < 0)
>>> +		return ret;
>> Blank line here and in similar places makes it easier for my
>> eyes to parse at least...
>> I'd also like to see some docs in here, not all of these
>> are self explanatory.
> I will apply recommendation in my whole code for next time
>>> +	ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				 DFSDM_CHCFGR1_SPICKSEL_MASK,
>>> +				 DFSDM_CHCFGR1_SPICKSEL(ch->src));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +	return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> +				  DFSDM_CHCFGR1_CHINSEL_MASK,
>>> +				  DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter index.
>>> + */
>>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>>> +{
>>> +	int ret;
>>> +
>>> +	/* Enable filter */
>>> +	ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +				 DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	/* Start conversion */
>>> +	return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +				  DFSDM_CR1_RSWSTART_MASK,
>>> +				  DFSDM_CR1_RSWSTART(1));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter index.
>>> + */
>>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>>> +{
>>> +	/* Mask IRQ for regular conversion achievement*/
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
>>> +			   DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +	/* Disable conversion */
>>> +	regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> +			   DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
>>> + *	to channel.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: channel index.
>>> + * @fl_id: Filter index.
>>> + */
>>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				 unsigned int ch_id)
>>> +{
>>> +	struct regmap *regmap = dfsdm->regmap;
>>> +	struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
>>> +	int ret;
>>> +
>>> +	/* Average integrator oversampling */
>>> +	ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
>>> +				 DFSDM_FCR_IOSR(fl->iosr));
>>> +
>>> +	/* Filter order and Oversampling */
>> Please handle each error properly as it happens rather than mudling onwards.
>> If there is reason for this odd construction, then document it clearly.
> If you mention the checks on ret value that are missing at end of
> functions, yes dirty code to fix.
> 
>>> +	if (!ret)
>>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>>> +					 DFSDM_FCR_FOSR_MASK,
>>> +					 DFSDM_FCR_FOSR(fl->fosr));
>>> +
>>> +	if (!ret)
>>> +		ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>>> +					 DFSDM_FCR_FORD_MASK,
>>> +					 DFSDM_FCR_FORD(fl->ford));
>>> +
>>> +	/* If only one channel no scan mode supported for the moment */
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>>> +				 DFSDM_CR1_RCH_MASK,
>>> +				 DFSDM_CR1_RCH(ch_id));
>>> +
>>> +	return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>>> +					 DFSDM_CR1_RSYNC_MASK,
>>> +			  DFSDM_CR1_RSYNC(fl->sync_mode));
>>> +}
>>> +
>>> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
>>> +	.owner = THIS_MODULE,
>>> +};
>>> +
>>> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
>>> +					 struct stm32_dfsdm *dfsdm)
>>> +{
>>> +	/*
>>> +	 * To be able to use buffer consumer interface a trigger is needed.
>>> +	 * As conversion are trigged by PDM samples from SPI bus, that makes
>>> +	 * sense to define the serial interface ( SPI or manchester) as
>>> +	 * trigger source.
>> It's not actually the case that you have to have a triggrer.
>> There are plenty of drivers (particularly ones with hardware buffering)
>> where there is no trigger envolved.  That's not to say it doesn't make sense
>> here.
>>
>> I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
>> somewhat...
>>> +	 */
>>> +
>>> +	struct iio_trigger *trig;
>>> +	int ret;
>>> +
>>> +	trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
>>> +	if (!trig)
>>> +		return -ENOMEM;
>>> +
>>> +	trig->dev.parent = pdev->dev.parent;
>>> +	trig->ops = &dfsdm_trigger_ops;
>>> +
>>> +	iio_trigger_set_drvdata(trig, dfsdm);
>>> +
>>> +	ret = devm_iio_trigger_register(&pdev->dev, trig);
>>> +	if (ret)
>>> +		return ret;
>> Just return ret in all cases.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>>> +				 struct iio_dev *indio_dev,
>>> +				 struct iio_chan_spec *chan, int chan_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>>> +	struct stm32_dfsdm_channel *df_ch;
>>> +	const char *of_str;
>>> +	int ret, val;
>>> +
>>> +	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;
>>> +	}
>>> +
>>> +	df_ch =  &dfsdm->ch_list[ch->channel];
>> Extra space on the line above.
>>> +	df_ch->id = ch->channel;
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-types", chan_idx,
>>> +					    &of_str);
>>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
>>> +	if (ret < 0 || val < 0)
>>> +		df_ch->type = 0;
>>> +	else
>>> +		df_ch->type = val;
>>> +
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-clk-src", chan_idx,
>>> +					    &of_str);
>>> +	val  = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
>>> +	if (ret < 0 || val < 0)
>>> +		df_ch->src = 0;
>>> +	else
>>> +		df_ch->src = val;
>>> +
>>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> +					 "st,adc-alt-channel", chan_idx,
>>> +					 &df_ch->alt_si);
>>> +	if (ret < 0)
>>> +		df_ch->alt_si = 0;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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;
>>> +	unsigned long clk_freq;
>>> +	unsigned int spi_freq, rem;
>>> +	int ret;
>>> +
>>> +	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->dfsdm.phys_base = res->start;
>>> +	priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
>>> +
>>> +	/* Source clock */
>>> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
>>> +	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");
>>> +	if (IS_ERR(priv->aclk))
>>> +		priv->aclk = NULL;
>>> +
>>> +	if (priv->aclk)
>>> +		clk_freq = clk_get_rate(priv->aclk);
>>> +	else
>>> +		clk_freq = clk_get_rate(priv->clk);
>>> +
>>> +	/* SPI clock freq */
>>> +	ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
>>> +				   &spi_freq);
>>> +	if (ret < 0) {
>>> +		dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
>>> +	priv->dfsdm.spi_master_freq = spi_freq;
>>> +
>>> +	if (rem) {
>>> +		dev_warn(&pdev->dev, "SPI clock not accurate\n");
>>> +		dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
>>> +			 clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
>>> +	}
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +static const struct of_device_id stm32_dfsdm_of_match[] = {
>>> +	{
>>> +		.compatible = "st,stm32h7-dfsdm",
>>> +		.data = &stm32h7_dfsdm_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_dfsdm_dev_data *dev_data;
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	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_dfsdm_dev_data *)of_id->data;
>>> +	dfsdm = &priv->dfsdm;
>>> +	dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
>>> +				      sizeof(*dfsdm->fl_list), GFP_KERNEL);
>>> +	if (!dfsdm->fl_list)
>>> +		return -ENOMEM;
>>> +
>>> +	dfsdm->num_fls = dev_data->num_filters;
>>> +	dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
>>> +				      sizeof(*dfsdm->ch_list),
>>> +				      GFP_KERNEL);
>>> +	if (!dfsdm->ch_list)
>>> +		return -ENOMEM;
>>> +	dfsdm->num_chs = dev_data->num_channels;
>>> +
>>> +	ret = stm32_dfsdm_parse_of(pdev, priv);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
>>> +					    &stm32h7_dfsdm_regmap_cfg);
>>> +	if (IS_ERR(dfsdm->regmap)) {
>>> +		ret = PTR_ERR(dfsdm->regmap);
>>> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
>>> +			__func__, ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
>>> +		struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
>>> +
>>> +		fl->id = i;
>> I'd like a comment on why this is needed...
>  to  be cleaned.
>>> +	}
>>> +
>>> +	platform_set_drvdata(pdev, dfsdm);
>>> +
>>> +	ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
>>> +}
>>> +
>>> +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/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
>>> new file mode 100644
>>> index 0000000..bb7d74f
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm.h
>>> @@ -0,0 +1,371 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM 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__H
>>> +#define MDF_STM32_DFSDM__H
>>> +
>>> +#include <linux/bitfield.h>
>>> +
>>> +#include <linux/iio/iio.h>
>>> +/*
>>> + * STM32 DFSDM - global register map
>>> + * ________________________________________________________
>>> + * | Offset |                 Registers block             |
>>> + * --------------------------------------------------------
>>> + * | 0x000  |      CHANNEL 0 + COMMON CHANNEL FIELDS      |
>>> + * --------------------------------------------------------
>>> + * | 0x020  |                CHANNEL 1                    |
>>> + * --------------------------------------------------------
>>> + * | ...    |                .....                        |
>>> + * --------------------------------------------------------
>>> + * | 0x0E0  |                CHANNEL 7                    |
>>> + * --------------------------------------------------------
>>> + * | 0x100  |      FILTER  0 + COMMON  FILTER FIELDs      |
>>> + * --------------------------------------------------------
>>> + * | 0x200  |                FILTER  1                    |
>>> + * --------------------------------------------------------
>>> + * | 0x300  |                FILTER  2                    |
>>> + * --------------------------------------------------------
>>> + * | 0x400  |                FILTER  3                    |
>>> + * --------------------------------------------------------
>>> + */
>>> +
>>> +/*
>>> + * Channels register 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 register 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)
>>> +
>>> +/* 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,
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
>>> + * TODO: complete structure.
>> nice :) RFC I guess :)
>>> + * @id:		filetr ID,
>>> + */
>>> +struct stm32_dfsdm_filter {
>>> +	unsigned int id;
>>> +	unsigned int iosr; /* integrator oversampling */
>>> +	unsigned int fosr; /* filter oversampling */
>>> +	enum stm32_dfsdm_sinc_order ford;
>>> +	u64 res; /* output sample resolution */
>>> +	unsigned int sync_mode; /* filter suynchronized with filter0 */
>>> +	unsigned int fast; /* filter fast mode */
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
>>> + * TODO: complete structure.
>>> + * @id:		filetr ID,
>> filter
>>> + */
>>> +struct stm32_dfsdm_channel {
>>> +	unsigned int id;   /* id of the channel */
>>> +	unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
>>> +	unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
>>> +	unsigned int alt_si; /* use alternative serial input interface */
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
>>> + * @base:	control registers base cpu addr
>>> + * @phys_base:	DFSDM IP register physical address.
>>> + * @fl_list:	filter resources list
>>> + * @num_fl:	number of filter resources available
>>> + * @ch_list:	channel resources list
>>> + * @num_chs:	number of channel resources available
>>> + */
>>> +struct stm32_dfsdm {
>>> +	void __iomem	*base;
>>> +	phys_addr_t	phys_base;
>>> +	struct regmap *regmap;
>>> +	struct stm32_dfsdm_filter *fl_list;
>>> +	unsigned int num_fls;
>>> +	struct stm32_dfsdm_channel *ch_list;
>>> +	unsigned int num_chs;
>>> +	unsigned int spi_master_freq;
>>> +};
>>> +
>>> +struct stm32_dfsdm_str2field {
>>> +	const char	*name;
>>> +	unsigned int	val;
>>> +};
>>> +
>>> +/* DFSDM channel serial interface type */
>>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
>>> +	{ "SPI_R", 0 }, /* SPI with data on rising edge */
>>> +	{ "SPI_F", 1 }, /* SPI with data on falling edge */
>>> +	{ "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
>>> +	{ "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
>>> +	{ 0, 0},
>>> +};
>>> +
>>> +/* DFSDM channel serial spi clock source */
>>> +enum stm32_dfsdm_spi_clk_src {
>>> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
>>> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
>>> +};
>>> +
>>> +/* DFSDM channel clock source */
>>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
>>> +	/* External SPI clock (CLKIN x) */
>>> +	{ "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
>>> +	/* Internal SPI clock (CLKOUT) */
>>> +	{ "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
>>> +	/* Internal SPI clock divided by 2 (falling edge) */
>>> +	{ "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
>>> +	/* Internal SPI clock divided by 2 (falling edge) */
>>> +	{ "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
>>> +	{ 0, 0 },
>>> +};
>>> +
>>> +/* DFSDM Serial interface trigger name  */
>>> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
>>> +
>>> +static inline int stm32_dfsdm_str2val(const char *str,
>>> +				      const struct stm32_dfsdm_str2field *list)
>>> +{
>>> +	const struct stm32_dfsdm_str2field *p = list;
>>> +
>>> +	for (p = list; p && p->name; p++) {
>>> +		if (!strcmp(p->name, str))
>>> +			return p->val;
>>> +	}
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>>> +			 unsigned int oversamp);
>>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
>>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
>>> +
>>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				 unsigned int ch_id);
>>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>>> +
>>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>>> +			       struct stm32_dfsdm_channel *ch);
>>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>>> +
>>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>>> +				 struct iio_dev *indio_dev,
>>> +				 struct iio_chan_spec *chan, int chan_idx);
>>> +
>>> +#endif
>>>
>>
>> --
>> 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] 109+ messages in thread

* Re: [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
  2017-03-20 11:29         ` Arnaud Pouliquen
  (?)
@ 2017-03-25 15:59           ` Jonathan Cameron
  -1 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 15:59 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, Olivier MOYSAN, kernel, linux-iio,
	Maxime Coquelin, linux-arm-kernel, Alexandre TORGUE

On 20/03/17 11:29, Arnaud Pouliquen wrote:
> Please find my comments in-line
> 
> Thanks and Regards,
> Arnaud
> 
> On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add DFSDM driver to handle PDM audio microphones.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> So key element here is that we really need to have a proper way of
>> doing DMA buffer consumers from IIO (as you said in your cover letter).
>>
>> Not entirely obvious how to do this.  Would like some input from Lars
>> if possible.  I'm afraid I don't have an suitable hardware to try anything
>> out on (or the time really!).  This will obviously be quite different from
>> the single scan stuff you have seen is there at the moment.
> I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
> DMA. I suppose that problematics are similar. Perhaps the extra
> constrains in DFSDM is the in-kernel API used to get the data.
Absolutely.  Cyclic dma is becoming more and more common on SoC ADCs.
There are a few more fpga based ones in Analog devices tree as well -
most of those actually do the dma buffer route rather than pushing through
the normal buffer interface (kfifo effectively).  Right now we just
don't have any means of accessing these dma buffers in kernel.

Unlike the normal consumer interface, I doubt we'll want to do demuxing
of the stream for this sort of usecase - feels like exclusive use is more
likely.
> 
>>
>> Whether this whole approach makes sense vs doing the dma in the alsa driver
>> isn't clear to me.
>>
>> On the plus side, presumably we'll get love dma ADC support for free
>> as part of doing it this way ;)
>>
>> It's been a while since I looked at the IIO dma buffer stuff at all. Will
>> try and refresh my memory sometime this week.
> 
> Ok, i will wait your feedback (and Lars's one) before updating my code
> for next version.
It may well be the case that it will take us sometime (when I say us, I mean
you and Lars ;) to pin down how to do dma buffer consumers, so it may be
a case of taking the driver in a state close to the current one with the
intent to switch to a generic way of handling it later.

As long as bindings and userspace don't change we can of course mess with
anything we like.

Jonathan
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/Kconfig                   |  13 +
>>>  drivers/iio/adc/Makefile                  |   1 +
>>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>>  4 files changed, 775 insertions(+)
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 3e0eb11..c108933 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-dfsdm-adc.
>>>  
>>> +config STM32_DFSDM_AUDIO
>>> +	tristate "STMicroelectronics STM32 dfsdm audio
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select STM32_DFSDM_CORE
>>> +	select REGMAP_MMIO
>>> +	select IIO_BUFFER_DMAENGINE
>>> +	help
>>> +	  Select this option to support Audio PDM micophone for
>>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-dfsdm-audio.
>>> +
>>>  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 161f271..79f975d 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>>> new file mode 100644
>>> index 0000000..115ef8f
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>>> @@ -0,0 +1,720 @@
>>> +/*
>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, 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/dmaengine.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/buffer.h>
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/sysfs.h>
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/trigger_consumer.h>
>>> +#include <linux/iio/triggered_buffer.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>>> +
>>> +struct stm32_dfsdm_audio {
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	unsigned int fl_id;
>>> +	unsigned int ch_id;
>>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>>> +
>>> +	u8 *rx_buf;
>>> +	unsigned int bufi; /* Buffer current position */
>>> +	unsigned int buf_sz; /* Buffer size */
>>> +
>>> +	struct dma_chan	*dma_chan;
>>> +	dma_addr_t dma_buf;
>>> +
>>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>>> +	void *cb_priv;
>>> +};
>>> +
>>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>>> +
>>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				    const struct iio_chan_spec *chan, char *buf)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				    const struct iio_chan_spec *chan,
>>> +				    const char *buf, size_t len)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>> +	unsigned int spi_freq = pdmc->spi_freq;
>>> +	unsigned int sample_freq;
>>> +	int ret;
>>> +
>>> +	ret = kstrtoint(buf, 0, &sample_freq);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>>> +	if (!sample_freq)
>>> +		return -EINVAL;
>>> +
>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>>> +
>>> +	if (spi_freq % sample_freq)
>>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>>> +			 spi_freq / (spi_freq / sample_freq));
>>> +
>>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			"Not able to find filter parameter that match!\n");
>>> +		return ret;
>>> +	}
>>> +	pdmc->sample_freq = sample_freq;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				      const struct iio_chan_spec *chan,
>>> +				      char *buf)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				      const struct iio_chan_spec *chan,
>>> +				      const char *buf, size_t len)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>> +	unsigned int sample_freq = pdmc->sample_freq;
>>> +	unsigned int spi_freq;
>>> +	int ret;
>>> +
>>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>> +		return -EPERM;
>>> +
>>> +	ret = kstrtoint(buf, 0, &spi_freq);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>>> +	if (!spi_freq)
>>> +		return -EINVAL;
>>> +
>>> +	if (sample_freq) {
>>> +		if (spi_freq % sample_freq)
>>> +			dev_warn(&indio_dev->dev,
>>> +				 "Sampling rate not accurate (%d)\n",
>>> +				 spi_freq / (spi_freq / sample_freq));
>>> +
>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"No filter parameters that match!\n");
>>> +			return ret;
>>> +		}
>>> +	}
>>> +	pdmc->spi_freq = spi_freq;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +/*
>>> + * Define external info for SPI Frequency and audio sampling rate that can be
>>> + * configured by ASoC driver through consumer.h API
>>> + */
>>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>>> +	/* filter oversampling: Post filter oversampling ratio */
>>> +	{
>>> +		.name = "audio_sampling_rate",
>>> +		.shared = IIO_SHARED_BY_TYPE,
>>> +		.read = dfsdm_audio_get_rate,
>>> +		.write = dfsdm_audio_set_rate,
>>> +	},
>>> +	/* data_right_bit_shift : Filter output data shifting */
>>> +	{
>>> +		.name = "spi_clk_freq",
>>> +		.shared = IIO_SHARED_BY_TYPE,
>>> +		.read = dfsdm_audio_get_spiclk,
>>> +		.write = dfsdm_audio_set_spiclk,
>>> +	},
>>> +	{},
>>> +};
>>> +
>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>>> +{
>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_dfsdm;
>>> +
>>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>>> +					   pdmc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	/* Enable DMA transfer*/
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	/* Enable conversion triggered by SPI clock*/
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	return 0;
>>> +
>>> +stop_channels:
>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>>> +stop_dfsdm:
>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>>> +{
>>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>>> +
>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>>> +
>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>> +}
>>> +
>>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>>> +				     unsigned int val)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>>> +
>>> +	/*
>>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>>> +	 * There should be :
>>> +	 * - always one buffer (period) DMA is working on
>>> +	 * - one buffer (period) driver pushed to ASoC side ().
>>> +	 */
>>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>>> +	pdmc->buf_sz = watermark * 2;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>>> +				 struct iio_trigger *trig)
>>> +{
>>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>>> +		return 0;
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>>> +	.driver_module = THIS_MODULE,
>>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = arg;
>>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>> +	unsigned int status;
>>> +
>>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>>> +
>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>> +	}
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>>> +{
>>> +	struct dma_tx_state state;
>>> +	enum dma_status status;
>>> +
>>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>>> +				     pdmc->dma_chan->cookie,
>>> +				     &state);
>>> +	if (status == DMA_IN_PROGRESS) {
>>> +		/* Residue is size in bytes from end of buffer */
>>> +		unsigned int i = pdmc->buf_sz - state.residue;
>>> +		unsigned int size;
>>> +
>>> +		/* Return available bytes */
>>> +		if (i >= pdmc->bufi)
>>> +			size = i - pdmc->bufi;
>>> +		else
>>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>>> +
>>> +		return size;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>>> +{
>>> +	struct iio_dev *indio_dev = data;
>>> +
>>> +	iio_trigger_poll_chained(indio_dev->trig);
>> This shouldn't be going through the trigger infrastructure at all really.
>> I'm not 100% sure what doing so is gaining you...
> Yes i will clean trigger part and call directly the ASoC callback here.
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct dma_async_tx_descriptor *desc;
>>> +	dma_cookie_t cookie;
>>> +	int ret;
>>> +
>>> +	if (!pdmc->dma_chan)
>>> +		return -EINVAL;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>>> +
>>> +	/* Prepare a DMA cyclic transaction */
>>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>>> +					 pdmc->dma_buf,
>>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>>> +					 DMA_DEV_TO_MEM,
>>> +					 DMA_PREP_INTERRUPT);
>>> +	if (!desc)
>>> +		return -EBUSY;
>>> +
>>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>>> +	desc->callback_param = indio_dev;
>>> +
>>> +	cookie = dmaengine_submit(desc);
>>> +	ret = dma_submit_error(cookie);
>>> +	if (ret) {
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +		return ret;
>>> +	}
>>> +
>>> +	/* Issue pending DMA requests */
>>> +	dma_async_issue_pending(pdmc->dma_chan);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	/* Reset pdmc buffer index */
>>> +	pdmc->bufi = 0;
>>> +
>>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>>> +	if (ret) {
>>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>>> +	if (ret) {
>>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>>> +		goto err_stop_conv;
>>> +	}
>>> +
>>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>>> +		goto err_stop_dma;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err_stop_dma:
>>> +	if (pdmc->dma_chan)
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +err_stop_conv:
>>> +	stm32_dfsdm_stop_conv(pdmc);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>>> +	if (ret < 0)
>>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>>> +
>>> +	if (pdmc->dma_chan)
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +
>>> +	stm32_dfsdm_stop_conv(pdmc);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>> +	.postenable = &stm32_dfsdm_postenable,
>>> +	.predisable = &stm32_dfsdm_predisable,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>>> +{
>> OK. So now I kind of understand what the trigger provided in the adc driver
>> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
>> a hack.  We need fullblown DMA buffer consumers to do this right.
> Yes exactly.
>>
>> Probably best person to comment on that is Lars. 
>>> +	struct iio_poll_func *pf = p;
>>> +	struct iio_dev *indio_dev = pf->indio_dev;
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	size_t old_pos;
>>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>>> +
>>> +	/*
>>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>>> +	 * an interface to push data samples per samples.
>>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>>> +	 * and interface is hacked using a private callback registered by ASoC.
>>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>>> +	 * support in IIO.
>>> +	 */
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>>> +		pdmc->bufi, available);
>>> +	old_pos = pdmc->bufi;
>>> +	while (available >= indio_dev->scan_bytes) {
>>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>>> +
>>> +		/* Mask 8 LSB that contains the channel ID */
>>> +		*buffer &= 0xFFFFFF00;
>>> +		available -= indio_dev->scan_bytes;
>>> +		pdmc->bufi += indio_dev->scan_bytes;
>>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>>> +			if (pdmc->cb)
>>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>>> +			pdmc->bufi = 0;
>>> +			old_pos = 0;
>>> +		}
>>> +	}
>>> +	if (pdmc->cb)
>>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>>> +				pdmc->cb_priv);
>>> +
>>> +	iio_trigger_notify_done(indio_dev->trig);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb - register a callback
>>> + *	that will be called when DMA transfer period is achieved.
>>> + *
>>> + * @iio_dev: Handle to IIO device.
>>> + * @cb: pointer to callback function.
>>> + *	@data: pointer to data buffer
>>> + *	@size: size in byte of the data buffer
>>> + *	@private: pointer to consumer private structure
>>> + * @private: pointer to consumer private structure
>>> + */
>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>> +			    int (*cb)(const void *data, size_t size,
>>> +				      void *private),
>>> +			    void *private)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +
>>> +	if (!iio_dev)
>>> +		return -EINVAL;
>>> +	pdmc = iio_priv(iio_dev);
>>> +
>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>> +		return -EINVAL;
>>> +
>>> +	pdmc->cb = cb;
>>> +	pdmc->cb_priv = private;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>>> +
>>> +/**
>>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>>> + *
>>> + * @iio_dev: Handle to IIO device.
>>> + */
>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +
>>> +	if (!iio_dev)
>>> +		return -EINVAL;
>>> +	pdmc = iio_priv(iio_dev);
>>> +
>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>> +		return -EINVAL;
>>> +	pdmc->cb = NULL;
>>> +	pdmc->cb_priv = NULL;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>>> +
>>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>>> +{
>>> +	struct iio_chan_spec *ch;
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>>> +	if (!ch)
>>> +		return -ENOMEM;
>>> +
>>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = 0;
>>> +	ch->ext_info = dfsdm_adc_ext_info;
>>> +
>>> +	ch->scan_type.sign = 's';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +
>>> +	pdmc->ch_id = ch->channel;
>>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>>> +
>>> +	indio_dev->num_channels = 1;
>>> +	indio_dev->channels = ch;
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>>> +	{}
>>> +};
>>> +
>>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct dma_slave_config config;
>>> +	int ret;
>>> +
>>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>>> +	if (!pdmc->dma_chan)
>>> +		return -EINVAL;
>>> +
>>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>>> +					 DFSDM_DMA_BUFFER_SIZE,
>>> +					 &pdmc->dma_buf, GFP_KERNEL);
>>> +	if (!pdmc->rx_buf) {
>>> +		ret = -ENOMEM;
>>> +		goto err_release;
>>> +	}
>>> +
>>> +	/* Configure DMA channel to read data register */
>>> +	memset(&config, 0, sizeof(config));
>>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>>> +
>>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>>> +	if (ret)
>>> +		goto err_free;
>>> +
>>> +	return 0;
>>> +
>>> +err_free:
>>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>>> +			  pdmc->rx_buf, pdmc->dma_buf);
>>> +err_release:
>>> +	dma_release_channel(pdmc->dma_chan);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>>> +{
>>> +	struct device *dev = &pdev->dev;
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +	struct device_node *np = dev->of_node;
>>> +	struct iio_dev *iio;
>>> +	char *name;
>>> +	int ret, irq, val;
>>> +
>>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>>> +	if (IS_ERR(iio)) {
>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>> +		return PTR_ERR(iio);
>>> +	}
>>> +
>>> +	pdmc = iio_priv(iio);
>>> +	if (IS_ERR(pdmc)) {
>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>> +		return PTR_ERR(pdmc);
>>> +	}
>>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>>> +
>>> +	iio->name = np->name;
>>> +	iio->dev.parent = dev;
>>> +	iio->dev.of_node = np;
>>> +	iio->info = &stm32_dfsdm_info_pdmc;
>>> +	iio->modes = INDIO_DIRECT_MODE;
>>> +
>>> +	platform_set_drvdata(pdev, pdmc);
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>>> +	if (ret != 0) {
>>> +		dev_err(dev, "Missing reg property\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>>> +	if (!name)
>>> +		return -ENOMEM;
>>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>>> +	iio->name = name;
>>> +
>>> +	/*
>>> +	 * In a first step IRQs generated for channels are not treated.
>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>> +	 */
>>> +	irq = platform_get_irq(pdev, 0);
>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>> +			       0, pdev->name, pdmc);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to request IRQ\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to set filter order\n");
>>> +		return ret;
>>> +	}
>>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>> +	if (!ret)
>>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>>> +
>>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "DMA request failed\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>>> +
>>> +	ret = iio_triggered_buffer_setup(iio,
>>> +					 &iio_pollfunc_store_time,
>>> +					 &stm32_dfsdm_audio_trigger_handler,
>>> +					 &stm32_dfsdm_buffer_setup_ops);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>>> +		goto err_dma_disable;
>>> +	}
>>> +
>>> +	ret = iio_device_register(iio);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>>> +		goto err_buffer_cleanup;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err_buffer_cleanup:
>>> +	iio_triggered_buffer_cleanup(iio);
>>> +
>>> +err_dma_disable:
>>> +	if (pdmc->dma_chan) {
>>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>>> +				  DFSDM_DMA_BUFFER_SIZE,
>>> +				  pdmc->rx_buf, pdmc->dma_buf);
>>> +		dma_release_channel(pdmc->dma_chan);
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>>> +
>>> +	iio_device_unregister(iio);
>> Same as in the other driver. Can just use the devm_iio_device_register
>> version and drop remove.
> oops missing the previous comment... sorry.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>>> +	.driver = {
>>> +		.name = "stm32-dfsdm-audio",
>>> +		.of_match_table = stm32_dfsdm_audio_match,
>>> +	},
>>> +	.probe = stm32_dfsdm_audio_probe,
>>> +	.remove = stm32_dfsdm_audio_remove,
>>> +};
>>> +module_platform_driver(stm32_dfsdm_audio_driver);
>>> +
>>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>> new file mode 100644
>>> index 0000000..9b41b28
>>> --- /dev/null
>>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>> @@ -0,0 +1,41 @@
>>> +/*
>>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>>> + *
>>> + * 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 STM32_DFSDM_AUDIO_H
>>> +#define STM32_DFSDM_AUDIO_H
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>>> + * @dev:	Pointer to client device.
>>> + * @indio_dev:	Device on which the channel exists.
>>> + * @cb:		Callback function.
>>> + *		@data:  pointer to data buffer
>>> + *		@size: size of the data buffer in bytes
>>> + * @private:	Private data passed to callback.
>>> + *
>>> + */
>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>> +			    int (*cb)(const void *data, size_t size,
>>> +				      void *private),
>>> +			    void *private);
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>>> + * @indio_dev:	Device on which the channel exists.
>>> + */
>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>>> +
>>> +#endif
>>>
>>
> --
> 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] 109+ messages in thread

* Re: [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-25 15:59           ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 15:59 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

On 20/03/17 11:29, Arnaud Pouliquen wrote:
> Please find my comments in-line
> 
> Thanks and Regards,
> Arnaud
> 
> On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add DFSDM driver to handle PDM audio microphones.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> So key element here is that we really need to have a proper way of
>> doing DMA buffer consumers from IIO (as you said in your cover letter).
>>
>> Not entirely obvious how to do this.  Would like some input from Lars
>> if possible.  I'm afraid I don't have an suitable hardware to try anything
>> out on (or the time really!).  This will obviously be quite different from
>> the single scan stuff you have seen is there at the moment.
> I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
> DMA. I suppose that problematics are similar. Perhaps the extra
> constrains in DFSDM is the in-kernel API used to get the data.
Absolutely.  Cyclic dma is becoming more and more common on SoC ADCs.
There are a few more fpga based ones in Analog devices tree as well -
most of those actually do the dma buffer route rather than pushing through
the normal buffer interface (kfifo effectively).  Right now we just
don't have any means of accessing these dma buffers in kernel.

Unlike the normal consumer interface, I doubt we'll want to do demuxing
of the stream for this sort of usecase - feels like exclusive use is more
likely.
> 
>>
>> Whether this whole approach makes sense vs doing the dma in the alsa driver
>> isn't clear to me.
>>
>> On the plus side, presumably we'll get love dma ADC support for free
>> as part of doing it this way ;)
>>
>> It's been a while since I looked at the IIO dma buffer stuff at all. Will
>> try and refresh my memory sometime this week.
> 
> Ok, i will wait your feedback (and Lars's one) before updating my code
> for next version.
It may well be the case that it will take us sometime (when I say us, I mean
you and Lars ;) to pin down how to do dma buffer consumers, so it may be
a case of taking the driver in a state close to the current one with the
intent to switch to a generic way of handling it later.

As long as bindings and userspace don't change we can of course mess with
anything we like.

Jonathan
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/Kconfig                   |  13 +
>>>  drivers/iio/adc/Makefile                  |   1 +
>>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>>  4 files changed, 775 insertions(+)
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 3e0eb11..c108933 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-dfsdm-adc.
>>>  
>>> +config STM32_DFSDM_AUDIO
>>> +	tristate "STMicroelectronics STM32 dfsdm audio
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select STM32_DFSDM_CORE
>>> +	select REGMAP_MMIO
>>> +	select IIO_BUFFER_DMAENGINE
>>> +	help
>>> +	  Select this option to support Audio PDM micophone for
>>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-dfsdm-audio.
>>> +
>>>  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 161f271..79f975d 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>>> new file mode 100644
>>> index 0000000..115ef8f
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>>> @@ -0,0 +1,720 @@
>>> +/*
>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, 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/dmaengine.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/buffer.h>
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/sysfs.h>
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/trigger_consumer.h>
>>> +#include <linux/iio/triggered_buffer.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>>> +
>>> +struct stm32_dfsdm_audio {
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	unsigned int fl_id;
>>> +	unsigned int ch_id;
>>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>>> +
>>> +	u8 *rx_buf;
>>> +	unsigned int bufi; /* Buffer current position */
>>> +	unsigned int buf_sz; /* Buffer size */
>>> +
>>> +	struct dma_chan	*dma_chan;
>>> +	dma_addr_t dma_buf;
>>> +
>>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>>> +	void *cb_priv;
>>> +};
>>> +
>>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>>> +
>>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				    const struct iio_chan_spec *chan, char *buf)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				    const struct iio_chan_spec *chan,
>>> +				    const char *buf, size_t len)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>> +	unsigned int spi_freq = pdmc->spi_freq;
>>> +	unsigned int sample_freq;
>>> +	int ret;
>>> +
>>> +	ret = kstrtoint(buf, 0, &sample_freq);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>>> +	if (!sample_freq)
>>> +		return -EINVAL;
>>> +
>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>>> +
>>> +	if (spi_freq % sample_freq)
>>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>>> +			 spi_freq / (spi_freq / sample_freq));
>>> +
>>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			"Not able to find filter parameter that match!\n");
>>> +		return ret;
>>> +	}
>>> +	pdmc->sample_freq = sample_freq;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				      const struct iio_chan_spec *chan,
>>> +				      char *buf)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				      const struct iio_chan_spec *chan,
>>> +				      const char *buf, size_t len)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>> +	unsigned int sample_freq = pdmc->sample_freq;
>>> +	unsigned int spi_freq;
>>> +	int ret;
>>> +
>>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>> +		return -EPERM;
>>> +
>>> +	ret = kstrtoint(buf, 0, &spi_freq);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>>> +	if (!spi_freq)
>>> +		return -EINVAL;
>>> +
>>> +	if (sample_freq) {
>>> +		if (spi_freq % sample_freq)
>>> +			dev_warn(&indio_dev->dev,
>>> +				 "Sampling rate not accurate (%d)\n",
>>> +				 spi_freq / (spi_freq / sample_freq));
>>> +
>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"No filter parameters that match!\n");
>>> +			return ret;
>>> +		}
>>> +	}
>>> +	pdmc->spi_freq = spi_freq;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +/*
>>> + * Define external info for SPI Frequency and audio sampling rate that can be
>>> + * configured by ASoC driver through consumer.h API
>>> + */
>>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>>> +	/* filter oversampling: Post filter oversampling ratio */
>>> +	{
>>> +		.name = "audio_sampling_rate",
>>> +		.shared = IIO_SHARED_BY_TYPE,
>>> +		.read = dfsdm_audio_get_rate,
>>> +		.write = dfsdm_audio_set_rate,
>>> +	},
>>> +	/* data_right_bit_shift : Filter output data shifting */
>>> +	{
>>> +		.name = "spi_clk_freq",
>>> +		.shared = IIO_SHARED_BY_TYPE,
>>> +		.read = dfsdm_audio_get_spiclk,
>>> +		.write = dfsdm_audio_set_spiclk,
>>> +	},
>>> +	{},
>>> +};
>>> +
>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>>> +{
>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_dfsdm;
>>> +
>>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>>> +					   pdmc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	/* Enable DMA transfer*/
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	/* Enable conversion triggered by SPI clock*/
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	return 0;
>>> +
>>> +stop_channels:
>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>>> +stop_dfsdm:
>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>>> +{
>>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>>> +
>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>>> +
>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>> +}
>>> +
>>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>>> +				     unsigned int val)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>>> +
>>> +	/*
>>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>>> +	 * There should be :
>>> +	 * - always one buffer (period) DMA is working on
>>> +	 * - one buffer (period) driver pushed to ASoC side ().
>>> +	 */
>>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>>> +	pdmc->buf_sz = watermark * 2;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>>> +				 struct iio_trigger *trig)
>>> +{
>>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>>> +		return 0;
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>>> +	.driver_module = THIS_MODULE,
>>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = arg;
>>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>> +	unsigned int status;
>>> +
>>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>>> +
>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>> +	}
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>>> +{
>>> +	struct dma_tx_state state;
>>> +	enum dma_status status;
>>> +
>>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>>> +				     pdmc->dma_chan->cookie,
>>> +				     &state);
>>> +	if (status == DMA_IN_PROGRESS) {
>>> +		/* Residue is size in bytes from end of buffer */
>>> +		unsigned int i = pdmc->buf_sz - state.residue;
>>> +		unsigned int size;
>>> +
>>> +		/* Return available bytes */
>>> +		if (i >= pdmc->bufi)
>>> +			size = i - pdmc->bufi;
>>> +		else
>>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>>> +
>>> +		return size;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>>> +{
>>> +	struct iio_dev *indio_dev = data;
>>> +
>>> +	iio_trigger_poll_chained(indio_dev->trig);
>> This shouldn't be going through the trigger infrastructure at all really.
>> I'm not 100% sure what doing so is gaining you...
> Yes i will clean trigger part and call directly the ASoC callback here.
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct dma_async_tx_descriptor *desc;
>>> +	dma_cookie_t cookie;
>>> +	int ret;
>>> +
>>> +	if (!pdmc->dma_chan)
>>> +		return -EINVAL;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>>> +
>>> +	/* Prepare a DMA cyclic transaction */
>>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>>> +					 pdmc->dma_buf,
>>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>>> +					 DMA_DEV_TO_MEM,
>>> +					 DMA_PREP_INTERRUPT);
>>> +	if (!desc)
>>> +		return -EBUSY;
>>> +
>>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>>> +	desc->callback_param = indio_dev;
>>> +
>>> +	cookie = dmaengine_submit(desc);
>>> +	ret = dma_submit_error(cookie);
>>> +	if (ret) {
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +		return ret;
>>> +	}
>>> +
>>> +	/* Issue pending DMA requests */
>>> +	dma_async_issue_pending(pdmc->dma_chan);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	/* Reset pdmc buffer index */
>>> +	pdmc->bufi = 0;
>>> +
>>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>>> +	if (ret) {
>>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>>> +	if (ret) {
>>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>>> +		goto err_stop_conv;
>>> +	}
>>> +
>>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>>> +		goto err_stop_dma;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err_stop_dma:
>>> +	if (pdmc->dma_chan)
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +err_stop_conv:
>>> +	stm32_dfsdm_stop_conv(pdmc);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>>> +	if (ret < 0)
>>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>>> +
>>> +	if (pdmc->dma_chan)
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +
>>> +	stm32_dfsdm_stop_conv(pdmc);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>> +	.postenable = &stm32_dfsdm_postenable,
>>> +	.predisable = &stm32_dfsdm_predisable,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>>> +{
>> OK. So now I kind of understand what the trigger provided in the adc driver
>> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
>> a hack.  We need fullblown DMA buffer consumers to do this right.
> Yes exactly.
>>
>> Probably best person to comment on that is Lars. 
>>> +	struct iio_poll_func *pf = p;
>>> +	struct iio_dev *indio_dev = pf->indio_dev;
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	size_t old_pos;
>>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>>> +
>>> +	/*
>>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>>> +	 * an interface to push data samples per samples.
>>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>>> +	 * and interface is hacked using a private callback registered by ASoC.
>>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>>> +	 * support in IIO.
>>> +	 */
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>>> +		pdmc->bufi, available);
>>> +	old_pos = pdmc->bufi;
>>> +	while (available >= indio_dev->scan_bytes) {
>>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>>> +
>>> +		/* Mask 8 LSB that contains the channel ID */
>>> +		*buffer &= 0xFFFFFF00;
>>> +		available -= indio_dev->scan_bytes;
>>> +		pdmc->bufi += indio_dev->scan_bytes;
>>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>>> +			if (pdmc->cb)
>>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>>> +			pdmc->bufi = 0;
>>> +			old_pos = 0;
>>> +		}
>>> +	}
>>> +	if (pdmc->cb)
>>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>>> +				pdmc->cb_priv);
>>> +
>>> +	iio_trigger_notify_done(indio_dev->trig);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb - register a callback
>>> + *	that will be called when DMA transfer period is achieved.
>>> + *
>>> + * @iio_dev: Handle to IIO device.
>>> + * @cb: pointer to callback function.
>>> + *	@data: pointer to data buffer
>>> + *	@size: size in byte of the data buffer
>>> + *	@private: pointer to consumer private structure
>>> + * @private: pointer to consumer private structure
>>> + */
>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>> +			    int (*cb)(const void *data, size_t size,
>>> +				      void *private),
>>> +			    void *private)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +
>>> +	if (!iio_dev)
>>> +		return -EINVAL;
>>> +	pdmc = iio_priv(iio_dev);
>>> +
>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>> +		return -EINVAL;
>>> +
>>> +	pdmc->cb = cb;
>>> +	pdmc->cb_priv = private;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>>> +
>>> +/**
>>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>>> + *
>>> + * @iio_dev: Handle to IIO device.
>>> + */
>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +
>>> +	if (!iio_dev)
>>> +		return -EINVAL;
>>> +	pdmc = iio_priv(iio_dev);
>>> +
>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>> +		return -EINVAL;
>>> +	pdmc->cb = NULL;
>>> +	pdmc->cb_priv = NULL;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>>> +
>>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>>> +{
>>> +	struct iio_chan_spec *ch;
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>>> +	if (!ch)
>>> +		return -ENOMEM;
>>> +
>>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = 0;
>>> +	ch->ext_info = dfsdm_adc_ext_info;
>>> +
>>> +	ch->scan_type.sign = 's';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +
>>> +	pdmc->ch_id = ch->channel;
>>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>>> +
>>> +	indio_dev->num_channels = 1;
>>> +	indio_dev->channels = ch;
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>>> +	{}
>>> +};
>>> +
>>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct dma_slave_config config;
>>> +	int ret;
>>> +
>>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>>> +	if (!pdmc->dma_chan)
>>> +		return -EINVAL;
>>> +
>>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>>> +					 DFSDM_DMA_BUFFER_SIZE,
>>> +					 &pdmc->dma_buf, GFP_KERNEL);
>>> +	if (!pdmc->rx_buf) {
>>> +		ret = -ENOMEM;
>>> +		goto err_release;
>>> +	}
>>> +
>>> +	/* Configure DMA channel to read data register */
>>> +	memset(&config, 0, sizeof(config));
>>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>>> +
>>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>>> +	if (ret)
>>> +		goto err_free;
>>> +
>>> +	return 0;
>>> +
>>> +err_free:
>>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>>> +			  pdmc->rx_buf, pdmc->dma_buf);
>>> +err_release:
>>> +	dma_release_channel(pdmc->dma_chan);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>>> +{
>>> +	struct device *dev = &pdev->dev;
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +	struct device_node *np = dev->of_node;
>>> +	struct iio_dev *iio;
>>> +	char *name;
>>> +	int ret, irq, val;
>>> +
>>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>>> +	if (IS_ERR(iio)) {
>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>> +		return PTR_ERR(iio);
>>> +	}
>>> +
>>> +	pdmc = iio_priv(iio);
>>> +	if (IS_ERR(pdmc)) {
>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>> +		return PTR_ERR(pdmc);
>>> +	}
>>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>>> +
>>> +	iio->name = np->name;
>>> +	iio->dev.parent = dev;
>>> +	iio->dev.of_node = np;
>>> +	iio->info = &stm32_dfsdm_info_pdmc;
>>> +	iio->modes = INDIO_DIRECT_MODE;
>>> +
>>> +	platform_set_drvdata(pdev, pdmc);
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>>> +	if (ret != 0) {
>>> +		dev_err(dev, "Missing reg property\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>>> +	if (!name)
>>> +		return -ENOMEM;
>>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>>> +	iio->name = name;
>>> +
>>> +	/*
>>> +	 * In a first step IRQs generated for channels are not treated.
>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>> +	 */
>>> +	irq = platform_get_irq(pdev, 0);
>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>> +			       0, pdev->name, pdmc);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to request IRQ\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to set filter order\n");
>>> +		return ret;
>>> +	}
>>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>> +	if (!ret)
>>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>>> +
>>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "DMA request failed\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>>> +
>>> +	ret = iio_triggered_buffer_setup(iio,
>>> +					 &iio_pollfunc_store_time,
>>> +					 &stm32_dfsdm_audio_trigger_handler,
>>> +					 &stm32_dfsdm_buffer_setup_ops);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>>> +		goto err_dma_disable;
>>> +	}
>>> +
>>> +	ret = iio_device_register(iio);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>>> +		goto err_buffer_cleanup;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err_buffer_cleanup:
>>> +	iio_triggered_buffer_cleanup(iio);
>>> +
>>> +err_dma_disable:
>>> +	if (pdmc->dma_chan) {
>>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>>> +				  DFSDM_DMA_BUFFER_SIZE,
>>> +				  pdmc->rx_buf, pdmc->dma_buf);
>>> +		dma_release_channel(pdmc->dma_chan);
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>>> +
>>> +	iio_device_unregister(iio);
>> Same as in the other driver. Can just use the devm_iio_device_register
>> version and drop remove.
> oops missing the previous comment... sorry.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>>> +	.driver = {
>>> +		.name = "stm32-dfsdm-audio",
>>> +		.of_match_table = stm32_dfsdm_audio_match,
>>> +	},
>>> +	.probe = stm32_dfsdm_audio_probe,
>>> +	.remove = stm32_dfsdm_audio_remove,
>>> +};
>>> +module_platform_driver(stm32_dfsdm_audio_driver);
>>> +
>>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>> new file mode 100644
>>> index 0000000..9b41b28
>>> --- /dev/null
>>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>> @@ -0,0 +1,41 @@
>>> +/*
>>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>>> + *
>>> + * 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 STM32_DFSDM_AUDIO_H
>>> +#define STM32_DFSDM_AUDIO_H
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>>> + * @dev:	Pointer to client device.
>>> + * @indio_dev:	Device on which the channel exists.
>>> + * @cb:		Callback function.
>>> + *		@data:  pointer to data buffer
>>> + *		@size: size of the data buffer in bytes
>>> + * @private:	Private data passed to callback.
>>> + *
>>> + */
>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>> +			    int (*cb)(const void *data, size_t size,
>>> +				      void *private),
>>> +			    void *private);
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>>> + * @indio_dev:	Device on which the channel exists.
>>> + */
>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>>> +
>>> +#endif
>>>
>>
> --
> 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] 109+ messages in thread

* [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-25 15:59           ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 15:59 UTC (permalink / raw)
  To: linux-arm-kernel

On 20/03/17 11:29, Arnaud Pouliquen wrote:
> Please find my comments in-line
> 
> Thanks and Regards,
> Arnaud
> 
> On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add DFSDM driver to handle PDM audio microphones.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> So key element here is that we really need to have a proper way of
>> doing DMA buffer consumers from IIO (as you said in your cover letter).
>>
>> Not entirely obvious how to do this.  Would like some input from Lars
>> if possible.  I'm afraid I don't have an suitable hardware to try anything
>> out on (or the time really!).  This will obviously be quite different from
>> the single scan stuff you have seen is there at the moment.
> I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
> DMA. I suppose that problematics are similar. Perhaps the extra
> constrains in DFSDM is the in-kernel API used to get the data.
Absolutely.  Cyclic dma is becoming more and more common on SoC ADCs.
There are a few more fpga based ones in Analog devices tree as well -
most of those actually do the dma buffer route rather than pushing through
the normal buffer interface (kfifo effectively).  Right now we just
don't have any means of accessing these dma buffers in kernel.

Unlike the normal consumer interface, I doubt we'll want to do demuxing
of the stream for this sort of usecase - feels like exclusive use is more
likely.
> 
>>
>> Whether this whole approach makes sense vs doing the dma in the alsa driver
>> isn't clear to me.
>>
>> On the plus side, presumably we'll get love dma ADC support for free
>> as part of doing it this way ;)
>>
>> It's been a while since I looked at the IIO dma buffer stuff at all. Will
>> try and refresh my memory sometime this week.
> 
> Ok, i will wait your feedback (and Lars's one) before updating my code
> for next version.
It may well be the case that it will take us sometime (when I say us, I mean
you and Lars ;) to pin down how to do dma buffer consumers, so it may be
a case of taking the driver in a state close to the current one with the
intent to switch to a generic way of handling it later.

As long as bindings and userspace don't change we can of course mess with
anything we like.

Jonathan
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/Kconfig                   |  13 +
>>>  drivers/iio/adc/Makefile                  |   1 +
>>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>>  4 files changed, 775 insertions(+)
>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 3e0eb11..c108933 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-dfsdm-adc.
>>>  
>>> +config STM32_DFSDM_AUDIO
>>> +	tristate "STMicroelectronics STM32 dfsdm audio
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select STM32_DFSDM_CORE
>>> +	select REGMAP_MMIO
>>> +	select IIO_BUFFER_DMAENGINE
>>> +	help
>>> +	  Select this option to support Audio PDM micophone for
>>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-dfsdm-audio.
>>> +
>>>  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 161f271..79f975d 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>>> new file mode 100644
>>> index 0000000..115ef8f
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>>> @@ -0,0 +1,720 @@
>>> +/*
>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, 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/dmaengine.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/buffer.h>
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/sysfs.h>
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/trigger_consumer.h>
>>> +#include <linux/iio/triggered_buffer.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>>> +
>>> +struct stm32_dfsdm_audio {
>>> +	struct stm32_dfsdm *dfsdm;
>>> +	unsigned int fl_id;
>>> +	unsigned int ch_id;
>>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>>> +
>>> +	u8 *rx_buf;
>>> +	unsigned int bufi; /* Buffer current position */
>>> +	unsigned int buf_sz; /* Buffer size */
>>> +
>>> +	struct dma_chan	*dma_chan;
>>> +	dma_addr_t dma_buf;
>>> +
>>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>>> +	void *cb_priv;
>>> +};
>>> +
>>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>>> +
>>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				    const struct iio_chan_spec *chan, char *buf)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				    const struct iio_chan_spec *chan,
>>> +				    const char *buf, size_t len)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>> +	unsigned int spi_freq = pdmc->spi_freq;
>>> +	unsigned int sample_freq;
>>> +	int ret;
>>> +
>>> +	ret = kstrtoint(buf, 0, &sample_freq);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>>> +	if (!sample_freq)
>>> +		return -EINVAL;
>>> +
>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>>> +
>>> +	if (spi_freq % sample_freq)
>>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>>> +			 spi_freq / (spi_freq / sample_freq));
>>> +
>>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			"Not able to find filter parameter that match!\n");
>>> +		return ret;
>>> +	}
>>> +	pdmc->sample_freq = sample_freq;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				      const struct iio_chan_spec *chan,
>>> +				      char *buf)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>>> +}
>>> +
>>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>> +				      const struct iio_chan_spec *chan,
>>> +				      const char *buf, size_t len)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>> +	unsigned int sample_freq = pdmc->sample_freq;
>>> +	unsigned int spi_freq;
>>> +	int ret;
>>> +
>>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>> +		return -EPERM;
>>> +
>>> +	ret = kstrtoint(buf, 0, &spi_freq);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>>> +	if (!spi_freq)
>>> +		return -EINVAL;
>>> +
>>> +	if (sample_freq) {
>>> +		if (spi_freq % sample_freq)
>>> +			dev_warn(&indio_dev->dev,
>>> +				 "Sampling rate not accurate (%d)\n",
>>> +				 spi_freq / (spi_freq / sample_freq));
>>> +
>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>> +		if (ret < 0) {
>>> +			dev_err(&indio_dev->dev,
>>> +				"No filter parameters that match!\n");
>>> +			return ret;
>>> +		}
>>> +	}
>>> +	pdmc->spi_freq = spi_freq;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +/*
>>> + * Define external info for SPI Frequency and audio sampling rate that can be
>>> + * configured by ASoC driver through consumer.h API
>>> + */
>>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>>> +	/* filter oversampling: Post filter oversampling ratio */
>>> +	{
>>> +		.name = "audio_sampling_rate",
>>> +		.shared = IIO_SHARED_BY_TYPE,
>>> +		.read = dfsdm_audio_get_rate,
>>> +		.write = dfsdm_audio_set_rate,
>>> +	},
>>> +	/* data_right_bit_shift : Filter output data shifting */
>>> +	{
>>> +		.name = "spi_clk_freq",
>>> +		.shared = IIO_SHARED_BY_TYPE,
>>> +		.read = dfsdm_audio_get_spiclk,
>>> +		.write = dfsdm_audio_set_spiclk,
>>> +	},
>>> +	{},
>>> +};
>>> +
>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>>> +{
>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>> +	int ret;
>>> +
>>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_dfsdm;
>>> +
>>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>>> +					   pdmc->ch_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	/* Enable DMA transfer*/
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	/* Enable conversion triggered by SPI clock*/
>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>>> +	if (ret < 0)
>>> +		goto stop_channels;
>>> +
>>> +	return 0;
>>> +
>>> +stop_channels:
>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>>> +stop_dfsdm:
>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>>> +{
>>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>>> +
>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>>> +
>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>> +}
>>> +
>>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>>> +				     unsigned int val)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>>> +
>>> +	/*
>>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>>> +	 * There should be :
>>> +	 * - always one buffer (period) DMA is working on
>>> +	 * - one buffer (period) driver pushed to ASoC side ().
>>> +	 */
>>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>>> +	pdmc->buf_sz = watermark * 2;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>>> +				 struct iio_trigger *trig)
>>> +{
>>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>>> +		return 0;
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>>> +	.driver_module = THIS_MODULE,
>>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = arg;
>>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>> +	unsigned int status;
>>> +
>>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>>> +
>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>> +	}
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>>> +{
>>> +	struct dma_tx_state state;
>>> +	enum dma_status status;
>>> +
>>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>>> +				     pdmc->dma_chan->cookie,
>>> +				     &state);
>>> +	if (status == DMA_IN_PROGRESS) {
>>> +		/* Residue is size in bytes from end of buffer */
>>> +		unsigned int i = pdmc->buf_sz - state.residue;
>>> +		unsigned int size;
>>> +
>>> +		/* Return available bytes */
>>> +		if (i >= pdmc->bufi)
>>> +			size = i - pdmc->bufi;
>>> +		else
>>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>>> +
>>> +		return size;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>>> +{
>>> +	struct iio_dev *indio_dev = data;
>>> +
>>> +	iio_trigger_poll_chained(indio_dev->trig);
>> This shouldn't be going through the trigger infrastructure at all really.
>> I'm not 100% sure what doing so is gaining you...
> Yes i will clean trigger part and call directly the ASoC callback here.
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct dma_async_tx_descriptor *desc;
>>> +	dma_cookie_t cookie;
>>> +	int ret;
>>> +
>>> +	if (!pdmc->dma_chan)
>>> +		return -EINVAL;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>>> +
>>> +	/* Prepare a DMA cyclic transaction */
>>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>>> +					 pdmc->dma_buf,
>>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>>> +					 DMA_DEV_TO_MEM,
>>> +					 DMA_PREP_INTERRUPT);
>>> +	if (!desc)
>>> +		return -EBUSY;
>>> +
>>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>>> +	desc->callback_param = indio_dev;
>>> +
>>> +	cookie = dmaengine_submit(desc);
>>> +	ret = dma_submit_error(cookie);
>>> +	if (ret) {
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +		return ret;
>>> +	}
>>> +
>>> +	/* Issue pending DMA requests */
>>> +	dma_async_issue_pending(pdmc->dma_chan);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	/* Reset pdmc buffer index */
>>> +	pdmc->bufi = 0;
>>> +
>>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>>> +	if (ret) {
>>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>>> +	if (ret) {
>>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>>> +		goto err_stop_conv;
>>> +	}
>>> +
>>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>>> +		goto err_stop_dma;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err_stop_dma:
>>> +	if (pdmc->dma_chan)
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +err_stop_conv:
>>> +	stm32_dfsdm_stop_conv(pdmc);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>>> +	if (ret < 0)
>>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>>> +
>>> +	if (pdmc->dma_chan)
>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>> +
>>> +	stm32_dfsdm_stop_conv(pdmc);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>> +	.postenable = &stm32_dfsdm_postenable,
>>> +	.predisable = &stm32_dfsdm_predisable,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>>> +{
>> OK. So now I kind of understand what the trigger provided in the adc driver
>> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
>> a hack.  We need fullblown DMA buffer consumers to do this right.
> Yes exactly.
>>
>> Probably best person to comment on that is Lars. 
>>> +	struct iio_poll_func *pf = p;
>>> +	struct iio_dev *indio_dev = pf->indio_dev;
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	size_t old_pos;
>>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>>> +
>>> +	/*
>>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>>> +	 * an interface to push data samples per samples.
>>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>>> +	 * and interface is hacked using a private callback registered by ASoC.
>>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>>> +	 * support in IIO.
>>> +	 */
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>>> +		pdmc->bufi, available);
>>> +	old_pos = pdmc->bufi;
>>> +	while (available >= indio_dev->scan_bytes) {
>>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>>> +
>>> +		/* Mask 8 LSB that contains the channel ID */
>>> +		*buffer &= 0xFFFFFF00;
>>> +		available -= indio_dev->scan_bytes;
>>> +		pdmc->bufi += indio_dev->scan_bytes;
>>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>>> +			if (pdmc->cb)
>>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>>> +			pdmc->bufi = 0;
>>> +			old_pos = 0;
>>> +		}
>>> +	}
>>> +	if (pdmc->cb)
>>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>>> +				pdmc->cb_priv);
>>> +
>>> +	iio_trigger_notify_done(indio_dev->trig);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb - register a callback
>>> + *	that will be called when DMA transfer period is achieved.
>>> + *
>>> + * @iio_dev: Handle to IIO device.
>>> + * @cb: pointer to callback function.
>>> + *	@data: pointer to data buffer
>>> + *	@size: size in byte of the data buffer
>>> + *	@private: pointer to consumer private structure
>>> + * @private: pointer to consumer private structure
>>> + */
>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>> +			    int (*cb)(const void *data, size_t size,
>>> +				      void *private),
>>> +			    void *private)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +
>>> +	if (!iio_dev)
>>> +		return -EINVAL;
>>> +	pdmc = iio_priv(iio_dev);
>>> +
>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>> +		return -EINVAL;
>>> +
>>> +	pdmc->cb = cb;
>>> +	pdmc->cb_priv = private;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>>> +
>>> +/**
>>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>>> + *
>>> + * @iio_dev: Handle to IIO device.
>>> + */
>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +
>>> +	if (!iio_dev)
>>> +		return -EINVAL;
>>> +	pdmc = iio_priv(iio_dev);
>>> +
>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>> +		return -EINVAL;
>>> +	pdmc->cb = NULL;
>>> +	pdmc->cb_priv = NULL;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>>> +
>>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>>> +{
>>> +	struct iio_chan_spec *ch;
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>>> +	if (!ch)
>>> +		return -ENOMEM;
>>> +
>>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = 0;
>>> +	ch->ext_info = dfsdm_adc_ext_info;
>>> +
>>> +	ch->scan_type.sign = 's';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +
>>> +	pdmc->ch_id = ch->channel;
>>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>>> +
>>> +	indio_dev->num_channels = 1;
>>> +	indio_dev->channels = ch;
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>>> +	{}
>>> +};
>>> +
>>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>> +	struct dma_slave_config config;
>>> +	int ret;
>>> +
>>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>>> +	if (!pdmc->dma_chan)
>>> +		return -EINVAL;
>>> +
>>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>>> +					 DFSDM_DMA_BUFFER_SIZE,
>>> +					 &pdmc->dma_buf, GFP_KERNEL);
>>> +	if (!pdmc->rx_buf) {
>>> +		ret = -ENOMEM;
>>> +		goto err_release;
>>> +	}
>>> +
>>> +	/* Configure DMA channel to read data register */
>>> +	memset(&config, 0, sizeof(config));
>>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>>> +
>>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>>> +	if (ret)
>>> +		goto err_free;
>>> +
>>> +	return 0;
>>> +
>>> +err_free:
>>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>>> +			  pdmc->rx_buf, pdmc->dma_buf);
>>> +err_release:
>>> +	dma_release_channel(pdmc->dma_chan);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>>> +{
>>> +	struct device *dev = &pdev->dev;
>>> +	struct stm32_dfsdm_audio *pdmc;
>>> +	struct device_node *np = dev->of_node;
>>> +	struct iio_dev *iio;
>>> +	char *name;
>>> +	int ret, irq, val;
>>> +
>>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>>> +	if (IS_ERR(iio)) {
>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>> +		return PTR_ERR(iio);
>>> +	}
>>> +
>>> +	pdmc = iio_priv(iio);
>>> +	if (IS_ERR(pdmc)) {
>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>> +		return PTR_ERR(pdmc);
>>> +	}
>>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>>> +
>>> +	iio->name = np->name;
>>> +	iio->dev.parent = dev;
>>> +	iio->dev.of_node = np;
>>> +	iio->info = &stm32_dfsdm_info_pdmc;
>>> +	iio->modes = INDIO_DIRECT_MODE;
>>> +
>>> +	platform_set_drvdata(pdev, pdmc);
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>>> +	if (ret != 0) {
>>> +		dev_err(dev, "Missing reg property\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>>> +	if (!name)
>>> +		return -ENOMEM;
>>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>>> +	iio->name = name;
>>> +
>>> +	/*
>>> +	 * In a first step IRQs generated for channels are not treated.
>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>> +	 */
>>> +	irq = platform_get_irq(pdev, 0);
>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>> +			       0, pdev->name, pdmc);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to request IRQ\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "Failed to set filter order\n");
>>> +		return ret;
>>> +	}
>>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>>> +
>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>> +	if (!ret)
>>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>>> +
>>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "DMA request failed\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>>> +
>>> +	ret = iio_triggered_buffer_setup(iio,
>>> +					 &iio_pollfunc_store_time,
>>> +					 &stm32_dfsdm_audio_trigger_handler,
>>> +					 &stm32_dfsdm_buffer_setup_ops);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>>> +		goto err_dma_disable;
>>> +	}
>>> +
>>> +	ret = iio_device_register(iio);
>>> +	if (ret) {
>>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>>> +		goto err_buffer_cleanup;
>>> +	}
>>> +
>>> +	return 0;
>>> +
>>> +err_buffer_cleanup:
>>> +	iio_triggered_buffer_cleanup(iio);
>>> +
>>> +err_dma_disable:
>>> +	if (pdmc->dma_chan) {
>>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>>> +				  DFSDM_DMA_BUFFER_SIZE,
>>> +				  pdmc->rx_buf, pdmc->dma_buf);
>>> +		dma_release_channel(pdmc->dma_chan);
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>>> +{
>>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>>> +
>>> +	iio_device_unregister(iio);
>> Same as in the other driver. Can just use the devm_iio_device_register
>> version and drop remove.
> oops missing the previous comment... sorry.
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>>> +	.driver = {
>>> +		.name = "stm32-dfsdm-audio",
>>> +		.of_match_table = stm32_dfsdm_audio_match,
>>> +	},
>>> +	.probe = stm32_dfsdm_audio_probe,
>>> +	.remove = stm32_dfsdm_audio_remove,
>>> +};
>>> +module_platform_driver(stm32_dfsdm_audio_driver);
>>> +
>>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>> new file mode 100644
>>> index 0000000..9b41b28
>>> --- /dev/null
>>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>> @@ -0,0 +1,41 @@
>>> +/*
>>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>>> + *
>>> + * 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 STM32_DFSDM_AUDIO_H
>>> +#define STM32_DFSDM_AUDIO_H
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>>> + * @dev:	Pointer to client device.
>>> + * @indio_dev:	Device on which the channel exists.
>>> + * @cb:		Callback function.
>>> + *		@data:  pointer to data buffer
>>> + *		@size: size of the data buffer in bytes
>>> + * @private:	Private data passed to callback.
>>> + *
>>> + */
>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>> +			    int (*cb)(const void *data, size_t size,
>>> +				      void *private),
>>> +			    void *private);
>>> +/**
>>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>>> + * @indio_dev:	Device on which the channel exists.
>>> + */
>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>>> +
>>> +#endif
>>>
>>
> --
> 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] 109+ messages in thread

* Re: [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
  2017-03-20 11:30         ` Arnaud Pouliquen
  (?)
@ 2017-03-25 16:01           ` Jonathan Cameron
  -1 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 16:01 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, Olivier MOYSAN, kernel, linux-iio,
	Maxime Coquelin, linux-arm-kernel, Alexandre TORGUE

On 20/03/17 11:30, Arnaud Pouliquen wrote:
> 
> 
> On 03/19/2017 11:44 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add iio consumer API to set buffer size and watermark according
>>> to sysfs API.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Hmm. Not keen on the length one.  Setting a requested watermark
>> is fair enough.  There is no actually buffer in these cases though
>> so setting it's length is downright odd..
> Length and watermark are configurable from user land through sysfs.
> Seems to me logic to also propose it in inkern API...
> But I can clean length , no problem.
The concept of length for the consumer interface doesn't currently
make sense as it refers to an actual buffer.   The consumer interface
currently passes one entry at a time...
> 
>>
>> Guess this is part of the hacks we need to clean up by doing
>> the dma buffer consumer stuff right...
> Yes all is linked :-).
> 
>>
>> Jonathan
>>> ---
>>>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>>>  include/linux/iio/consumer.h                | 13 +++++++++++++
>>>  2 files changed, 25 insertions(+)
>>>
>>> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
>>> index b8f550e..43c066a 100644
>>> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
>>> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
>>> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>>  }
>>>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>>>  
>>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
>>> +				      size_t length, size_t watermark)
>>> +{
>>> +	if (!length || length < watermark)
>>> +		return -EINVAL;
>>> +	cb_buff->buffer.watermark = watermark;
>>> +	cb_buff->buffer.length = length;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
>>> +
>>>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>>>  {
>>>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
>>> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
>>> index cb44771..0f6e94d 100644
>>> --- a/include/linux/iio/consumer.h
>>> +++ b/include/linux/iio/consumer.h
>>> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>>  						       void *private),
>>>  					     void *private);
>>>  /**
>>> + * iio_channel_cb_set_buffer_size() - set the buffer length.
>>> + * @cb_buffer:		The callback buffer from whom we want the channel
>>> + *			information.
>>> + * @length: buffer length in bytes
>>> + * @watermark: buffer watermark in bytes
>>> + *
>>> + * This function allows to configure the buffer length. The watermark if
>>> + * forced to half of the buffer.
>>> + */
>>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
>>> +				     size_t length, size_t watermark);
>>> +
>>> +/**
>>>   * iio_channel_release_all_cb() - release and unregister the callback.
>>>   * @cb_buffer:		The callback buffer that was allocated.
>>>   */
>>>
>>
>> --
>> 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] 109+ messages in thread

* Re: [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-25 16:01           ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 16:01 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

On 20/03/17 11:30, Arnaud Pouliquen wrote:
> 
> 
> On 03/19/2017 11:44 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add iio consumer API to set buffer size and watermark according
>>> to sysfs API.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Hmm. Not keen on the length one.  Setting a requested watermark
>> is fair enough.  There is no actually buffer in these cases though
>> so setting it's length is downright odd..
> Length and watermark are configurable from user land through sysfs.
> Seems to me logic to also propose it in inkern API...
> But I can clean length , no problem.
The concept of length for the consumer interface doesn't currently
make sense as it refers to an actual buffer.   The consumer interface
currently passes one entry at a time...
> 
>>
>> Guess this is part of the hacks we need to clean up by doing
>> the dma buffer consumer stuff right...
> Yes all is linked :-).
> 
>>
>> Jonathan
>>> ---
>>>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>>>  include/linux/iio/consumer.h                | 13 +++++++++++++
>>>  2 files changed, 25 insertions(+)
>>>
>>> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
>>> index b8f550e..43c066a 100644
>>> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
>>> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
>>> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>>  }
>>>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>>>  
>>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
>>> +				      size_t length, size_t watermark)
>>> +{
>>> +	if (!length || length < watermark)
>>> +		return -EINVAL;
>>> +	cb_buff->buffer.watermark = watermark;
>>> +	cb_buff->buffer.length = length;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
>>> +
>>>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>>>  {
>>>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
>>> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
>>> index cb44771..0f6e94d 100644
>>> --- a/include/linux/iio/consumer.h
>>> +++ b/include/linux/iio/consumer.h
>>> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>>  						       void *private),
>>>  					     void *private);
>>>  /**
>>> + * iio_channel_cb_set_buffer_size() - set the buffer length.
>>> + * @cb_buffer:		The callback buffer from whom we want the channel
>>> + *			information.
>>> + * @length: buffer length in bytes
>>> + * @watermark: buffer watermark in bytes
>>> + *
>>> + * This function allows to configure the buffer length. The watermark if
>>> + * forced to half of the buffer.
>>> + */
>>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
>>> +				     size_t length, size_t watermark);
>>> +
>>> +/**
>>>   * iio_channel_release_all_cb() - release and unregister the callback.
>>>   * @cb_buffer:		The callback buffer that was allocated.
>>>   */
>>>
>>
>> --
>> 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] 109+ messages in thread

* [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes
@ 2017-03-25 16:01           ` Jonathan Cameron
  0 siblings, 0 replies; 109+ messages in thread
From: Jonathan Cameron @ 2017-03-25 16:01 UTC (permalink / raw)
  To: linux-arm-kernel

On 20/03/17 11:30, Arnaud Pouliquen wrote:
> 
> 
> On 03/19/2017 11:44 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add iio consumer API to set buffer size and watermark according
>>> to sysfs API.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Hmm. Not keen on the length one.  Setting a requested watermark
>> is fair enough.  There is no actually buffer in these cases though
>> so setting it's length is downright odd..
> Length and watermark are configurable from user land through sysfs.
> Seems to me logic to also propose it in inkern API...
> But I can clean length , no problem.
The concept of length for the consumer interface doesn't currently
make sense as it refers to an actual buffer.   The consumer interface
currently passes one entry at a time...
> 
>>
>> Guess this is part of the hacks we need to clean up by doing
>> the dma buffer consumer stuff right...
> Yes all is linked :-).
> 
>>
>> Jonathan
>>> ---
>>>  drivers/iio/buffer/industrialio-buffer-cb.c | 12 ++++++++++++
>>>  include/linux/iio/consumer.h                | 13 +++++++++++++
>>>  2 files changed, 25 insertions(+)
>>>
>>> diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
>>> index b8f550e..43c066a 100644
>>> --- a/drivers/iio/buffer/industrialio-buffer-cb.c
>>> +++ b/drivers/iio/buffer/industrialio-buffer-cb.c
>>> @@ -103,6 +103,18 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>>  }
>>>  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
>>>  
>>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buff,
>>> +				      size_t length, size_t watermark)
>>> +{
>>> +	if (!length || length < watermark)
>>> +		return -EINVAL;
>>> +	cb_buff->buffer.watermark = watermark;
>>> +	cb_buff->buffer.length = length;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
>>> +
>>>  int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
>>>  {
>>>  	return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
>>> diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
>>> index cb44771..0f6e94d 100644
>>> --- a/include/linux/iio/consumer.h
>>> +++ b/include/linux/iio/consumer.h
>>> @@ -134,6 +134,19 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
>>>  						       void *private),
>>>  					     void *private);
>>>  /**
>>> + * iio_channel_cb_set_buffer_size() - set the buffer length.
>>> + * @cb_buffer:		The callback buffer from whom we want the channel
>>> + *			information.
>>> + * @length: buffer length in bytes
>>> + * @watermark: buffer watermark in bytes
>>> + *
>>> + * This function allows to configure the buffer length. The watermark if
>>> + * forced to half of the buffer.
>>> + */
>>> +int iio_channel_cb_set_buffer_params(struct iio_cb_buffer *cb_buffer,
>>> +				     size_t length, size_t watermark);
>>> +
>>> +/**
>>>   * iio_channel_release_all_cb() - release and unregister the callback.
>>>   * @cb_buffer:		The callback buffer that was allocated.
>>>   */
>>>
>>
>> --
>> 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] 109+ messages in thread

* Re: [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
  2017-03-24 14:46     ` Rob Herring
  (?)
@ 2017-03-27 11:59       ` Mark Brown
  -1 siblings, 0 replies; 109+ messages in thread
From: Mark Brown @ 2017-03-27 11:59 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	olivier moysan, kernel, Liam Girdwood, linux-iio,
	Arnaud Pouliquen, Takashi Iwai, Maxime Coquelin,
	linux-arm-kernel, Peter Meerwald-Stadler, Hartmut Knaack,
	Jonathan Cameron, Alexandre Torgue


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

On Fri, Mar 24, 2017 at 09:46:03AM -0500, Rob Herring wrote:
> On Fri, Mar 17, 2017 at 03:08:21PM +0100, Arnaud Pouliquen wrote:

> > +Device-Tree bindings for dmic codec

> Please define what is and isn't a DMIC here. What's the interface?

Digital microphones are a well understood concept for anyone familiar
with the problem domain, they all provide a PDM output.

> > +
> > +Required properties:
> > +	- compatible: should be "dmic-codec".

> DMICs don't have part numbers?

Not meaningfully in this context any more than analogue microphones do -
they have no software visible interface.

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

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



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

* Re: [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
@ 2017-03-27 11:59       ` Mark Brown
  0 siblings, 0 replies; 109+ messages in thread
From: Mark Brown @ 2017-03-27 11:59 UTC (permalink / raw)
  To: Rob Herring
  Cc: Arnaud Pouliquen, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, devicetree, linux-arm-kernel,
	linux-iio, alsa-devel, kernel, Maxime Coquelin, Alexandre Torgue,
	olivier moysan

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

On Fri, Mar 24, 2017 at 09:46:03AM -0500, Rob Herring wrote:
> On Fri, Mar 17, 2017 at 03:08:21PM +0100, Arnaud Pouliquen wrote:

> > +Device-Tree bindings for dmic codec

> Please define what is and isn't a DMIC here. What's the interface?

Digital microphones are a well understood concept for anyone familiar
with the problem domain, they all provide a PDM output.

> > +
> > +Required properties:
> > +	- compatible: should be "dmic-codec".

> DMICs don't have part numbers?

Not meaningfully in this context any more than analogue microphones do -
they have no software visible interface.

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

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

* [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver
@ 2017-03-27 11:59       ` Mark Brown
  0 siblings, 0 replies; 109+ messages in thread
From: Mark Brown @ 2017-03-27 11:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Mar 24, 2017 at 09:46:03AM -0500, Rob Herring wrote:
> On Fri, Mar 17, 2017 at 03:08:21PM +0100, Arnaud Pouliquen wrote:

> > +Device-Tree bindings for dmic codec

> Please define what is and isn't a DMIC here. What's the interface?

Digital microphones are a well understood concept for anyone familiar
with the problem domain, they all provide a PDM output.

> > +
> > +Required properties:
> > +	- compatible: should be "dmic-codec".

> DMICs don't have part numbers?

Not meaningfully in this context any more than analogue microphones do -
they have no software visible interface.
-------------- 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/20170327/9e013c7a/attachment.sig>

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

* Re: [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
  2017-03-25 15:59           ` Jonathan Cameron
  (?)
@ 2017-03-28  7:45             ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-28  7:45 UTC (permalink / raw)
  To: Jonathan Cameron, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, alsa-devel, Olivier MOYSAN, kernel, linux-iio,
	Maxime Coquelin, linux-arm-kernel, Alexandre TORGUE



On 03/25/2017 04:59 PM, Jonathan Cameron wrote:
> On 20/03/17 11:29, Arnaud Pouliquen wrote:
>> Please find my comments in-line
>>
>> Thanks and Regards,
>> Arnaud
>>
>> On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
>>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>>> Add DFSDM driver to handle PDM audio microphones.
>>>>
>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> So key element here is that we really need to have a proper way of
>>> doing DMA buffer consumers from IIO (as you said in your cover letter).
>>>
>>> Not entirely obvious how to do this.  Would like some input from Lars
>>> if possible.  I'm afraid I don't have an suitable hardware to try anything
>>> out on (or the time really!).  This will obviously be quite different from
>>> the single scan stuff you have seen is there at the moment.
>> I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
>> DMA. I suppose that problematics are similar. Perhaps the extra
>> constrains in DFSDM is the in-kernel API used to get the data.
> Absolutely.  Cyclic dma is becoming more and more common on SoC ADCs.
> There are a few more fpga based ones in Analog devices tree as well -
> most of those actually do the dma buffer route rather than pushing through
> the normal buffer interface (kfifo effectively).  Right now we just
> don't have any means of accessing these dma buffers in kernel.
> 
> Unlike the normal consumer interface, I doubt we'll want to do demuxing
> of the stream for this sort of usecase - feels like exclusive use is more
> likely.
>>
>>>
>>> Whether this whole approach makes sense vs doing the dma in the alsa driver
>>> isn't clear to me.
>>>
>>> On the plus side, presumably we'll get love dma ADC support for free
>>> as part of doing it this way ;)
>>>
>>> It's been a while since I looked at the IIO dma buffer stuff at all. Will
>>> try and refresh my memory sometime this week.
>>
>> Ok, i will wait your feedback (and Lars's one) before updating my code
>> for next version.
> It may well be the case that it will take us sometime (when I say us, I mean
> you and Lars ;) to pin down how to do dma buffer consumers, so it may be
> a case of taking the driver in a state close to the current one with the
> intent to switch to a generic way of handling it later.

I had a short discussion with Lars on IIO IRC, seems that he also does
not see a straightforward solution for this IP.
The Fact that audio part is in a separate file should help to rework it
in a generic way.
So i will propose a V4 for upstream, integrating discussed fixes.

Thanks
Arnaud


> 
> As long as bindings and userspace don't change we can of course mess with
> anything we like.
> 
> Jonathan
>>>
>>> Jonathan
>>>> ---
>>>>  drivers/iio/adc/Kconfig                   |  13 +
>>>>  drivers/iio/adc/Makefile                  |   1 +
>>>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>>>  4 files changed, 775 insertions(+)
>>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>>>
>>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>>> index 3e0eb11..c108933 100644
>>>> --- a/drivers/iio/adc/Kconfig
>>>> +++ b/drivers/iio/adc/Kconfig
>>>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>>>  	  This driver can also be built as a module.  If so, the module
>>>>  	  will be called stm32-dfsdm-adc.
>>>>  
>>>> +config STM32_DFSDM_AUDIO
>>>> +	tristate "STMicroelectronics STM32 dfsdm audio
>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>> +	select STM32_DFSDM_CORE
>>>> +	select REGMAP_MMIO
>>>> +	select IIO_BUFFER_DMAENGINE
>>>> +	help
>>>> +	  Select this option to support Audio PDM micophone for
>>>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>>>> +
>>>> +	  This driver can also be built as a module.  If so, the module
>>>> +	  will be called stm32-dfsdm-audio.
>>>> +
>>>>  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 161f271..79f975d 100644
>>>> --- a/drivers/iio/adc/Makefile
>>>> +++ b/drivers/iio/adc/Makefile
>>>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>>>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>>>> new file mode 100644
>>>> index 0000000..115ef8f
>>>> --- /dev/null
>>>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>>>> @@ -0,0 +1,720 @@
>>>> +/*
>>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>>> + *
>>>> + * Copyright (C) 2017, 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/dmaengine.h>
>>>> +#include <linux/dma-mapping.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/regmap.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +#include <linux/iio/buffer.h>
>>>> +#include <linux/iio/hw_consumer.h>
>>>> +#include <linux/iio/sysfs.h>
>>>> +#include <linux/iio/trigger.h>
>>>> +#include <linux/iio/trigger_consumer.h>
>>>> +#include <linux/iio/triggered_buffer.h>
>>>> +
>>>> +#include "stm32-dfsdm.h"
>>>> +
>>>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>>>> +
>>>> +struct stm32_dfsdm_audio {
>>>> +	struct stm32_dfsdm *dfsdm;
>>>> +	unsigned int fl_id;
>>>> +	unsigned int ch_id;
>>>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>>>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>>>> +
>>>> +	u8 *rx_buf;
>>>> +	unsigned int bufi; /* Buffer current position */
>>>> +	unsigned int buf_sz; /* Buffer size */
>>>> +
>>>> +	struct dma_chan	*dma_chan;
>>>> +	dma_addr_t dma_buf;
>>>> +
>>>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>>>> +	void *cb_priv;
>>>> +};
>>>> +
>>>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>>>> +
>>>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				    const struct iio_chan_spec *chan, char *buf)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +
>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				    const struct iio_chan_spec *chan,
>>>> +				    const char *buf, size_t len)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>>> +	unsigned int spi_freq = pdmc->spi_freq;
>>>> +	unsigned int sample_freq;
>>>> +	int ret;
>>>> +
>>>> +	ret = kstrtoint(buf, 0, &sample_freq);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>>>> +	if (!sample_freq)
>>>> +		return -EINVAL;
>>>> +
>>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>>>> +
>>>> +	if (spi_freq % sample_freq)
>>>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>>>> +			 spi_freq / (spi_freq / sample_freq));
>>>> +
>>>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>>> +	if (ret < 0) {
>>>> +		dev_err(&indio_dev->dev,
>>>> +			"Not able to find filter parameter that match!\n");
>>>> +		return ret;
>>>> +	}
>>>> +	pdmc->sample_freq = sample_freq;
>>>> +
>>>> +	return len;
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				      const struct iio_chan_spec *chan,
>>>> +				      char *buf)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +
>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				      const struct iio_chan_spec *chan,
>>>> +				      const char *buf, size_t len)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>>> +	unsigned int sample_freq = pdmc->sample_freq;
>>>> +	unsigned int spi_freq;
>>>> +	int ret;
>>>> +
>>>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>>> +		return -EPERM;
>>>> +
>>>> +	ret = kstrtoint(buf, 0, &spi_freq);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>>>> +	if (!spi_freq)
>>>> +		return -EINVAL;
>>>> +
>>>> +	if (sample_freq) {
>>>> +		if (spi_freq % sample_freq)
>>>> +			dev_warn(&indio_dev->dev,
>>>> +				 "Sampling rate not accurate (%d)\n",
>>>> +				 spi_freq / (spi_freq / sample_freq));
>>>> +
>>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>>> +		if (ret < 0) {
>>>> +			dev_err(&indio_dev->dev,
>>>> +				"No filter parameters that match!\n");
>>>> +			return ret;
>>>> +		}
>>>> +	}
>>>> +	pdmc->spi_freq = spi_freq;
>>>> +
>>>> +	return len;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Define external info for SPI Frequency and audio sampling rate that can be
>>>> + * configured by ASoC driver through consumer.h API
>>>> + */
>>>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>>>> +	/* filter oversampling: Post filter oversampling ratio */
>>>> +	{
>>>> +		.name = "audio_sampling_rate",
>>>> +		.shared = IIO_SHARED_BY_TYPE,
>>>> +		.read = dfsdm_audio_get_rate,
>>>> +		.write = dfsdm_audio_set_rate,
>>>> +	},
>>>> +	/* data_right_bit_shift : Filter output data shifting */
>>>> +	{
>>>> +		.name = "spi_clk_freq",
>>>> +		.shared = IIO_SHARED_BY_TYPE,
>>>> +		.read = dfsdm_audio_get_spiclk,
>>>> +		.write = dfsdm_audio_set_spiclk,
>>>> +	},
>>>> +	{},
>>>> +};
>>>> +
>>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>>>> +{
>>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>>> +	int ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_dfsdm;
>>>> +
>>>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>>>> +					   pdmc->ch_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_channels;
>>>> +
>>>> +	/* Enable DMA transfer*/
>>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	/* Enable conversion triggered by SPI clock*/
>>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_channels;
>>>> +
>>>> +	return 0;
>>>> +
>>>> +stop_channels:
>>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>>>> +stop_dfsdm:
>>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>>>> +{
>>>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>>>> +
>>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>>>> +
>>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>>>> +				     unsigned int val)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>>>> +
>>>> +	/*
>>>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>>>> +	 * There should be :
>>>> +	 * - always one buffer (period) DMA is working on
>>>> +	 * - one buffer (period) driver pushed to ASoC side ().
>>>> +	 */
>>>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>>>> +	pdmc->buf_sz = watermark * 2;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>>>> +				 struct iio_trigger *trig)
>>>> +{
>>>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>>>> +		return 0;
>>>> +
>>>> +	return -EINVAL;
>>>> +}
>>>> +
>>>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>>>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>>>> +	.driver_module = THIS_MODULE,
>>>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>>>> +};
>>>> +
>>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = arg;
>>>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>>> +	unsigned int status;
>>>> +
>>>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>>>> +
>>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>>>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>>>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>>> +	}
>>>> +
>>>> +	return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>>>> +{
>>>> +	struct dma_tx_state state;
>>>> +	enum dma_status status;
>>>> +
>>>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>>>> +				     pdmc->dma_chan->cookie,
>>>> +				     &state);
>>>> +	if (status == DMA_IN_PROGRESS) {
>>>> +		/* Residue is size in bytes from end of buffer */
>>>> +		unsigned int i = pdmc->buf_sz - state.residue;
>>>> +		unsigned int size;
>>>> +
>>>> +		/* Return available bytes */
>>>> +		if (i >= pdmc->bufi)
>>>> +			size = i - pdmc->bufi;
>>>> +		else
>>>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>>>> +
>>>> +		return size;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>>>> +{
>>>> +	struct iio_dev *indio_dev = data;
>>>> +
>>>> +	iio_trigger_poll_chained(indio_dev->trig);
>>> This shouldn't be going through the trigger infrastructure at all really.
>>> I'm not 100% sure what doing so is gaining you...
>> Yes i will clean trigger part and call directly the ASoC callback here.
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct dma_async_tx_descriptor *desc;
>>>> +	dma_cookie_t cookie;
>>>> +	int ret;
>>>> +
>>>> +	if (!pdmc->dma_chan)
>>>> +		return -EINVAL;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>>>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>>>> +
>>>> +	/* Prepare a DMA cyclic transaction */
>>>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>>>> +					 pdmc->dma_buf,
>>>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>>>> +					 DMA_DEV_TO_MEM,
>>>> +					 DMA_PREP_INTERRUPT);
>>>> +	if (!desc)
>>>> +		return -EBUSY;
>>>> +
>>>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>>>> +	desc->callback_param = indio_dev;
>>>> +
>>>> +	cookie = dmaengine_submit(desc);
>>>> +	ret = dma_submit_error(cookie);
>>>> +	if (ret) {
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	/* Issue pending DMA requests */
>>>> +	dma_async_issue_pending(pdmc->dma_chan);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>>> +	/* Reset pdmc buffer index */
>>>> +	pdmc->bufi = 0;
>>>> +
>>>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>>>> +	if (ret) {
>>>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>>>> +	if (ret) {
>>>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>>>> +		goto err_stop_conv;
>>>> +	}
>>>> +
>>>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>>>> +	if (ret < 0) {
>>>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>>>> +		goto err_stop_dma;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_stop_dma:
>>>> +	if (pdmc->dma_chan)
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +err_stop_conv:
>>>> +	stm32_dfsdm_stop_conv(pdmc);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>>>> +	if (ret < 0)
>>>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>>>> +
>>>> +	if (pdmc->dma_chan)
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +
>>>> +	stm32_dfsdm_stop_conv(pdmc);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>>> +	.postenable = &stm32_dfsdm_postenable,
>>>> +	.predisable = &stm32_dfsdm_predisable,
>>>> +};
>>>> +
>>>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>>>> +{
>>> OK. So now I kind of understand what the trigger provided in the adc driver
>>> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
>>> a hack.  We need fullblown DMA buffer consumers to do this right.
>> Yes exactly.
>>>
>>> Probably best person to comment on that is Lars. 
>>>> +	struct iio_poll_func *pf = p;
>>>> +	struct iio_dev *indio_dev = pf->indio_dev;
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	size_t old_pos;
>>>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>>>> +
>>>> +	/*
>>>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>>>> +	 * an interface to push data samples per samples.
>>>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>>>> +	 * and interface is hacked using a private callback registered by ASoC.
>>>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>>>> +	 * support in IIO.
>>>> +	 */
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>>>> +		pdmc->bufi, available);
>>>> +	old_pos = pdmc->bufi;
>>>> +	while (available >= indio_dev->scan_bytes) {
>>>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>>>> +
>>>> +		/* Mask 8 LSB that contains the channel ID */
>>>> +		*buffer &= 0xFFFFFF00;
>>>> +		available -= indio_dev->scan_bytes;
>>>> +		pdmc->bufi += indio_dev->scan_bytes;
>>>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>>>> +			if (pdmc->cb)
>>>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>>>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>>>> +			pdmc->bufi = 0;
>>>> +			old_pos = 0;
>>>> +		}
>>>> +	}
>>>> +	if (pdmc->cb)
>>>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>>>> +				pdmc->cb_priv);
>>>> +
>>>> +	iio_trigger_notify_done(indio_dev->trig);
>>>> +
>>>> +	return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb - register a callback
>>>> + *	that will be called when DMA transfer period is achieved.
>>>> + *
>>>> + * @iio_dev: Handle to IIO device.
>>>> + * @cb: pointer to callback function.
>>>> + *	@data: pointer to data buffer
>>>> + *	@size: size in byte of the data buffer
>>>> + *	@private: pointer to consumer private structure
>>>> + * @private: pointer to consumer private structure
>>>> + */
>>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>>> +			    int (*cb)(const void *data, size_t size,
>>>> +				      void *private),
>>>> +			    void *private)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +
>>>> +	if (!iio_dev)
>>>> +		return -EINVAL;
>>>> +	pdmc = iio_priv(iio_dev);
>>>> +
>>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>>> +		return -EINVAL;
>>>> +
>>>> +	pdmc->cb = cb;
>>>> +	pdmc->cb_priv = private;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>>>> + *
>>>> + * @iio_dev: Handle to IIO device.
>>>> + */
>>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +
>>>> +	if (!iio_dev)
>>>> +		return -EINVAL;
>>>> +	pdmc = iio_priv(iio_dev);
>>>> +
>>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>>> +		return -EINVAL;
>>>> +	pdmc->cb = NULL;
>>>> +	pdmc->cb_priv = NULL;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>>>> +
>>>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct iio_chan_spec *ch;
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>>>> +	if (!ch)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ch->type = IIO_VOLTAGE;
>>>> +	ch->indexed = 1;
>>>> +	ch->scan_index = 0;
>>>> +	ch->ext_info = dfsdm_adc_ext_info;
>>>> +
>>>> +	ch->scan_type.sign = 's';
>>>> +	ch->scan_type.realbits = 24;
>>>> +	ch->scan_type.storagebits = 32;
>>>> +
>>>> +	pdmc->ch_id = ch->channel;
>>>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>>>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>>>> +
>>>> +	indio_dev->num_channels = 1;
>>>> +	indio_dev->channels = ch;
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>>>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>>>> +	{}
>>>> +};
>>>> +
>>>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct dma_slave_config config;
>>>> +	int ret;
>>>> +
>>>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>>>> +	if (!pdmc->dma_chan)
>>>> +		return -EINVAL;
>>>> +
>>>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>>>> +					 DFSDM_DMA_BUFFER_SIZE,
>>>> +					 &pdmc->dma_buf, GFP_KERNEL);
>>>> +	if (!pdmc->rx_buf) {
>>>> +		ret = -ENOMEM;
>>>> +		goto err_release;
>>>> +	}
>>>> +
>>>> +	/* Configure DMA channel to read data register */
>>>> +	memset(&config, 0, sizeof(config));
>>>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>>>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>>>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>>>> +
>>>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>>>> +	if (ret)
>>>> +		goto err_free;
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_free:
>>>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>>>> +			  pdmc->rx_buf, pdmc->dma_buf);
>>>> +err_release:
>>>> +	dma_release_channel(pdmc->dma_chan);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>>>> +{
>>>> +	struct device *dev = &pdev->dev;
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +	struct device_node *np = dev->of_node;
>>>> +	struct iio_dev *iio;
>>>> +	char *name;
>>>> +	int ret, irq, val;
>>>> +
>>>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>>>> +	if (IS_ERR(iio)) {
>>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>>> +		return PTR_ERR(iio);
>>>> +	}
>>>> +
>>>> +	pdmc = iio_priv(iio);
>>>> +	if (IS_ERR(pdmc)) {
>>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>>> +		return PTR_ERR(pdmc);
>>>> +	}
>>>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>>>> +
>>>> +	iio->name = np->name;
>>>> +	iio->dev.parent = dev;
>>>> +	iio->dev.of_node = np;
>>>> +	iio->info = &stm32_dfsdm_info_pdmc;
>>>> +	iio->modes = INDIO_DIRECT_MODE;
>>>> +
>>>> +	platform_set_drvdata(pdev, pdmc);
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>>>> +	if (ret != 0) {
>>>> +		dev_err(dev, "Missing reg property\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>>>> +	if (!name)
>>>> +		return -ENOMEM;
>>>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>>>> +	iio->name = name;
>>>> +
>>>> +	/*
>>>> +	 * In a first step IRQs generated for channels are not treated.
>>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>>> +	 */
>>>> +	irq = platform_get_irq(pdev, 0);
>>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>>> +			       0, pdev->name, pdmc);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "Failed to request IRQ\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "Failed to set filter order\n");
>>>> +		return ret;
>>>> +	}
>>>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>>> +	if (!ret)
>>>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>>>> +
>>>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "DMA request failed\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>>>> +
>>>> +	ret = iio_triggered_buffer_setup(iio,
>>>> +					 &iio_pollfunc_store_time,
>>>> +					 &stm32_dfsdm_audio_trigger_handler,
>>>> +					 &stm32_dfsdm_buffer_setup_ops);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>>>> +		goto err_dma_disable;
>>>> +	}
>>>> +
>>>> +	ret = iio_device_register(iio);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>>>> +		goto err_buffer_cleanup;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_buffer_cleanup:
>>>> +	iio_triggered_buffer_cleanup(iio);
>>>> +
>>>> +err_dma_disable:
>>>> +	if (pdmc->dma_chan) {
>>>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>>>> +				  DFSDM_DMA_BUFFER_SIZE,
>>>> +				  pdmc->rx_buf, pdmc->dma_buf);
>>>> +		dma_release_channel(pdmc->dma_chan);
>>>> +	}
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>>>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>>>> +
>>>> +	iio_device_unregister(iio);
>>> Same as in the other driver. Can just use the devm_iio_device_register
>>> version and drop remove.
>> oops missing the previous comment... sorry.
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>>>> +	.driver = {
>>>> +		.name = "stm32-dfsdm-audio",
>>>> +		.of_match_table = stm32_dfsdm_audio_match,
>>>> +	},
>>>> +	.probe = stm32_dfsdm_audio_probe,
>>>> +	.remove = stm32_dfsdm_audio_remove,
>>>> +};
>>>> +module_platform_driver(stm32_dfsdm_audio_driver);
>>>> +
>>>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>>>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>>> new file mode 100644
>>>> index 0000000..9b41b28
>>>> --- /dev/null
>>>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>>> @@ -0,0 +1,41 @@
>>>> +/*
>>>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>>>> + *
>>>> + * 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 STM32_DFSDM_AUDIO_H
>>>> +#define STM32_DFSDM_AUDIO_H
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>>>> + * @dev:	Pointer to client device.
>>>> + * @indio_dev:	Device on which the channel exists.
>>>> + * @cb:		Callback function.
>>>> + *		@data:  pointer to data buffer
>>>> + *		@size: size of the data buffer in bytes
>>>> + * @private:	Private data passed to callback.
>>>> + *
>>>> + */
>>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>>> +			    int (*cb)(const void *data, size_t size,
>>>> +				      void *private),
>>>> +			    void *private);
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>>>> + * @indio_dev:	Device on which the channel exists.
>>>> + */
>>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>>>> +
>>>> +#endif
>>>>
>>>
>> --
>> 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] 109+ messages in thread

* Re: [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-28  7:45             ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-28  7:45 UTC (permalink / raw)
  To: Jonathan Cameron, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, kernel,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN



On 03/25/2017 04:59 PM, Jonathan Cameron wrote:
> On 20/03/17 11:29, Arnaud Pouliquen wrote:
>> Please find my comments in-line
>>
>> Thanks and Regards,
>> Arnaud
>>
>> On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
>>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>>> Add DFSDM driver to handle PDM audio microphones.
>>>>
>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> So key element here is that we really need to have a proper way of
>>> doing DMA buffer consumers from IIO (as you said in your cover letter).
>>>
>>> Not entirely obvious how to do this.  Would like some input from Lars
>>> if possible.  I'm afraid I don't have an suitable hardware to try anything
>>> out on (or the time really!).  This will obviously be quite different from
>>> the single scan stuff you have seen is there at the moment.
>> I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
>> DMA. I suppose that problematics are similar. Perhaps the extra
>> constrains in DFSDM is the in-kernel API used to get the data.
> Absolutely.  Cyclic dma is becoming more and more common on SoC ADCs.
> There are a few more fpga based ones in Analog devices tree as well -
> most of those actually do the dma buffer route rather than pushing through
> the normal buffer interface (kfifo effectively).  Right now we just
> don't have any means of accessing these dma buffers in kernel.
> 
> Unlike the normal consumer interface, I doubt we'll want to do demuxing
> of the stream for this sort of usecase - feels like exclusive use is more
> likely.
>>
>>>
>>> Whether this whole approach makes sense vs doing the dma in the alsa driver
>>> isn't clear to me.
>>>
>>> On the plus side, presumably we'll get love dma ADC support for free
>>> as part of doing it this way ;)
>>>
>>> It's been a while since I looked at the IIO dma buffer stuff at all. Will
>>> try and refresh my memory sometime this week.
>>
>> Ok, i will wait your feedback (and Lars's one) before updating my code
>> for next version.
> It may well be the case that it will take us sometime (when I say us, I mean
> you and Lars ;) to pin down how to do dma buffer consumers, so it may be
> a case of taking the driver in a state close to the current one with the
> intent to switch to a generic way of handling it later.

I had a short discussion with Lars on IIO IRC, seems that he also does
not see a straightforward solution for this IP.
The Fact that audio part is in a separate file should help to rework it
in a generic way.
So i will propose a V4 for upstream, integrating discussed fixes.

Thanks
Arnaud


> 
> As long as bindings and userspace don't change we can of course mess with
> anything we like.
> 
> Jonathan
>>>
>>> Jonathan
>>>> ---
>>>>  drivers/iio/adc/Kconfig                   |  13 +
>>>>  drivers/iio/adc/Makefile                  |   1 +
>>>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>>>  4 files changed, 775 insertions(+)
>>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>>>
>>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>>> index 3e0eb11..c108933 100644
>>>> --- a/drivers/iio/adc/Kconfig
>>>> +++ b/drivers/iio/adc/Kconfig
>>>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>>>  	  This driver can also be built as a module.  If so, the module
>>>>  	  will be called stm32-dfsdm-adc.
>>>>  
>>>> +config STM32_DFSDM_AUDIO
>>>> +	tristate "STMicroelectronics STM32 dfsdm audio
>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>> +	select STM32_DFSDM_CORE
>>>> +	select REGMAP_MMIO
>>>> +	select IIO_BUFFER_DMAENGINE
>>>> +	help
>>>> +	  Select this option to support Audio PDM micophone for
>>>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>>>> +
>>>> +	  This driver can also be built as a module.  If so, the module
>>>> +	  will be called stm32-dfsdm-audio.
>>>> +
>>>>  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 161f271..79f975d 100644
>>>> --- a/drivers/iio/adc/Makefile
>>>> +++ b/drivers/iio/adc/Makefile
>>>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>>>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>>>> new file mode 100644
>>>> index 0000000..115ef8f
>>>> --- /dev/null
>>>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>>>> @@ -0,0 +1,720 @@
>>>> +/*
>>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>>> + *
>>>> + * Copyright (C) 2017, 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/dmaengine.h>
>>>> +#include <linux/dma-mapping.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/regmap.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +#include <linux/iio/buffer.h>
>>>> +#include <linux/iio/hw_consumer.h>
>>>> +#include <linux/iio/sysfs.h>
>>>> +#include <linux/iio/trigger.h>
>>>> +#include <linux/iio/trigger_consumer.h>
>>>> +#include <linux/iio/triggered_buffer.h>
>>>> +
>>>> +#include "stm32-dfsdm.h"
>>>> +
>>>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>>>> +
>>>> +struct stm32_dfsdm_audio {
>>>> +	struct stm32_dfsdm *dfsdm;
>>>> +	unsigned int fl_id;
>>>> +	unsigned int ch_id;
>>>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>>>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>>>> +
>>>> +	u8 *rx_buf;
>>>> +	unsigned int bufi; /* Buffer current position */
>>>> +	unsigned int buf_sz; /* Buffer size */
>>>> +
>>>> +	struct dma_chan	*dma_chan;
>>>> +	dma_addr_t dma_buf;
>>>> +
>>>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>>>> +	void *cb_priv;
>>>> +};
>>>> +
>>>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>>>> +
>>>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				    const struct iio_chan_spec *chan, char *buf)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +
>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				    const struct iio_chan_spec *chan,
>>>> +				    const char *buf, size_t len)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>>> +	unsigned int spi_freq = pdmc->spi_freq;
>>>> +	unsigned int sample_freq;
>>>> +	int ret;
>>>> +
>>>> +	ret = kstrtoint(buf, 0, &sample_freq);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>>>> +	if (!sample_freq)
>>>> +		return -EINVAL;
>>>> +
>>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>>>> +
>>>> +	if (spi_freq % sample_freq)
>>>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>>>> +			 spi_freq / (spi_freq / sample_freq));
>>>> +
>>>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>>> +	if (ret < 0) {
>>>> +		dev_err(&indio_dev->dev,
>>>> +			"Not able to find filter parameter that match!\n");
>>>> +		return ret;
>>>> +	}
>>>> +	pdmc->sample_freq = sample_freq;
>>>> +
>>>> +	return len;
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				      const struct iio_chan_spec *chan,
>>>> +				      char *buf)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +
>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				      const struct iio_chan_spec *chan,
>>>> +				      const char *buf, size_t len)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>>> +	unsigned int sample_freq = pdmc->sample_freq;
>>>> +	unsigned int spi_freq;
>>>> +	int ret;
>>>> +
>>>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>>> +		return -EPERM;
>>>> +
>>>> +	ret = kstrtoint(buf, 0, &spi_freq);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>>>> +	if (!spi_freq)
>>>> +		return -EINVAL;
>>>> +
>>>> +	if (sample_freq) {
>>>> +		if (spi_freq % sample_freq)
>>>> +			dev_warn(&indio_dev->dev,
>>>> +				 "Sampling rate not accurate (%d)\n",
>>>> +				 spi_freq / (spi_freq / sample_freq));
>>>> +
>>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>>> +		if (ret < 0) {
>>>> +			dev_err(&indio_dev->dev,
>>>> +				"No filter parameters that match!\n");
>>>> +			return ret;
>>>> +		}
>>>> +	}
>>>> +	pdmc->spi_freq = spi_freq;
>>>> +
>>>> +	return len;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Define external info for SPI Frequency and audio sampling rate that can be
>>>> + * configured by ASoC driver through consumer.h API
>>>> + */
>>>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>>>> +	/* filter oversampling: Post filter oversampling ratio */
>>>> +	{
>>>> +		.name = "audio_sampling_rate",
>>>> +		.shared = IIO_SHARED_BY_TYPE,
>>>> +		.read = dfsdm_audio_get_rate,
>>>> +		.write = dfsdm_audio_set_rate,
>>>> +	},
>>>> +	/* data_right_bit_shift : Filter output data shifting */
>>>> +	{
>>>> +		.name = "spi_clk_freq",
>>>> +		.shared = IIO_SHARED_BY_TYPE,
>>>> +		.read = dfsdm_audio_get_spiclk,
>>>> +		.write = dfsdm_audio_set_spiclk,
>>>> +	},
>>>> +	{},
>>>> +};
>>>> +
>>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>>>> +{
>>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>>> +	int ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_dfsdm;
>>>> +
>>>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>>>> +					   pdmc->ch_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_channels;
>>>> +
>>>> +	/* Enable DMA transfer*/
>>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	/* Enable conversion triggered by SPI clock*/
>>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_channels;
>>>> +
>>>> +	return 0;
>>>> +
>>>> +stop_channels:
>>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>>>> +stop_dfsdm:
>>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>>>> +{
>>>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>>>> +
>>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>>>> +
>>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>>>> +				     unsigned int val)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>>>> +
>>>> +	/*
>>>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>>>> +	 * There should be :
>>>> +	 * - always one buffer (period) DMA is working on
>>>> +	 * - one buffer (period) driver pushed to ASoC side ().
>>>> +	 */
>>>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>>>> +	pdmc->buf_sz = watermark * 2;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>>>> +				 struct iio_trigger *trig)
>>>> +{
>>>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>>>> +		return 0;
>>>> +
>>>> +	return -EINVAL;
>>>> +}
>>>> +
>>>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>>>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>>>> +	.driver_module = THIS_MODULE,
>>>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>>>> +};
>>>> +
>>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = arg;
>>>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>>> +	unsigned int status;
>>>> +
>>>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>>>> +
>>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>>>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>>>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>>> +	}
>>>> +
>>>> +	return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>>>> +{
>>>> +	struct dma_tx_state state;
>>>> +	enum dma_status status;
>>>> +
>>>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>>>> +				     pdmc->dma_chan->cookie,
>>>> +				     &state);
>>>> +	if (status == DMA_IN_PROGRESS) {
>>>> +		/* Residue is size in bytes from end of buffer */
>>>> +		unsigned int i = pdmc->buf_sz - state.residue;
>>>> +		unsigned int size;
>>>> +
>>>> +		/* Return available bytes */
>>>> +		if (i >= pdmc->bufi)
>>>> +			size = i - pdmc->bufi;
>>>> +		else
>>>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>>>> +
>>>> +		return size;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>>>> +{
>>>> +	struct iio_dev *indio_dev = data;
>>>> +
>>>> +	iio_trigger_poll_chained(indio_dev->trig);
>>> This shouldn't be going through the trigger infrastructure at all really.
>>> I'm not 100% sure what doing so is gaining you...
>> Yes i will clean trigger part and call directly the ASoC callback here.
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct dma_async_tx_descriptor *desc;
>>>> +	dma_cookie_t cookie;
>>>> +	int ret;
>>>> +
>>>> +	if (!pdmc->dma_chan)
>>>> +		return -EINVAL;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>>>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>>>> +
>>>> +	/* Prepare a DMA cyclic transaction */
>>>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>>>> +					 pdmc->dma_buf,
>>>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>>>> +					 DMA_DEV_TO_MEM,
>>>> +					 DMA_PREP_INTERRUPT);
>>>> +	if (!desc)
>>>> +		return -EBUSY;
>>>> +
>>>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>>>> +	desc->callback_param = indio_dev;
>>>> +
>>>> +	cookie = dmaengine_submit(desc);
>>>> +	ret = dma_submit_error(cookie);
>>>> +	if (ret) {
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	/* Issue pending DMA requests */
>>>> +	dma_async_issue_pending(pdmc->dma_chan);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>>> +	/* Reset pdmc buffer index */
>>>> +	pdmc->bufi = 0;
>>>> +
>>>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>>>> +	if (ret) {
>>>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>>>> +	if (ret) {
>>>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>>>> +		goto err_stop_conv;
>>>> +	}
>>>> +
>>>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>>>> +	if (ret < 0) {
>>>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>>>> +		goto err_stop_dma;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_stop_dma:
>>>> +	if (pdmc->dma_chan)
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +err_stop_conv:
>>>> +	stm32_dfsdm_stop_conv(pdmc);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>>>> +	if (ret < 0)
>>>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>>>> +
>>>> +	if (pdmc->dma_chan)
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +
>>>> +	stm32_dfsdm_stop_conv(pdmc);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>>> +	.postenable = &stm32_dfsdm_postenable,
>>>> +	.predisable = &stm32_dfsdm_predisable,
>>>> +};
>>>> +
>>>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>>>> +{
>>> OK. So now I kind of understand what the trigger provided in the adc driver
>>> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
>>> a hack.  We need fullblown DMA buffer consumers to do this right.
>> Yes exactly.
>>>
>>> Probably best person to comment on that is Lars. 
>>>> +	struct iio_poll_func *pf = p;
>>>> +	struct iio_dev *indio_dev = pf->indio_dev;
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	size_t old_pos;
>>>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>>>> +
>>>> +	/*
>>>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>>>> +	 * an interface to push data samples per samples.
>>>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>>>> +	 * and interface is hacked using a private callback registered by ASoC.
>>>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>>>> +	 * support in IIO.
>>>> +	 */
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>>>> +		pdmc->bufi, available);
>>>> +	old_pos = pdmc->bufi;
>>>> +	while (available >= indio_dev->scan_bytes) {
>>>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>>>> +
>>>> +		/* Mask 8 LSB that contains the channel ID */
>>>> +		*buffer &= 0xFFFFFF00;
>>>> +		available -= indio_dev->scan_bytes;
>>>> +		pdmc->bufi += indio_dev->scan_bytes;
>>>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>>>> +			if (pdmc->cb)
>>>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>>>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>>>> +			pdmc->bufi = 0;
>>>> +			old_pos = 0;
>>>> +		}
>>>> +	}
>>>> +	if (pdmc->cb)
>>>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>>>> +				pdmc->cb_priv);
>>>> +
>>>> +	iio_trigger_notify_done(indio_dev->trig);
>>>> +
>>>> +	return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb - register a callback
>>>> + *	that will be called when DMA transfer period is achieved.
>>>> + *
>>>> + * @iio_dev: Handle to IIO device.
>>>> + * @cb: pointer to callback function.
>>>> + *	@data: pointer to data buffer
>>>> + *	@size: size in byte of the data buffer
>>>> + *	@private: pointer to consumer private structure
>>>> + * @private: pointer to consumer private structure
>>>> + */
>>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>>> +			    int (*cb)(const void *data, size_t size,
>>>> +				      void *private),
>>>> +			    void *private)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +
>>>> +	if (!iio_dev)
>>>> +		return -EINVAL;
>>>> +	pdmc = iio_priv(iio_dev);
>>>> +
>>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>>> +		return -EINVAL;
>>>> +
>>>> +	pdmc->cb = cb;
>>>> +	pdmc->cb_priv = private;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>>>> + *
>>>> + * @iio_dev: Handle to IIO device.
>>>> + */
>>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +
>>>> +	if (!iio_dev)
>>>> +		return -EINVAL;
>>>> +	pdmc = iio_priv(iio_dev);
>>>> +
>>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>>> +		return -EINVAL;
>>>> +	pdmc->cb = NULL;
>>>> +	pdmc->cb_priv = NULL;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>>>> +
>>>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct iio_chan_spec *ch;
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>>>> +	if (!ch)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ch->type = IIO_VOLTAGE;
>>>> +	ch->indexed = 1;
>>>> +	ch->scan_index = 0;
>>>> +	ch->ext_info = dfsdm_adc_ext_info;
>>>> +
>>>> +	ch->scan_type.sign = 's';
>>>> +	ch->scan_type.realbits = 24;
>>>> +	ch->scan_type.storagebits = 32;
>>>> +
>>>> +	pdmc->ch_id = ch->channel;
>>>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>>>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>>>> +
>>>> +	indio_dev->num_channels = 1;
>>>> +	indio_dev->channels = ch;
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>>>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>>>> +	{}
>>>> +};
>>>> +
>>>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct dma_slave_config config;
>>>> +	int ret;
>>>> +
>>>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>>>> +	if (!pdmc->dma_chan)
>>>> +		return -EINVAL;
>>>> +
>>>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>>>> +					 DFSDM_DMA_BUFFER_SIZE,
>>>> +					 &pdmc->dma_buf, GFP_KERNEL);
>>>> +	if (!pdmc->rx_buf) {
>>>> +		ret = -ENOMEM;
>>>> +		goto err_release;
>>>> +	}
>>>> +
>>>> +	/* Configure DMA channel to read data register */
>>>> +	memset(&config, 0, sizeof(config));
>>>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>>>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>>>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>>>> +
>>>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>>>> +	if (ret)
>>>> +		goto err_free;
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_free:
>>>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>>>> +			  pdmc->rx_buf, pdmc->dma_buf);
>>>> +err_release:
>>>> +	dma_release_channel(pdmc->dma_chan);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>>>> +{
>>>> +	struct device *dev = &pdev->dev;
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +	struct device_node *np = dev->of_node;
>>>> +	struct iio_dev *iio;
>>>> +	char *name;
>>>> +	int ret, irq, val;
>>>> +
>>>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>>>> +	if (IS_ERR(iio)) {
>>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>>> +		return PTR_ERR(iio);
>>>> +	}
>>>> +
>>>> +	pdmc = iio_priv(iio);
>>>> +	if (IS_ERR(pdmc)) {
>>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>>> +		return PTR_ERR(pdmc);
>>>> +	}
>>>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>>>> +
>>>> +	iio->name = np->name;
>>>> +	iio->dev.parent = dev;
>>>> +	iio->dev.of_node = np;
>>>> +	iio->info = &stm32_dfsdm_info_pdmc;
>>>> +	iio->modes = INDIO_DIRECT_MODE;
>>>> +
>>>> +	platform_set_drvdata(pdev, pdmc);
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>>>> +	if (ret != 0) {
>>>> +		dev_err(dev, "Missing reg property\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>>>> +	if (!name)
>>>> +		return -ENOMEM;
>>>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>>>> +	iio->name = name;
>>>> +
>>>> +	/*
>>>> +	 * In a first step IRQs generated for channels are not treated.
>>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>>> +	 */
>>>> +	irq = platform_get_irq(pdev, 0);
>>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>>> +			       0, pdev->name, pdmc);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "Failed to request IRQ\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "Failed to set filter order\n");
>>>> +		return ret;
>>>> +	}
>>>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>>> +	if (!ret)
>>>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>>>> +
>>>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "DMA request failed\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>>>> +
>>>> +	ret = iio_triggered_buffer_setup(iio,
>>>> +					 &iio_pollfunc_store_time,
>>>> +					 &stm32_dfsdm_audio_trigger_handler,
>>>> +					 &stm32_dfsdm_buffer_setup_ops);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>>>> +		goto err_dma_disable;
>>>> +	}
>>>> +
>>>> +	ret = iio_device_register(iio);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>>>> +		goto err_buffer_cleanup;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_buffer_cleanup:
>>>> +	iio_triggered_buffer_cleanup(iio);
>>>> +
>>>> +err_dma_disable:
>>>> +	if (pdmc->dma_chan) {
>>>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>>>> +				  DFSDM_DMA_BUFFER_SIZE,
>>>> +				  pdmc->rx_buf, pdmc->dma_buf);
>>>> +		dma_release_channel(pdmc->dma_chan);
>>>> +	}
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>>>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>>>> +
>>>> +	iio_device_unregister(iio);
>>> Same as in the other driver. Can just use the devm_iio_device_register
>>> version and drop remove.
>> oops missing the previous comment... sorry.
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>>>> +	.driver = {
>>>> +		.name = "stm32-dfsdm-audio",
>>>> +		.of_match_table = stm32_dfsdm_audio_match,
>>>> +	},
>>>> +	.probe = stm32_dfsdm_audio_probe,
>>>> +	.remove = stm32_dfsdm_audio_remove,
>>>> +};
>>>> +module_platform_driver(stm32_dfsdm_audio_driver);
>>>> +
>>>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>>>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>>> new file mode 100644
>>>> index 0000000..9b41b28
>>>> --- /dev/null
>>>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>>> @@ -0,0 +1,41 @@
>>>> +/*
>>>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>>>> + *
>>>> + * 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 STM32_DFSDM_AUDIO_H
>>>> +#define STM32_DFSDM_AUDIO_H
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>>>> + * @dev:	Pointer to client device.
>>>> + * @indio_dev:	Device on which the channel exists.
>>>> + * @cb:		Callback function.
>>>> + *		@data:  pointer to data buffer
>>>> + *		@size: size of the data buffer in bytes
>>>> + * @private:	Private data passed to callback.
>>>> + *
>>>> + */
>>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>>> +			    int (*cb)(const void *data, size_t size,
>>>> +				      void *private),
>>>> +			    void *private);
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>>>> + * @indio_dev:	Device on which the channel exists.
>>>> + */
>>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>>>> +
>>>> +#endif
>>>>
>>>
>> --
>> 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] 109+ messages in thread

* [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone
@ 2017-03-28  7:45             ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-28  7:45 UTC (permalink / raw)
  To: linux-arm-kernel



On 03/25/2017 04:59 PM, Jonathan Cameron wrote:
> On 20/03/17 11:29, Arnaud Pouliquen wrote:
>> Please find my comments in-line
>>
>> Thanks and Regards,
>> Arnaud
>>
>> On 03/19/2017 11:38 PM, Jonathan Cameron wrote:
>>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>>> Add DFSDM driver to handle PDM audio microphones.
>>>>
>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> So key element here is that we really need to have a proper way of
>>> doing DMA buffer consumers from IIO (as you said in your cover letter).
>>>
>>> Not entirely obvious how to do this.  Would like some input from Lars
>>> if possible.  I'm afraid I don't have an suitable hardware to try anything
>>> out on (or the time really!).  This will obviously be quite different from
>>> the single scan stuff you have seen is there at the moment.
>> I saw 2 other drivers ti_am335x_adc.c and stm32_adc.c) that use cyclic
>> DMA. I suppose that problematics are similar. Perhaps the extra
>> constrains in DFSDM is the in-kernel API used to get the data.
> Absolutely.  Cyclic dma is becoming more and more common on SoC ADCs.
> There are a few more fpga based ones in Analog devices tree as well -
> most of those actually do the dma buffer route rather than pushing through
> the normal buffer interface (kfifo effectively).  Right now we just
> don't have any means of accessing these dma buffers in kernel.
> 
> Unlike the normal consumer interface, I doubt we'll want to do demuxing
> of the stream for this sort of usecase - feels like exclusive use is more
> likely.
>>
>>>
>>> Whether this whole approach makes sense vs doing the dma in the alsa driver
>>> isn't clear to me.
>>>
>>> On the plus side, presumably we'll get love dma ADC support for free
>>> as part of doing it this way ;)
>>>
>>> It's been a while since I looked at the IIO dma buffer stuff at all. Will
>>> try and refresh my memory sometime this week.
>>
>> Ok, i will wait your feedback (and Lars's one) before updating my code
>> for next version.
> It may well be the case that it will take us sometime (when I say us, I mean
> you and Lars ;) to pin down how to do dma buffer consumers, so it may be
> a case of taking the driver in a state close to the current one with the
> intent to switch to a generic way of handling it later.

I had a short discussion with Lars on IIO IRC, seems that he also does
not see a straightforward solution for this IP.
The Fact that audio part is in a separate file should help to rework it
in a generic way.
So i will propose a V4 for upstream, integrating discussed fixes.

Thanks
Arnaud


> 
> As long as bindings and userspace don't change we can of course mess with
> anything we like.
> 
> Jonathan
>>>
>>> Jonathan
>>>> ---
>>>>  drivers/iio/adc/Kconfig                   |  13 +
>>>>  drivers/iio/adc/Makefile                  |   1 +
>>>>  drivers/iio/adc/stm32-dfsdm-audio.c       | 720 ++++++++++++++++++++++++++++++
>>>>  include/linux/iio/adc/stm32-dfsdm-audio.h |  41 ++
>>>>  4 files changed, 775 insertions(+)
>>>>  create mode 100644 drivers/iio/adc/stm32-dfsdm-audio.c
>>>>  create mode 100644 include/linux/iio/adc/stm32-dfsdm-audio.h
>>>>
>>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>>> index 3e0eb11..c108933 100644
>>>> --- a/drivers/iio/adc/Kconfig
>>>> +++ b/drivers/iio/adc/Kconfig
>>>> @@ -478,6 +478,19 @@ config STM32_DFSDM_ADC
>>>>  	  This driver can also be built as a module.  If so, the module
>>>>  	  will be called stm32-dfsdm-adc.
>>>>  
>>>> +config STM32_DFSDM_AUDIO
>>>> +	tristate "STMicroelectronics STM32 dfsdm audio
>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>> +	select STM32_DFSDM_CORE
>>>> +	select REGMAP_MMIO
>>>> +	select IIO_BUFFER_DMAENGINE
>>>> +	help
>>>> +	  Select this option to support Audio PDM micophone for
>>>> +	  STMicroelectronics  STM32 digital filter for sigma delta converter.
>>>> +
>>>> +	  This driver can also be built as a module.  If so, the module
>>>> +	  will be called stm32-dfsdm-audio.
>>>> +
>>>>  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 161f271..79f975d 100644
>>>> --- a/drivers/iio/adc/Makefile
>>>> +++ b/drivers/iio/adc/Makefile
>>>> @@ -44,6 +44,7 @@ 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_STM32_DFSDM_AUDIO) += stm32-dfsdm-audio.o
>>>>  obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>>>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>>>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>>>> diff --git a/drivers/iio/adc/stm32-dfsdm-audio.c b/drivers/iio/adc/stm32-dfsdm-audio.c
>>>> new file mode 100644
>>>> index 0000000..115ef8f
>>>> --- /dev/null
>>>> +++ b/drivers/iio/adc/stm32-dfsdm-audio.c
>>>> @@ -0,0 +1,720 @@
>>>> +/*
>>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>>> + *
>>>> + * Copyright (C) 2017, 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/dmaengine.h>
>>>> +#include <linux/dma-mapping.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/regmap.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +#include <linux/iio/buffer.h>
>>>> +#include <linux/iio/hw_consumer.h>
>>>> +#include <linux/iio/sysfs.h>
>>>> +#include <linux/iio/trigger.h>
>>>> +#include <linux/iio/trigger_consumer.h>
>>>> +#include <linux/iio/triggered_buffer.h>
>>>> +
>>>> +#include "stm32-dfsdm.h"
>>>> +
>>>> +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
>>>> +
>>>> +struct stm32_dfsdm_audio {
>>>> +	struct stm32_dfsdm *dfsdm;
>>>> +	unsigned int fl_id;
>>>> +	unsigned int ch_id;
>>>> +	unsigned int spi_freq;  /* SPI bus clock frequency */
>>>> +	unsigned int sample_freq; /* Sample frequency after filter decimation */
>>>> +
>>>> +	u8 *rx_buf;
>>>> +	unsigned int bufi; /* Buffer current position */
>>>> +	unsigned int buf_sz; /* Buffer size */
>>>> +
>>>> +	struct dma_chan	*dma_chan;
>>>> +	dma_addr_t dma_buf;
>>>> +
>>>> +	int (*cb)(const void *data, size_t size, void *cb_priv);
>>>> +	void *cb_priv;
>>>> +};
>>>> +
>>>> +const char *stm32_dfsdm_spi_trigger = DFSDM_SPI_TRIGGER_NAME;
>>>> +
>>>> +static ssize_t dfsdm_audio_get_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				    const struct iio_chan_spec *chan, char *buf)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +
>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->sample_freq);
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_set_rate(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				    const struct iio_chan_spec *chan,
>>>> +				    const char *buf, size_t len)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>>> +	unsigned int spi_freq = pdmc->spi_freq;
>>>> +	unsigned int sample_freq;
>>>> +	int ret;
>>>> +
>>>> +	ret = kstrtoint(buf, 0, &sample_freq);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "Requested sample_freq :%d\n", sample_freq);
>>>> +	if (!sample_freq)
>>>> +		return -EINVAL;
>>>> +
>>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>>> +		spi_freq = pdmc->dfsdm->spi_master_freq;
>>>> +
>>>> +	if (spi_freq % sample_freq)
>>>> +		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
>>>> +			 spi_freq / (spi_freq / sample_freq));
>>>> +
>>>> +	ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>>> +	if (ret < 0) {
>>>> +		dev_err(&indio_dev->dev,
>>>> +			"Not able to find filter parameter that match!\n");
>>>> +		return ret;
>>>> +	}
>>>> +	pdmc->sample_freq = sample_freq;
>>>> +
>>>> +	return len;
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_get_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				      const struct iio_chan_spec *chan,
>>>> +				      char *buf)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +
>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", pdmc->spi_freq);
>>>> +}
>>>> +
>>>> +static ssize_t dfsdm_audio_set_spiclk(struct iio_dev *indio_dev, uintptr_t priv,
>>>> +				      const struct iio_chan_spec *chan,
>>>> +				      const char *buf, size_t len)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct stm32_dfsdm_filter *fl = &pdmc->dfsdm->fl_list[pdmc->fl_id];
>>>> +	struct stm32_dfsdm_channel *ch = &pdmc->dfsdm->ch_list[pdmc->ch_id];
>>>> +	unsigned int sample_freq = pdmc->sample_freq;
>>>> +	unsigned int spi_freq;
>>>> +	int ret;
>>>> +
>>>> +	/* If DFSDM is master on SPI, SPI freq can not be updated */
>>>> +	if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
>>>> +		return -EPERM;
>>>> +
>>>> +	ret = kstrtoint(buf, 0, &spi_freq);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "Requested frequency :%d\n", spi_freq);
>>>> +	if (!spi_freq)
>>>> +		return -EINVAL;
>>>> +
>>>> +	if (sample_freq) {
>>>> +		if (spi_freq % sample_freq)
>>>> +			dev_warn(&indio_dev->dev,
>>>> +				 "Sampling rate not accurate (%d)\n",
>>>> +				 spi_freq / (spi_freq / sample_freq));
>>>> +
>>>> +		ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
>>>> +		if (ret < 0) {
>>>> +			dev_err(&indio_dev->dev,
>>>> +				"No filter parameters that match!\n");
>>>> +			return ret;
>>>> +		}
>>>> +	}
>>>> +	pdmc->spi_freq = spi_freq;
>>>> +
>>>> +	return len;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Define external info for SPI Frequency and audio sampling rate that can be
>>>> + * configured by ASoC driver through consumer.h API
>>>> + */
>>>> +static const struct iio_chan_spec_ext_info dfsdm_adc_ext_info[] = {
>>>> +	/* filter oversampling: Post filter oversampling ratio */
>>>> +	{
>>>> +		.name = "audio_sampling_rate",
>>>> +		.shared = IIO_SHARED_BY_TYPE,
>>>> +		.read = dfsdm_audio_get_rate,
>>>> +		.write = dfsdm_audio_set_rate,
>>>> +	},
>>>> +	/* data_right_bit_shift : Filter output data shifting */
>>>> +	{
>>>> +		.name = "spi_clk_freq",
>>>> +		.shared = IIO_SHARED_BY_TYPE,
>>>> +		.read = dfsdm_audio_get_spiclk,
>>>> +		.write = dfsdm_audio_set_spiclk,
>>>> +	},
>>>> +	{},
>>>> +};
>>>> +
>>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_audio *pdmc, bool single)
>>>> +{
>>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>>> +	int ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_dfsdm(pdmc->dfsdm);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_channel(pdmc->dfsdm, pdmc->ch_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_dfsdm;
>>>> +
>>>> +	ret = stm32_dfsdm_filter_configure(pdmc->dfsdm, pdmc->fl_id,
>>>> +					   pdmc->ch_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_channels;
>>>> +
>>>> +	/* Enable DMA transfer*/
>>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>>> +				 DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN(1));
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	/* Enable conversion triggered by SPI clock*/
>>>> +	ret = regmap_update_bits(regmap, DFSDM_CR1(pdmc->fl_id),
>>>> +				 DFSDM_CR1_RCONT_MASK,  DFSDM_CR1_RCONT(1));
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_start_filter(pdmc->dfsdm, pdmc->fl_id);
>>>> +	if (ret < 0)
>>>> +		goto stop_channels;
>>>> +
>>>> +	return 0;
>>>> +
>>>> +stop_channels:
>>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->fl_id);
>>>> +stop_dfsdm:
>>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_audio *pdmc)
>>>> +{
>>>> +	stm32_dfsdm_stop_filter(pdmc->dfsdm, pdmc->fl_id);
>>>> +
>>>> +	stm32_dfsdm_stop_channel(pdmc->dfsdm, pdmc->ch_id);
>>>> +
>>>> +	stm32_dfsdm_stop_dfsdm(pdmc->dfsdm);
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
>>>> +				     unsigned int val)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
>>>> +
>>>> +	/*
>>>> +	 * DMA cyclic transfers are used, buffer is split into two periods.
>>>> +	 * There should be :
>>>> +	 * - always one buffer (period) DMA is working on
>>>> +	 * - one buffer (period) driver pushed to ASoC side ().
>>>> +	 */
>>>> +	watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
>>>> +	pdmc->buf_sz = watermark * 2;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
>>>> +				 struct iio_trigger *trig)
>>>> +{
>>>> +	if (!strcmp(stm32_dfsdm_spi_trigger, trig->name))
>>>> +		return 0;
>>>> +
>>>> +	return -EINVAL;
>>>> +}
>>>> +
>>>> +static const struct iio_info stm32_dfsdm_info_pdmc = {
>>>> +	.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
>>>> +	.driver_module = THIS_MODULE,
>>>> +	.validate_trigger = stm32_dfsdm_validate_trigger,
>>>> +};
>>>> +
>>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = arg;
>>>> +	struct iio_dev *indio_dev = iio_priv_to_dev(pdmc);
>>>> +	struct regmap *regmap = pdmc->dfsdm->regmap;
>>>> +	unsigned int status;
>>>> +
>>>> +	regmap_read(regmap, DFSDM_ISR(pdmc->fl_id), &status);
>>>> +
>>>> +	if (status & DFSDM_ISR_ROVRF_MASK) {
>>>> +		dev_err(&indio_dev->dev, "Unexpected Conversion overflow\n");
>>>> +		regmap_update_bits(regmap, DFSDM_ICR(pdmc->fl_id),
>>>> +				   DFSDM_ICR_CLRROVRF_MASK,
>>>> +				   DFSDM_ICR_CLRROVRF_MASK);
>>>> +	}
>>>> +
>>>> +	return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static unsigned int stm32_dfsdm_audio_avail_data(struct stm32_dfsdm_audio *pdmc)
>>>> +{
>>>> +	struct dma_tx_state state;
>>>> +	enum dma_status status;
>>>> +
>>>> +	status = dmaengine_tx_status(pdmc->dma_chan,
>>>> +				     pdmc->dma_chan->cookie,
>>>> +				     &state);
>>>> +	if (status == DMA_IN_PROGRESS) {
>>>> +		/* Residue is size in bytes from end of buffer */
>>>> +		unsigned int i = pdmc->buf_sz - state.residue;
>>>> +		unsigned int size;
>>>> +
>>>> +		/* Return available bytes */
>>>> +		if (i >= pdmc->bufi)
>>>> +			size = i - pdmc->bufi;
>>>> +		else
>>>> +			size = pdmc->buf_sz + i - pdmc->bufi;
>>>> +
>>>> +		return size;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static void stm32_dfsdm_audio_dma_buffer_done(void *data)
>>>> +{
>>>> +	struct iio_dev *indio_dev = data;
>>>> +
>>>> +	iio_trigger_poll_chained(indio_dev->trig);
>>> This shouldn't be going through the trigger infrastructure at all really.
>>> I'm not 100% sure what doing so is gaining you...
>> Yes i will clean trigger part and call directly the ASoC callback here.
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_dma_start(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct dma_async_tx_descriptor *desc;
>>>> +	dma_cookie_t cookie;
>>>> +	int ret;
>>>> +
>>>> +	if (!pdmc->dma_chan)
>>>> +		return -EINVAL;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
>>>> +		pdmc->buf_sz, pdmc->buf_sz / 2);
>>>> +
>>>> +	/* Prepare a DMA cyclic transaction */
>>>> +	desc = dmaengine_prep_dma_cyclic(pdmc->dma_chan,
>>>> +					 pdmc->dma_buf,
>>>> +					 pdmc->buf_sz, pdmc->buf_sz / 2,
>>>> +					 DMA_DEV_TO_MEM,
>>>> +					 DMA_PREP_INTERRUPT);
>>>> +	if (!desc)
>>>> +		return -EBUSY;
>>>> +
>>>> +	desc->callback = stm32_dfsdm_audio_dma_buffer_done;
>>>> +	desc->callback_param = indio_dev;
>>>> +
>>>> +	cookie = dmaengine_submit(desc);
>>>> +	ret = dma_submit_error(cookie);
>>>> +	if (ret) {
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	/* Issue pending DMA requests */
>>>> +	dma_async_issue_pending(pdmc->dma_chan);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>>> +	/* Reset pdmc buffer index */
>>>> +	pdmc->bufi = 0;
>>>> +
>>>> +	ret = stm32_dfsdm_start_conv(pdmc, false);
>>>> +	if (ret) {
>>>> +		dev_err(&indio_dev->dev, "Can't start conversion\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = stm32_dfsdm_audio_dma_start(indio_dev);
>>>> +	if (ret) {
>>>> +		dev_err(&indio_dev->dev, "Can't start DMA\n");
>>>> +		goto err_stop_conv;
>>>> +	}
>>>> +
>>>> +	ret = iio_triggered_buffer_postenable(indio_dev);
>>>> +	if (ret < 0) {
>>>> +		dev_err(&indio_dev->dev, "%s :%d\n", __func__, __LINE__);
>>>> +		goto err_stop_dma;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_stop_dma:
>>>> +	if (pdmc->dma_chan)
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +err_stop_conv:
>>>> +	stm32_dfsdm_stop_conv(pdmc);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>>> +	ret = iio_triggered_buffer_predisable(indio_dev);
>>>> +	if (ret < 0)
>>>> +		dev_err(&indio_dev->dev, "Predisable failed\n");
>>>> +
>>>> +	if (pdmc->dma_chan)
>>>> +		dmaengine_terminate_all(pdmc->dma_chan);
>>>> +
>>>> +	stm32_dfsdm_stop_conv(pdmc);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>>> +	.postenable = &stm32_dfsdm_postenable,
>>>> +	.predisable = &stm32_dfsdm_predisable,
>>>> +};
>>>> +
>>>> +static irqreturn_t stm32_dfsdm_audio_trigger_handler(int irq, void *p)
>>>> +{
>>> OK. So now I kind of understand what the trigger provided in the adc driver
>>> was about.  hmm.  Triggers are supposed to be per sample so this is indeed
>>> a hack.  We need fullblown DMA buffer consumers to do this right.
>> Yes exactly.
>>>
>>> Probably best person to comment on that is Lars. 
>>>> +	struct iio_poll_func *pf = p;
>>>> +	struct iio_dev *indio_dev = pf->indio_dev;
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	size_t old_pos;
>>>> +	int available = stm32_dfsdm_audio_avail_data(pdmc);
>>>> +
>>>> +	/*
>>>> +	 * Buffer interface is not support cyclic DMA buffer,and offer only
>>>> +	 * an interface to push data samples per samples.
>>>> +	 * For this reason iio_push_to_buffers_with_timestamp in not used
>>>> +	 * and interface is hacked using a private callback registered by ASoC.
>>>> +	 * This should be a temporary solution waiting a cyclic DMA engine
>>>> +	 * support in IIO.
>>>> +	 */
>>>> +
>>>> +	dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__,
>>>> +		pdmc->bufi, available);
>>>> +	old_pos = pdmc->bufi;
>>>> +	while (available >= indio_dev->scan_bytes) {
>>>> +		u32 *buffer = (u32 *)&pdmc->rx_buf[pdmc->bufi];
>>>> +
>>>> +		/* Mask 8 LSB that contains the channel ID */
>>>> +		*buffer &= 0xFFFFFF00;
>>>> +		available -= indio_dev->scan_bytes;
>>>> +		pdmc->bufi += indio_dev->scan_bytes;
>>>> +		if (pdmc->bufi >= pdmc->buf_sz) {
>>>> +			if (pdmc->cb)
>>>> +				pdmc->cb(&pdmc->rx_buf[old_pos],
>>>> +					 pdmc->buf_sz - old_pos, pdmc->cb_priv);
>>>> +			pdmc->bufi = 0;
>>>> +			old_pos = 0;
>>>> +		}
>>>> +	}
>>>> +	if (pdmc->cb)
>>>> +		pdmc->cb(&pdmc->rx_buf[old_pos], pdmc->bufi - old_pos,
>>>> +				pdmc->cb_priv);
>>>> +
>>>> +	iio_trigger_notify_done(indio_dev->trig);
>>>> +
>>>> +	return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb - register a callback
>>>> + *	that will be called when DMA transfer period is achieved.
>>>> + *
>>>> + * @iio_dev: Handle to IIO device.
>>>> + * @cb: pointer to callback function.
>>>> + *	@data: pointer to data buffer
>>>> + *	@size: size in byte of the data buffer
>>>> + *	@private: pointer to consumer private structure
>>>> + * @private: pointer to consumer private structure
>>>> + */
>>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>>> +			    int (*cb)(const void *data, size_t size,
>>>> +				      void *private),
>>>> +			    void *private)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +
>>>> +	if (!iio_dev)
>>>> +		return -EINVAL;
>>>> +	pdmc = iio_priv(iio_dev);
>>>> +
>>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>>> +		return -EINVAL;
>>>> +
>>>> +	pdmc->cb = cb;
>>>> +	pdmc->cb_priv = private;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_release_buff_cb - unregister buffer callback
>>>> + *
>>>> + * @iio_dev: Handle to IIO device.
>>>> + */
>>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +
>>>> +	if (!iio_dev)
>>>> +		return -EINVAL;
>>>> +	pdmc = iio_priv(iio_dev);
>>>> +
>>>> +	if (iio_dev !=  iio_priv_to_dev(pdmc))
>>>> +		return -EINVAL;
>>>> +	pdmc->cb = NULL;
>>>> +	pdmc->cb_priv = NULL;
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
>>>> +
>>>> +static int stm32_dfsdm_audio_chan_init(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct iio_chan_spec *ch;
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	int ret;
>>>> +
>>>> +	ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
>>>> +	if (!ch)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	ret = stm32_dfsdm_channel_parse_of(pdmc->dfsdm, indio_dev, ch, 0);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ch->type = IIO_VOLTAGE;
>>>> +	ch->indexed = 1;
>>>> +	ch->scan_index = 0;
>>>> +	ch->ext_info = dfsdm_adc_ext_info;
>>>> +
>>>> +	ch->scan_type.sign = 's';
>>>> +	ch->scan_type.realbits = 24;
>>>> +	ch->scan_type.storagebits = 32;
>>>> +
>>>> +	pdmc->ch_id = ch->channel;
>>>> +	ret = stm32_dfsdm_chan_configure(pdmc->dfsdm,
>>>> +					 &pdmc->dfsdm->ch_list[ch->channel]);
>>>> +
>>>> +	indio_dev->num_channels = 1;
>>>> +	indio_dev->channels = ch;
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static const struct of_device_id stm32_dfsdm_audio_match[] = {
>>>> +	{ .compatible = "st,stm32-dfsdm-audio"},
>>>> +	{}
>>>> +};
>>>> +
>>>> +static int stm32_dfsdm_audio_dma_request(struct iio_dev *indio_dev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = iio_priv(indio_dev);
>>>> +	struct dma_slave_config config;
>>>> +	int ret;
>>>> +
>>>> +	pdmc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
>>>> +	if (!pdmc->dma_chan)
>>>> +		return -EINVAL;
>>>> +
>>>> +	pdmc->rx_buf = dma_alloc_coherent(pdmc->dma_chan->device->dev,
>>>> +					 DFSDM_DMA_BUFFER_SIZE,
>>>> +					 &pdmc->dma_buf, GFP_KERNEL);
>>>> +	if (!pdmc->rx_buf) {
>>>> +		ret = -ENOMEM;
>>>> +		goto err_release;
>>>> +	}
>>>> +
>>>> +	/* Configure DMA channel to read data register */
>>>> +	memset(&config, 0, sizeof(config));
>>>> +	config.src_addr = (dma_addr_t)pdmc->dfsdm->phys_base;
>>>> +	config.src_addr += DFSDM_RDATAR(pdmc->fl_id);
>>>> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>>>> +
>>>> +	ret = dmaengine_slave_config(pdmc->dma_chan, &config);
>>>> +	if (ret)
>>>> +		goto err_free;
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_free:
>>>> +	dma_free_coherent(pdmc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
>>>> +			  pdmc->rx_buf, pdmc->dma_buf);
>>>> +err_release:
>>>> +	dma_release_channel(pdmc->dma_chan);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_probe(struct platform_device *pdev)
>>>> +{
>>>> +	struct device *dev = &pdev->dev;
>>>> +	struct stm32_dfsdm_audio *pdmc;
>>>> +	struct device_node *np = dev->of_node;
>>>> +	struct iio_dev *iio;
>>>> +	char *name;
>>>> +	int ret, irq, val;
>>>> +
>>>> +	iio = devm_iio_device_alloc(dev, sizeof(*pdmc));
>>>> +	if (IS_ERR(iio)) {
>>>> +		dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>>> +		return PTR_ERR(iio);
>>>> +	}
>>>> +
>>>> +	pdmc = iio_priv(iio);
>>>> +	if (IS_ERR(pdmc)) {
>>>> +		dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>>> +		return PTR_ERR(pdmc);
>>>> +	}
>>>> +	pdmc->dfsdm = dev_get_drvdata(dev->parent);
>>>> +
>>>> +	iio->name = np->name;
>>>> +	iio->dev.parent = dev;
>>>> +	iio->dev.of_node = np;
>>>> +	iio->info = &stm32_dfsdm_info_pdmc;
>>>> +	iio->modes = INDIO_DIRECT_MODE;
>>>> +
>>>> +	platform_set_drvdata(pdev, pdmc);
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "reg", &pdmc->fl_id);
>>>> +	if (ret != 0) {
>>>> +		dev_err(dev, "Missing reg property\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	name = kzalloc(sizeof("dfsdm-pdm0"), GFP_KERNEL);
>>>> +	if (!name)
>>>> +		return -ENOMEM;
>>>> +	snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", pdmc->fl_id);
>>>> +	iio->name = name;
>>>> +
>>>> +	/*
>>>> +	 * In a first step IRQs generated for channels are not treated.
>>>> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>>> +	 */
>>>> +	irq = platform_get_irq(pdev, 0);
>>>> +	ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>>> +			       0, pdev->name, pdmc);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "Failed to request IRQ\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>>> +	if (ret < 0) {
>>>> +		dev_err(dev, "Failed to set filter order\n");
>>>> +		return ret;
>>>> +	}
>>>> +	pdmc->dfsdm->fl_list[pdmc->fl_id].ford = val;
>>>> +
>>>> +	ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>>> +	if (!ret)
>>>> +		pdmc->dfsdm->fl_list[pdmc->fl_id].sync_mode = val;
>>>> +
>>>> +	ret = stm32_dfsdm_audio_chan_init(iio);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	ret = stm32_dfsdm_audio_dma_request(iio);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "DMA request failed\n");
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	iio->modes |= INDIO_BUFFER_SOFTWARE;
>>>> +
>>>> +	ret = iio_triggered_buffer_setup(iio,
>>>> +					 &iio_pollfunc_store_time,
>>>> +					 &stm32_dfsdm_audio_trigger_handler,
>>>> +					 &stm32_dfsdm_buffer_setup_ops);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "Buffer setup failed\n");
>>>> +		goto err_dma_disable;
>>>> +	}
>>>> +
>>>> +	ret = iio_device_register(iio);
>>>> +	if (ret) {
>>>> +		dev_err(&pdev->dev, "IIO dev register failed\n");
>>>> +		goto err_buffer_cleanup;
>>>> +	}
>>>> +
>>>> +	return 0;
>>>> +
>>>> +err_buffer_cleanup:
>>>> +	iio_triggered_buffer_cleanup(iio);
>>>> +
>>>> +err_dma_disable:
>>>> +	if (pdmc->dma_chan) {
>>>> +		dma_free_coherent(pdmc->dma_chan->device->dev,
>>>> +				  DFSDM_DMA_BUFFER_SIZE,
>>>> +				  pdmc->rx_buf, pdmc->dma_buf);
>>>> +		dma_release_channel(pdmc->dma_chan);
>>>> +	}
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>> +static int stm32_dfsdm_audio_remove(struct platform_device *pdev)
>>>> +{
>>>> +	struct stm32_dfsdm_audio *pdmc = platform_get_drvdata(pdev);
>>>> +	struct iio_dev *iio = iio_priv_to_dev(pdmc);
>>>> +
>>>> +	iio_device_unregister(iio);
>>> Same as in the other driver. Can just use the devm_iio_device_register
>>> version and drop remove.
>> oops missing the previous comment... sorry.
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static struct platform_driver stm32_dfsdm_audio_driver = {
>>>> +	.driver = {
>>>> +		.name = "stm32-dfsdm-audio",
>>>> +		.of_match_table = stm32_dfsdm_audio_match,
>>>> +	},
>>>> +	.probe = stm32_dfsdm_audio_probe,
>>>> +	.remove = stm32_dfsdm_audio_remove,
>>>> +};
>>>> +module_platform_driver(stm32_dfsdm_audio_driver);
>>>> +
>>>> +MODULE_DESCRIPTION("STM32 sigma delta converter for PDM microphone");
>>>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/include/linux/iio/adc/stm32-dfsdm-audio.h b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>>> new file mode 100644
>>>> index 0000000..9b41b28
>>>> --- /dev/null
>>>> +++ b/include/linux/iio/adc/stm32-dfsdm-audio.h
>>>> @@ -0,0 +1,41 @@
>>>> +/*
>>>> + * This file discribe the STM32 DFSDM IIO driver API for audio part
>>>> + *
>>>> + * 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 STM32_DFSDM_AUDIO_H
>>>> +#define STM32_DFSDM_AUDIO_H
>>>> +
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb() - register callback for capture buffer period.
>>>> + * @dev:	Pointer to client device.
>>>> + * @indio_dev:	Device on which the channel exists.
>>>> + * @cb:		Callback function.
>>>> + *		@data:  pointer to data buffer
>>>> + *		@size: size of the data buffer in bytes
>>>> + * @private:	Private data passed to callback.
>>>> + *
>>>> + */
>>>> +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
>>>> +			    int (*cb)(const void *data, size_t size,
>>>> +				      void *private),
>>>> +			    void *private);
>>>> +/**
>>>> + * stm32_dfsdm_get_buff_cb() - release callback for capture buffer period.
>>>> + * @indio_dev:	Device on which the channel exists.
>>>> + */
>>>> +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev);
>>>> +
>>>> +#endif
>>>>
>>>
>> --
>> 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] 109+ messages in thread

* Re: [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
  2017-03-24 14:52     ` Rob Herring
  (?)
@ 2017-03-29 12:42       ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-29 12:42 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Olivier MOYSAN, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Mark Brown, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre TORGUE

Hello Rob,

Sorry for this dirty/crappy patch...
Please find answers below

Thanks

Arnaud


On 03/24/2017 03:52 PM, Rob Herring wrote:
> On Fri, Mar 17, 2017 at 03:08:23PM +0100, Arnaud Pouliquen wrote:
>> Add bindings that describes audio settings to support
>> Digital Filter for pulse density modulation(PDM) microphone.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>> V2->V3:
>>    Fixes based on V2 comments
>>
>>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
>>  1 file changed, 41 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>> new file mode 100644
>> index 0000000..ab610bc
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>> @@ -0,0 +1,40 @@
>> +STMicroelectronics audio DFSDM DT bindings
>> +
>> +This driver supports audio PDM microphone capture through Digital Filter format
>> +Sigma Delta modulators (DFSDM).
>> +
>> +Required properties:
>> +  - compatible: "st,stm32h7-adfsdm".
>> +
>> +  - #sound-dai-cells : Must be equal to 0
>> +
>> +  - io-channels : phandle to iio dfsdm instance node.
>> +
>> +
>> +Example of a simple sound card using audio DFSDM node.
>> +
>> +	dmic0: dmic_@0 {
> 
> Drop the '_' and unit address.
> 
>> +		compatible = "dmic-codec";
>> +		#sound-dai-cells = <0>;
>> +	};
>> +
>> +	asoc-pdm@0 {
> 
> asoc is a Linux term. Drop the unit address.
> 
>> +		compatible = "st,stm32h7-adfsdm";
> 
> Is this a separate block from the ADC? A drawing of the h/w blocks and 
> connections would help.
I don't know how to explain it...So don't hesitate is still not clear.
On DFSDM we can connect 2 ADC types:
- Generic Sigma delta(SD) ADC for analog to digital conversion
- Digital microphones for audio capture.

In both cases, an SPI or Manchester bus is used to transfer 1-bit stream
 to DFSDM.
1) In case of SD ADC, The link is described in IIO declaration using
"io-channels" property ( [04/11] IIO: add DT bindings for stm32 DFSDM
filter)
2) For audio purpose, the link is done by the simple or the graph card
So this device exposes the Digital audio interface (DAI) associated to
the DFSDM ADC.
Link between the DFSDM DAI and the DFSDM IIO device is done through the
"io-channels" property.

> 
>> +		#sound-dai-cells = <0>;
>> +		io-channels = <&dfsdm_adc0 0>;
>> +	};
>> +
>> +	sound_dfsdm_pdm {
> 
> sound-card {
> 
>> + 		compatible = "simple-audio-card";
>> + 		simple-audio-card,name = "dfsdm_pdm";
>> +
>> + 		dfsdm0_mic0: simple-audio-card,dai-link@0 {
> 
> I'd suggest moving to the graph card.
yes should be in V4
> 
>> + 			format = "pdm";
>> + 			cpu {
>> + 				sound-dai = <&asoc_pdm1>;
> 
> This phandle doesn't point to anything.
> 
>> + 			};
>> + 			dmic0_codec: codec {
>> + 				sound-dai = <&dmic0>;
>> + 			};
>> + 		};
>> +	};
>> \ No newline at end of file
> 
> ^^^
> 
>> -- 
>> 1.9.1
>>

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

* Re: [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
@ 2017-03-29 12:42       ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-29 12:42 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, devicetree,
	linux-arm-kernel, linux-iio, alsa-devel, kernel, Maxime Coquelin,
	Alexandre TORGUE, Olivier MOYSAN

Hello Rob,

Sorry for this dirty/crappy patch...
Please find answers below

Thanks

Arnaud


On 03/24/2017 03:52 PM, Rob Herring wrote:
> On Fri, Mar 17, 2017 at 03:08:23PM +0100, Arnaud Pouliquen wrote:
>> Add bindings that describes audio settings to support
>> Digital Filter for pulse density modulation(PDM) microphone.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>> V2->V3:
>>    Fixes based on V2 comments
>>
>>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
>>  1 file changed, 41 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>> new file mode 100644
>> index 0000000..ab610bc
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>> @@ -0,0 +1,40 @@
>> +STMicroelectronics audio DFSDM DT bindings
>> +
>> +This driver supports audio PDM microphone capture through Digital Filter format
>> +Sigma Delta modulators (DFSDM).
>> +
>> +Required properties:
>> +  - compatible: "st,stm32h7-adfsdm".
>> +
>> +  - #sound-dai-cells : Must be equal to 0
>> +
>> +  - io-channels : phandle to iio dfsdm instance node.
>> +
>> +
>> +Example of a simple sound card using audio DFSDM node.
>> +
>> +	dmic0: dmic_@0 {
> 
> Drop the '_' and unit address.
> 
>> +		compatible = "dmic-codec";
>> +		#sound-dai-cells = <0>;
>> +	};
>> +
>> +	asoc-pdm@0 {
> 
> asoc is a Linux term. Drop the unit address.
> 
>> +		compatible = "st,stm32h7-adfsdm";
> 
> Is this a separate block from the ADC? A drawing of the h/w blocks and 
> connections would help.
I don't know how to explain it...So don't hesitate is still not clear.
On DFSDM we can connect 2 ADC types:
- Generic Sigma delta(SD) ADC for analog to digital conversion
- Digital microphones for audio capture.

In both cases, an SPI or Manchester bus is used to transfer 1-bit stream
 to DFSDM.
1) In case of SD ADC, The link is described in IIO declaration using
"io-channels" property ( [04/11] IIO: add DT bindings for stm32 DFSDM
filter)
2) For audio purpose, the link is done by the simple or the graph card
So this device exposes the Digital audio interface (DAI) associated to
the DFSDM ADC.
Link between the DFSDM DAI and the DFSDM IIO device is done through the
"io-channels" property.

> 
>> +		#sound-dai-cells = <0>;
>> +		io-channels = <&dfsdm_adc0 0>;
>> +	};
>> +
>> +	sound_dfsdm_pdm {
> 
> sound-card {
> 
>> + 		compatible = "simple-audio-card";
>> + 		simple-audio-card,name = "dfsdm_pdm";
>> +
>> + 		dfsdm0_mic0: simple-audio-card,dai-link@0 {
> 
> I'd suggest moving to the graph card.
yes should be in V4
> 
>> + 			format = "pdm";
>> + 			cpu {
>> + 				sound-dai = <&asoc_pdm1>;
> 
> This phandle doesn't point to anything.
> 
>> + 			};
>> + 			dmic0_codec: codec {
>> + 				sound-dai = <&dmic0>;
>> + 			};
>> + 		};
>> +	};
>> \ No newline at end of file
> 
> ^^^
> 
>> -- 
>> 1.9.1
>>

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

* [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter
@ 2017-03-29 12:42       ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-03-29 12:42 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Rob,

Sorry for this dirty/crappy patch...
Please find answers below

Thanks

Arnaud


On 03/24/2017 03:52 PM, Rob Herring wrote:
> On Fri, Mar 17, 2017 at 03:08:23PM +0100, Arnaud Pouliquen wrote:
>> Add bindings that describes audio settings to support
>> Digital Filter for pulse density modulation(PDM) microphone.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>> V2->V3:
>>    Fixes based on V2 comments
>>
>>  .../devicetree/bindings/sound/st,stm32-adfsdm.txt  | 41 ++++++++++++++++++++++
>>  1 file changed, 41 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>> new file mode 100644
>> index 0000000..ab610bc
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/st,stm32-adfsdm.txt
>> @@ -0,0 +1,40 @@
>> +STMicroelectronics audio DFSDM DT bindings
>> +
>> +This driver supports audio PDM microphone capture through Digital Filter format
>> +Sigma Delta modulators (DFSDM).
>> +
>> +Required properties:
>> +  - compatible: "st,stm32h7-adfsdm".
>> +
>> +  - #sound-dai-cells : Must be equal to 0
>> +
>> +  - io-channels : phandle to iio dfsdm instance node.
>> +
>> +
>> +Example of a simple sound card using audio DFSDM node.
>> +
>> +	dmic0: dmic_ at 0 {
> 
> Drop the '_' and unit address.
> 
>> +		compatible = "dmic-codec";
>> +		#sound-dai-cells = <0>;
>> +	};
>> +
>> +	asoc-pdm at 0 {
> 
> asoc is a Linux term. Drop the unit address.
> 
>> +		compatible = "st,stm32h7-adfsdm";
> 
> Is this a separate block from the ADC? A drawing of the h/w blocks and 
> connections would help.
I don't know how to explain it...So don't hesitate is still not clear.
On DFSDM we can connect 2 ADC types:
- Generic Sigma delta(SD) ADC for analog to digital conversion
- Digital microphones for audio capture.

In both cases, an SPI or Manchester bus is used to transfer 1-bit stream
 to DFSDM.
1) In case of SD ADC, The link is described in IIO declaration using
"io-channels" property ( [04/11] IIO: add DT bindings for stm32 DFSDM
filter)
2) For audio purpose, the link is done by the simple or the graph card
So this device exposes the Digital audio interface (DAI) associated to
the DFSDM ADC.
Link between the DFSDM DAI and the DFSDM IIO device is done through the
"io-channels" property.

> 
>> +		#sound-dai-cells = <0>;
>> +		io-channels = <&dfsdm_adc0 0>;
>> +	};
>> +
>> +	sound_dfsdm_pdm {
> 
> sound-card {
> 
>> + 		compatible = "simple-audio-card";
>> + 		simple-audio-card,name = "dfsdm_pdm";
>> +
>> + 		dfsdm0_mic0: simple-audio-card,dai-link at 0 {
> 
> I'd suggest moving to the graph card.
yes should be in V4
> 
>> + 			format = "pdm";
>> + 			cpu {
>> + 				sound-dai = <&asoc_pdm1>;
> 
> This phandle doesn't point to anything.
> 
>> + 			};
>> + 			dmic0_codec: codec {
>> + 				sound-dai = <&dmic0>;
>> + 			};
>> + 		};
>> +	};
>> \ No newline at end of file
> 
> ^^^
> 
>> -- 
>> 1.9.1
>>

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

* Re: [PATCH v3 01/11] iio: Add hardware consumer support
  2017-03-17 14:08   ` Arnaud Pouliquen
  (?)
@ 2017-06-06 10:15     ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-06-06 10:15 UTC (permalink / raw)
  To: Lars-Peter Clausen, Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Olivier MOYSAN,
	Maxime Coquelin, Liam Girdwood, linux-iio, Takashi Iwai,
	Rob Herring, linux-arm-kernel, Peter Meerwald-Stadler,
	Hartmut Knaack, Jonathan Cameron, Alexandre TORGUE

Hello Lars,

Any news on the upstream of your HW consumer interface, prerequisite for
my STM32 DFSDM upstream?
Don't hesitate if i can help...

Regards
Arnaud


On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
> From: Lars-Peter Clausen <lars@metafoo.de>
> 
> Hardware consumer's can be used when one IIO device has a direct connection
> to another device in hardware.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2 -> V3:
> 	 Just Adding sign off.  No update but Jonathan comment.
>          Need to be taken into account (in agreement with Lars)
>          when transforming rfc in patch.
> ---
>  drivers/iio/Kconfig             |   6 ++
>  drivers/iio/Makefile            |   1 +
>  drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/iio/hw_consumer.h |  12 ++++
>  4 files changed, 175 insertions(+)
>  create mode 100644 drivers/iio/hw_consumer.c
>  create mode 100644 include/linux/iio/hw_consumer.h
> 
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 6743b18..956dd18 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -30,6 +30,12 @@ config IIO_CONFIGFS
>  	  (e.g. software triggers). For more info see
>  	  Documentation/iio/iio_configfs.txt.
>  
> +config IIO_HW_CONSUMER
> +	tristate
> +	help
> +	  Hardware consumer buffer
> +
> +
>  config IIO_TRIGGER
>  	bool "Enable triggered sampling support"
>  	help
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 87e4c43..8472b97 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
> +obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
>  
>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>  obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
> diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
> new file mode 100644
> index 0000000..66f0732
> --- /dev/null
> +++ b/drivers/iio/hw_consumer.c
> @@ -0,0 +1,156 @@
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +
> +#include <linux/iio/iio.h>
> +#include "iio_core.h"
> +#include <linux/iio/machine.h>
> +#include <linux/iio/driver.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +struct iio_hw_consumer {
> +	struct list_head buffers;
> +	struct iio_channel *channels;
> +};
> +
> +struct hw_consumer_buffer {
> +	struct list_head head;
> +	struct iio_dev *indio_dev;
> +	struct iio_buffer buffer;
> +};
> +
> +static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
> +	struct iio_buffer *buffer)
> +{
> +	return container_of(buffer, struct hw_consumer_buffer, buffer);
> +}
> +
> +static void iio_hw_buf_release(struct iio_buffer *buffer)
> +{
> +	struct hw_consumer_buffer *hw_buf =
> +		iio_buffer_to_hw_consumer_buffer(buffer);
> +	kfree(hw_buf->buffer.scan_mask);
> +	kfree(hw_buf);
> +}
> +
> +static const struct iio_buffer_access_funcs iio_hw_buf_access = {
> +	.release = &iio_hw_buf_release,
> +	.modes = INDIO_BUFFER_HARDWARE,
> +};
> +
> +static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
> +	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head) {
> +		if (buf->indio_dev == indio_dev)
> +			return buf;
> +	}
> +
> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
> +	if (!buf)
> +		return NULL;
> +
> +	buf->buffer.access = &iio_hw_buf_access;
> +	buf->indio_dev = indio_dev;
> +	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
> +		sizeof(long), GFP_KERNEL);
> +	if (!buf->buffer.scan_mask)
> +		goto err_free_buf;
> +
> +	iio_buffer_init(&buf->buffer);
> +	list_add_tail(&buf->head, &hwc->buffers);
> +
> +	return buf;
> +
> +err_free_buf:
> +	kfree(buf);
> +	return NULL;
> +}
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
> +{
> +	struct hw_consumer_buffer *buf;
> +	struct iio_hw_consumer *hwc;
> +	struct iio_channel *chan;
> +	int ret;
> +
> +	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
> +	if (!hwc)
> +		return ERR_PTR(-ENOMEM);
> +
> +	INIT_LIST_HEAD(&hwc->buffers);
> +
> +	hwc->channels = iio_channel_get_all(dev);
> +	if (IS_ERR(hwc->channels)) {
> +		ret = PTR_ERR(hwc->channels);
> +		goto err_free_hwc;
> +	}
> +
> +	chan = &hwc->channels[0];
> +	while (chan->indio_dev) {
> +		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
> +		if (buf == NULL) {
> +			ret = -ENOMEM;
> +			goto err_put_buffers;
> +		}
> +		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
> +		chan++;
> +	}
> +
> +	return hwc;
> +
> +err_put_buffers:
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_buffer_put(&buf->buffer);
> +	iio_channel_release_all(hwc->channels);
> +err_free_hwc:
> +	kfree(hwc);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
> +
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	iio_channel_release_all(hwc->channels);
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_buffer_put(&buf->buffer);
> +	kfree(hwc);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
> +
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +	int ret;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head) {
> +		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
> +		if (ret)
> +			goto err_disable_buffers;
> +	}
> +
> +	return 0;
> +
> +err_disable_buffers:
> +	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
> +
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
> diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
> new file mode 100644
> index 0000000..f12653d
> --- /dev/null
> +++ b/include/linux/iio/hw_consumer.h
> @@ -0,0 +1,12 @@
> +#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
> +#define LINUX_IIO_HW_CONSUMER_BUFFER_H
> +
> +struct device;
> +struct iio_hw_consumer;
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
> +
> +#endif
> 

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

* Re: [PATCH v3 01/11] iio: Add hardware consumer support
@ 2017-06-06 10:15     ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-06-06 10:15 UTC (permalink / raw)
  To: Lars-Peter Clausen, Mark Brown
  Cc: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

Hello Lars,

Any news on the upstream of your HW consumer interface, prerequisite for
my STM32 DFSDM upstream?
Don't hesitate if i can help...

Regards
Arnaud


On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
> From: Lars-Peter Clausen <lars@metafoo.de>
> 
> Hardware consumer's can be used when one IIO device has a direct connection
> to another device in hardware.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2 -> V3:
> 	 Just Adding sign off.  No update but Jonathan comment.
>          Need to be taken into account (in agreement with Lars)
>          when transforming rfc in patch.
> ---
>  drivers/iio/Kconfig             |   6 ++
>  drivers/iio/Makefile            |   1 +
>  drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/iio/hw_consumer.h |  12 ++++
>  4 files changed, 175 insertions(+)
>  create mode 100644 drivers/iio/hw_consumer.c
>  create mode 100644 include/linux/iio/hw_consumer.h
> 
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 6743b18..956dd18 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -30,6 +30,12 @@ config IIO_CONFIGFS
>  	  (e.g. software triggers). For more info see
>  	  Documentation/iio/iio_configfs.txt.
>  
> +config IIO_HW_CONSUMER
> +	tristate
> +	help
> +	  Hardware consumer buffer
> +
> +
>  config IIO_TRIGGER
>  	bool "Enable triggered sampling support"
>  	help
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 87e4c43..8472b97 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
> +obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
>  
>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>  obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
> diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
> new file mode 100644
> index 0000000..66f0732
> --- /dev/null
> +++ b/drivers/iio/hw_consumer.c
> @@ -0,0 +1,156 @@
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +
> +#include <linux/iio/iio.h>
> +#include "iio_core.h"
> +#include <linux/iio/machine.h>
> +#include <linux/iio/driver.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +struct iio_hw_consumer {
> +	struct list_head buffers;
> +	struct iio_channel *channels;
> +};
> +
> +struct hw_consumer_buffer {
> +	struct list_head head;
> +	struct iio_dev *indio_dev;
> +	struct iio_buffer buffer;
> +};
> +
> +static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
> +	struct iio_buffer *buffer)
> +{
> +	return container_of(buffer, struct hw_consumer_buffer, buffer);
> +}
> +
> +static void iio_hw_buf_release(struct iio_buffer *buffer)
> +{
> +	struct hw_consumer_buffer *hw_buf =
> +		iio_buffer_to_hw_consumer_buffer(buffer);
> +	kfree(hw_buf->buffer.scan_mask);
> +	kfree(hw_buf);
> +}
> +
> +static const struct iio_buffer_access_funcs iio_hw_buf_access = {
> +	.release = &iio_hw_buf_release,
> +	.modes = INDIO_BUFFER_HARDWARE,
> +};
> +
> +static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
> +	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head) {
> +		if (buf->indio_dev == indio_dev)
> +			return buf;
> +	}
> +
> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
> +	if (!buf)
> +		return NULL;
> +
> +	buf->buffer.access = &iio_hw_buf_access;
> +	buf->indio_dev = indio_dev;
> +	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
> +		sizeof(long), GFP_KERNEL);
> +	if (!buf->buffer.scan_mask)
> +		goto err_free_buf;
> +
> +	iio_buffer_init(&buf->buffer);
> +	list_add_tail(&buf->head, &hwc->buffers);
> +
> +	return buf;
> +
> +err_free_buf:
> +	kfree(buf);
> +	return NULL;
> +}
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
> +{
> +	struct hw_consumer_buffer *buf;
> +	struct iio_hw_consumer *hwc;
> +	struct iio_channel *chan;
> +	int ret;
> +
> +	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
> +	if (!hwc)
> +		return ERR_PTR(-ENOMEM);
> +
> +	INIT_LIST_HEAD(&hwc->buffers);
> +
> +	hwc->channels = iio_channel_get_all(dev);
> +	if (IS_ERR(hwc->channels)) {
> +		ret = PTR_ERR(hwc->channels);
> +		goto err_free_hwc;
> +	}
> +
> +	chan = &hwc->channels[0];
> +	while (chan->indio_dev) {
> +		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
> +		if (buf == NULL) {
> +			ret = -ENOMEM;
> +			goto err_put_buffers;
> +		}
> +		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
> +		chan++;
> +	}
> +
> +	return hwc;
> +
> +err_put_buffers:
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_buffer_put(&buf->buffer);
> +	iio_channel_release_all(hwc->channels);
> +err_free_hwc:
> +	kfree(hwc);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
> +
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	iio_channel_release_all(hwc->channels);
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_buffer_put(&buf->buffer);
> +	kfree(hwc);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
> +
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +	int ret;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head) {
> +		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
> +		if (ret)
> +			goto err_disable_buffers;
> +	}
> +
> +	return 0;
> +
> +err_disable_buffers:
> +	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
> +
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
> diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
> new file mode 100644
> index 0000000..f12653d
> --- /dev/null
> +++ b/include/linux/iio/hw_consumer.h
> @@ -0,0 +1,12 @@
> +#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
> +#define LINUX_IIO_HW_CONSUMER_BUFFER_H
> +
> +struct device;
> +struct iio_hw_consumer;
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
> +
> +#endif
> 

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

* [PATCH v3 01/11] iio: Add hardware consumer support
@ 2017-06-06 10:15     ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-06-06 10:15 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Lars,

Any news on the upstream of your HW consumer interface, prerequisite for
my STM32 DFSDM upstream?
Don't hesitate if i can help...

Regards
Arnaud


On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
> From: Lars-Peter Clausen <lars@metafoo.de>
> 
> Hardware consumer's can be used when one IIO device has a direct connection
> to another device in hardware.
> 
> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
> V2 -> V3:
> 	 Just Adding sign off.  No update but Jonathan comment.
>          Need to be taken into account (in agreement with Lars)
>          when transforming rfc in patch.
> ---
>  drivers/iio/Kconfig             |   6 ++
>  drivers/iio/Makefile            |   1 +
>  drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/iio/hw_consumer.h |  12 ++++
>  4 files changed, 175 insertions(+)
>  create mode 100644 drivers/iio/hw_consumer.c
>  create mode 100644 include/linux/iio/hw_consumer.h
> 
> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
> index 6743b18..956dd18 100644
> --- a/drivers/iio/Kconfig
> +++ b/drivers/iio/Kconfig
> @@ -30,6 +30,12 @@ config IIO_CONFIGFS
>  	  (e.g. software triggers). For more info see
>  	  Documentation/iio/iio_configfs.txt.
>  
> +config IIO_HW_CONSUMER
> +	tristate
> +	help
> +	  Hardware consumer buffer
> +
> +
>  config IIO_TRIGGER
>  	bool "Enable triggered sampling support"
>  	help
> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
> index 87e4c43..8472b97 100644
> --- a/drivers/iio/Makefile
> +++ b/drivers/iio/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
> +obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
>  
>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>  obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
> diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
> new file mode 100644
> index 0000000..66f0732
> --- /dev/null
> +++ b/drivers/iio/hw_consumer.c
> @@ -0,0 +1,156 @@
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/slab.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +
> +#include <linux/iio/iio.h>
> +#include "iio_core.h"
> +#include <linux/iio/machine.h>
> +#include <linux/iio/driver.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/buffer.h>
> +
> +struct iio_hw_consumer {
> +	struct list_head buffers;
> +	struct iio_channel *channels;
> +};
> +
> +struct hw_consumer_buffer {
> +	struct list_head head;
> +	struct iio_dev *indio_dev;
> +	struct iio_buffer buffer;
> +};
> +
> +static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
> +	struct iio_buffer *buffer)
> +{
> +	return container_of(buffer, struct hw_consumer_buffer, buffer);
> +}
> +
> +static void iio_hw_buf_release(struct iio_buffer *buffer)
> +{
> +	struct hw_consumer_buffer *hw_buf =
> +		iio_buffer_to_hw_consumer_buffer(buffer);
> +	kfree(hw_buf->buffer.scan_mask);
> +	kfree(hw_buf);
> +}
> +
> +static const struct iio_buffer_access_funcs iio_hw_buf_access = {
> +	.release = &iio_hw_buf_release,
> +	.modes = INDIO_BUFFER_HARDWARE,
> +};
> +
> +static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
> +	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head) {
> +		if (buf->indio_dev == indio_dev)
> +			return buf;
> +	}
> +
> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
> +	if (!buf)
> +		return NULL;
> +
> +	buf->buffer.access = &iio_hw_buf_access;
> +	buf->indio_dev = indio_dev;
> +	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
> +		sizeof(long), GFP_KERNEL);
> +	if (!buf->buffer.scan_mask)
> +		goto err_free_buf;
> +
> +	iio_buffer_init(&buf->buffer);
> +	list_add_tail(&buf->head, &hwc->buffers);
> +
> +	return buf;
> +
> +err_free_buf:
> +	kfree(buf);
> +	return NULL;
> +}
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
> +{
> +	struct hw_consumer_buffer *buf;
> +	struct iio_hw_consumer *hwc;
> +	struct iio_channel *chan;
> +	int ret;
> +
> +	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
> +	if (!hwc)
> +		return ERR_PTR(-ENOMEM);
> +
> +	INIT_LIST_HEAD(&hwc->buffers);
> +
> +	hwc->channels = iio_channel_get_all(dev);
> +	if (IS_ERR(hwc->channels)) {
> +		ret = PTR_ERR(hwc->channels);
> +		goto err_free_hwc;
> +	}
> +
> +	chan = &hwc->channels[0];
> +	while (chan->indio_dev) {
> +		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
> +		if (buf == NULL) {
> +			ret = -ENOMEM;
> +			goto err_put_buffers;
> +		}
> +		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
> +		chan++;
> +	}
> +
> +	return hwc;
> +
> +err_put_buffers:
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_buffer_put(&buf->buffer);
> +	iio_channel_release_all(hwc->channels);
> +err_free_hwc:
> +	kfree(hwc);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
> +
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	iio_channel_release_all(hwc->channels);
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_buffer_put(&buf->buffer);
> +	kfree(hwc);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
> +
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +	int ret;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head) {
> +		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
> +		if (ret)
> +			goto err_disable_buffers;
> +	}
> +
> +	return 0;
> +
> +err_disable_buffers:
> +	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
> +
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
> +{
> +	struct hw_consumer_buffer *buf;
> +
> +	list_for_each_entry(buf, &hwc->buffers, head)
> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
> +}
> +EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
> diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
> new file mode 100644
> index 0000000..f12653d
> --- /dev/null
> +++ b/include/linux/iio/hw_consumer.h
> @@ -0,0 +1,12 @@
> +#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
> +#define LINUX_IIO_HW_CONSUMER_BUFFER_H
> +
> +struct device;
> +struct iio_hw_consumer;
> +
> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
> +
> +#endif
> 

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

* Re: [PATCH v3 01/11] iio: Add hardware consumer support
  2017-06-06 10:15     ` Arnaud Pouliquen
  (?)
@ 2017-09-11  9:01       ` Arnaud Pouliquen
  -1 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-09-11  9:01 UTC (permalink / raw)
  To: Lars-Peter Clausen, Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Olivier MOYSAN,
	Maxime Coquelin, Liam Girdwood, linux-iio, Takashi Iwai,
	Rob Herring, linux-arm-kernel, Peter Meerwald-Stadler,
	Hartmut Knaack, Benjamin GAIGNARD, Jonathan Cameron,
	Alexandre TORGUE

Hello Lars,

New gentlemen Reminder.

I suppose that you have to treat a lot of subjects with more priority
than this one, but on my side I'm starting to have a lot of pressure
from project, as STM32 DFSDM upstream is blocked since close to 6 months.

As you proposed the interface, I would be fair you could push it for
upstream this month.

If you don't have time for this, another solution could be that I push
it for you.
Please tell me your preference.

Regards,
Arnaud


On 06/06/2017 12:15 PM, Arnaud Pouliquen wrote:
> Hello Lars,
> 
> Any news on the upstream of your HW consumer interface, prerequisite for
> my STM32 DFSDM upstream?
> Don't hesitate if i can help...
> 
> Regards
> Arnaud
> 
> 
> On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
>> From: Lars-Peter Clausen <lars@metafoo.de>
>>
>> Hardware consumer's can be used when one IIO device has a direct connection
>> to another device in hardware.
>>
>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>> V2 -> V3:
>> 	 Just Adding sign off.  No update but Jonathan comment.
>>          Need to be taken into account (in agreement with Lars)
>>          when transforming rfc in patch.
>> ---
>>  drivers/iio/Kconfig             |   6 ++
>>  drivers/iio/Makefile            |   1 +
>>  drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
>>  include/linux/iio/hw_consumer.h |  12 ++++
>>  4 files changed, 175 insertions(+)
>>  create mode 100644 drivers/iio/hw_consumer.c
>>  create mode 100644 include/linux/iio/hw_consumer.h
>>
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index 6743b18..956dd18 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -30,6 +30,12 @@ config IIO_CONFIGFS
>>  	  (e.g. software triggers). For more info see
>>  	  Documentation/iio/iio_configfs.txt.
>>  
>> +config IIO_HW_CONSUMER
>> +	tristate
>> +	help
>> +	  Hardware consumer buffer
>> +
>> +
>>  config IIO_TRIGGER
>>  	bool "Enable triggered sampling support"
>>  	help
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 87e4c43..8472b97 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
>>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>> +obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
>>  
>>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>>  obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
>> diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
>> new file mode 100644
>> index 0000000..66f0732
>> --- /dev/null
>> +++ b/drivers/iio/hw_consumer.c
>> @@ -0,0 +1,156 @@
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/slab.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include "iio_core.h"
>> +#include <linux/iio/machine.h>
>> +#include <linux/iio/driver.h>
>> +#include <linux/iio/consumer.h>
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/buffer.h>
>> +
>> +struct iio_hw_consumer {
>> +	struct list_head buffers;
>> +	struct iio_channel *channels;
>> +};
>> +
>> +struct hw_consumer_buffer {
>> +	struct list_head head;
>> +	struct iio_dev *indio_dev;
>> +	struct iio_buffer buffer;
>> +};
>> +
>> +static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
>> +	struct iio_buffer *buffer)
>> +{
>> +	return container_of(buffer, struct hw_consumer_buffer, buffer);
>> +}
>> +
>> +static void iio_hw_buf_release(struct iio_buffer *buffer)
>> +{
>> +	struct hw_consumer_buffer *hw_buf =
>> +		iio_buffer_to_hw_consumer_buffer(buffer);
>> +	kfree(hw_buf->buffer.scan_mask);
>> +	kfree(hw_buf);
>> +}
>> +
>> +static const struct iio_buffer_access_funcs iio_hw_buf_access = {
>> +	.release = &iio_hw_buf_release,
>> +	.modes = INDIO_BUFFER_HARDWARE,
>> +};
>> +
>> +static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
>> +	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head) {
>> +		if (buf->indio_dev == indio_dev)
>> +			return buf;
>> +	}
>> +
>> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>> +	if (!buf)
>> +		return NULL;
>> +
>> +	buf->buffer.access = &iio_hw_buf_access;
>> +	buf->indio_dev = indio_dev;
>> +	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
>> +		sizeof(long), GFP_KERNEL);
>> +	if (!buf->buffer.scan_mask)
>> +		goto err_free_buf;
>> +
>> +	iio_buffer_init(&buf->buffer);
>> +	list_add_tail(&buf->head, &hwc->buffers);
>> +
>> +	return buf;
>> +
>> +err_free_buf:
>> +	kfree(buf);
>> +	return NULL;
>> +}
>> +
>> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +	struct iio_hw_consumer *hwc;
>> +	struct iio_channel *chan;
>> +	int ret;
>> +
>> +	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
>> +	if (!hwc)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	INIT_LIST_HEAD(&hwc->buffers);
>> +
>> +	hwc->channels = iio_channel_get_all(dev);
>> +	if (IS_ERR(hwc->channels)) {
>> +		ret = PTR_ERR(hwc->channels);
>> +		goto err_free_hwc;
>> +	}
>> +
>> +	chan = &hwc->channels[0];
>> +	while (chan->indio_dev) {
>> +		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
>> +		if (buf == NULL) {
>> +			ret = -ENOMEM;
>> +			goto err_put_buffers;
>> +		}
>> +		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
>> +		chan++;
>> +	}
>> +
>> +	return hwc;
>> +
>> +err_put_buffers:
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_buffer_put(&buf->buffer);
>> +	iio_channel_release_all(hwc->channels);
>> +err_free_hwc:
>> +	kfree(hwc);
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
>> +
>> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	iio_channel_release_all(hwc->channels);
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_buffer_put(&buf->buffer);
>> +	kfree(hwc);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
>> +
>> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +	int ret;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head) {
>> +		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
>> +		if (ret)
>> +			goto err_disable_buffers;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_disable_buffers:
>> +	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
>> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
>> +
>> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
>> diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
>> new file mode 100644
>> index 0000000..f12653d
>> --- /dev/null
>> +++ b/include/linux/iio/hw_consumer.h
>> @@ -0,0 +1,12 @@
>> +#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
>> +#define LINUX_IIO_HW_CONSUMER_BUFFER_H
>> +
>> +struct device;
>> +struct iio_hw_consumer;
>> +
>> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
>> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
>> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
>> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
>> +
>> +#endif
>>

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

* Re: [PATCH v3 01/11] iio: Add hardware consumer support
@ 2017-09-11  9:01       ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-09-11  9:01 UTC (permalink / raw)
  To: Lars-Peter Clausen, Mark Brown
  Cc: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN,
	Benjamin GAIGNARD

Hello Lars,

New gentlemen Reminder.

I suppose that you have to treat a lot of subjects with more priority
than this one, but on my side I'm starting to have a lot of pressure
from project, as STM32 DFSDM upstream is blocked since close to 6 months.

As you proposed the interface, I would be fair you could push it for
upstream this month.

If you don't have time for this, another solution could be that I push
it for you.
Please tell me your preference.

Regards,
Arnaud


On 06/06/2017 12:15 PM, Arnaud Pouliquen wrote:
> Hello Lars,
> 
> Any news on the upstream of your HW consumer interface, prerequisite for
> my STM32 DFSDM upstream?
> Don't hesitate if i can help...
> 
> Regards
> Arnaud
> 
> 
> On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
>> From: Lars-Peter Clausen <lars@metafoo.de>
>>
>> Hardware consumer's can be used when one IIO device has a direct connection
>> to another device in hardware.
>>
>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>> V2 -> V3:
>> 	 Just Adding sign off.  No update but Jonathan comment.
>>          Need to be taken into account (in agreement with Lars)
>>          when transforming rfc in patch.
>> ---
>>  drivers/iio/Kconfig             |   6 ++
>>  drivers/iio/Makefile            |   1 +
>>  drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
>>  include/linux/iio/hw_consumer.h |  12 ++++
>>  4 files changed, 175 insertions(+)
>>  create mode 100644 drivers/iio/hw_consumer.c
>>  create mode 100644 include/linux/iio/hw_consumer.h
>>
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index 6743b18..956dd18 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -30,6 +30,12 @@ config IIO_CONFIGFS
>>  	  (e.g. software triggers). For more info see
>>  	  Documentation/iio/iio_configfs.txt.
>>  
>> +config IIO_HW_CONSUMER
>> +	tristate
>> +	help
>> +	  Hardware consumer buffer
>> +
>> +
>>  config IIO_TRIGGER
>>  	bool "Enable triggered sampling support"
>>  	help
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 87e4c43..8472b97 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
>>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>> +obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
>>  
>>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>>  obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
>> diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
>> new file mode 100644
>> index 0000000..66f0732
>> --- /dev/null
>> +++ b/drivers/iio/hw_consumer.c
>> @@ -0,0 +1,156 @@
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/slab.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include "iio_core.h"
>> +#include <linux/iio/machine.h>
>> +#include <linux/iio/driver.h>
>> +#include <linux/iio/consumer.h>
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/buffer.h>
>> +
>> +struct iio_hw_consumer {
>> +	struct list_head buffers;
>> +	struct iio_channel *channels;
>> +};
>> +
>> +struct hw_consumer_buffer {
>> +	struct list_head head;
>> +	struct iio_dev *indio_dev;
>> +	struct iio_buffer buffer;
>> +};
>> +
>> +static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
>> +	struct iio_buffer *buffer)
>> +{
>> +	return container_of(buffer, struct hw_consumer_buffer, buffer);
>> +}
>> +
>> +static void iio_hw_buf_release(struct iio_buffer *buffer)
>> +{
>> +	struct hw_consumer_buffer *hw_buf =
>> +		iio_buffer_to_hw_consumer_buffer(buffer);
>> +	kfree(hw_buf->buffer.scan_mask);
>> +	kfree(hw_buf);
>> +}
>> +
>> +static const struct iio_buffer_access_funcs iio_hw_buf_access = {
>> +	.release = &iio_hw_buf_release,
>> +	.modes = INDIO_BUFFER_HARDWARE,
>> +};
>> +
>> +static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
>> +	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head) {
>> +		if (buf->indio_dev == indio_dev)
>> +			return buf;
>> +	}
>> +
>> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>> +	if (!buf)
>> +		return NULL;
>> +
>> +	buf->buffer.access = &iio_hw_buf_access;
>> +	buf->indio_dev = indio_dev;
>> +	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
>> +		sizeof(long), GFP_KERNEL);
>> +	if (!buf->buffer.scan_mask)
>> +		goto err_free_buf;
>> +
>> +	iio_buffer_init(&buf->buffer);
>> +	list_add_tail(&buf->head, &hwc->buffers);
>> +
>> +	return buf;
>> +
>> +err_free_buf:
>> +	kfree(buf);
>> +	return NULL;
>> +}
>> +
>> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +	struct iio_hw_consumer *hwc;
>> +	struct iio_channel *chan;
>> +	int ret;
>> +
>> +	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
>> +	if (!hwc)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	INIT_LIST_HEAD(&hwc->buffers);
>> +
>> +	hwc->channels = iio_channel_get_all(dev);
>> +	if (IS_ERR(hwc->channels)) {
>> +		ret = PTR_ERR(hwc->channels);
>> +		goto err_free_hwc;
>> +	}
>> +
>> +	chan = &hwc->channels[0];
>> +	while (chan->indio_dev) {
>> +		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
>> +		if (buf == NULL) {
>> +			ret = -ENOMEM;
>> +			goto err_put_buffers;
>> +		}
>> +		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
>> +		chan++;
>> +	}
>> +
>> +	return hwc;
>> +
>> +err_put_buffers:
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_buffer_put(&buf->buffer);
>> +	iio_channel_release_all(hwc->channels);
>> +err_free_hwc:
>> +	kfree(hwc);
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
>> +
>> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	iio_channel_release_all(hwc->channels);
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_buffer_put(&buf->buffer);
>> +	kfree(hwc);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
>> +
>> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +	int ret;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head) {
>> +		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
>> +		if (ret)
>> +			goto err_disable_buffers;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_disable_buffers:
>> +	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
>> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
>> +
>> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
>> diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
>> new file mode 100644
>> index 0000000..f12653d
>> --- /dev/null
>> +++ b/include/linux/iio/hw_consumer.h
>> @@ -0,0 +1,12 @@
>> +#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
>> +#define LINUX_IIO_HW_CONSUMER_BUFFER_H
>> +
>> +struct device;
>> +struct iio_hw_consumer;
>> +
>> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
>> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
>> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
>> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
>> +
>> +#endif
>>

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

* [PATCH v3 01/11] iio: Add hardware consumer support
@ 2017-09-11  9:01       ` Arnaud Pouliquen
  0 siblings, 0 replies; 109+ messages in thread
From: Arnaud Pouliquen @ 2017-09-11  9:01 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Lars,

New gentlemen Reminder.

I suppose that you have to treat a lot of subjects with more priority
than this one, but on my side I'm starting to have a lot of pressure
from project, as STM32 DFSDM upstream is blocked since close to 6 months.

As you proposed the interface, I would be fair you could push it for
upstream this month.

If you don't have time for this, another solution could be that I push
it for you.
Please tell me your preference.

Regards,
Arnaud


On 06/06/2017 12:15 PM, Arnaud Pouliquen wrote:
> Hello Lars,
> 
> Any news on the upstream of your HW consumer interface, prerequisite for
> my STM32 DFSDM upstream?
> Don't hesitate if i can help...
> 
> Regards
> Arnaud
> 
> 
> On 03/17/2017 03:08 PM, Arnaud POULIQUEN wrote:
>> From: Lars-Peter Clausen <lars@metafoo.de>
>>
>> Hardware consumer's can be used when one IIO device has a direct connection
>> to another device in hardware.
>>
>> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>> V2 -> V3:
>> 	 Just Adding sign off.  No update but Jonathan comment.
>>          Need to be taken into account (in agreement with Lars)
>>          when transforming rfc in patch.
>> ---
>>  drivers/iio/Kconfig             |   6 ++
>>  drivers/iio/Makefile            |   1 +
>>  drivers/iio/hw_consumer.c       | 156 ++++++++++++++++++++++++++++++++++++++++
>>  include/linux/iio/hw_consumer.h |  12 ++++
>>  4 files changed, 175 insertions(+)
>>  create mode 100644 drivers/iio/hw_consumer.c
>>  create mode 100644 include/linux/iio/hw_consumer.h
>>
>> diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
>> index 6743b18..956dd18 100644
>> --- a/drivers/iio/Kconfig
>> +++ b/drivers/iio/Kconfig
>> @@ -30,6 +30,12 @@ config IIO_CONFIGFS
>>  	  (e.g. software triggers). For more info see
>>  	  Documentation/iio/iio_configfs.txt.
>>  
>> +config IIO_HW_CONSUMER
>> +	tristate
>> +	help
>> +	  Hardware consumer buffer
>> +
>> +
>>  config IIO_TRIGGER
>>  	bool "Enable triggered sampling support"
>>  	help
>> diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
>> index 87e4c43..8472b97 100644
>> --- a/drivers/iio/Makefile
>> +++ b/drivers/iio/Makefile
>> @@ -6,6 +6,7 @@ obj-$(CONFIG_IIO) += industrialio.o
>>  industrialio-y := industrialio-core.o industrialio-event.o inkern.o
>>  industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
>>  industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
>> +obj-$(CONFIG_IIO_HW_CONSUMER) += hw_consumer.o
>>  
>>  obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
>>  obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
>> diff --git a/drivers/iio/hw_consumer.c b/drivers/iio/hw_consumer.c
>> new file mode 100644
>> index 0000000..66f0732
>> --- /dev/null
>> +++ b/drivers/iio/hw_consumer.c
>> @@ -0,0 +1,156 @@
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/slab.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include "iio_core.h"
>> +#include <linux/iio/machine.h>
>> +#include <linux/iio/driver.h>
>> +#include <linux/iio/consumer.h>
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/buffer.h>
>> +
>> +struct iio_hw_consumer {
>> +	struct list_head buffers;
>> +	struct iio_channel *channels;
>> +};
>> +
>> +struct hw_consumer_buffer {
>> +	struct list_head head;
>> +	struct iio_dev *indio_dev;
>> +	struct iio_buffer buffer;
>> +};
>> +
>> +static struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer(
>> +	struct iio_buffer *buffer)
>> +{
>> +	return container_of(buffer, struct hw_consumer_buffer, buffer);
>> +}
>> +
>> +static void iio_hw_buf_release(struct iio_buffer *buffer)
>> +{
>> +	struct hw_consumer_buffer *hw_buf =
>> +		iio_buffer_to_hw_consumer_buffer(buffer);
>> +	kfree(hw_buf->buffer.scan_mask);
>> +	kfree(hw_buf);
>> +}
>> +
>> +static const struct iio_buffer_access_funcs iio_hw_buf_access = {
>> +	.release = &iio_hw_buf_release,
>> +	.modes = INDIO_BUFFER_HARDWARE,
>> +};
>> +
>> +static struct hw_consumer_buffer *iio_hw_consumer_get_buffer(
>> +	struct iio_hw_consumer *hwc, struct iio_dev *indio_dev)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head) {
>> +		if (buf->indio_dev == indio_dev)
>> +			return buf;
>> +	}
>> +
>> +	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
>> +	if (!buf)
>> +		return NULL;
>> +
>> +	buf->buffer.access = &iio_hw_buf_access;
>> +	buf->indio_dev = indio_dev;
>> +	buf->buffer.scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
>> +		sizeof(long), GFP_KERNEL);
>> +	if (!buf->buffer.scan_mask)
>> +		goto err_free_buf;
>> +
>> +	iio_buffer_init(&buf->buffer);
>> +	list_add_tail(&buf->head, &hwc->buffers);
>> +
>> +	return buf;
>> +
>> +err_free_buf:
>> +	kfree(buf);
>> +	return NULL;
>> +}
>> +
>> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +	struct iio_hw_consumer *hwc;
>> +	struct iio_channel *chan;
>> +	int ret;
>> +
>> +	hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
>> +	if (!hwc)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	INIT_LIST_HEAD(&hwc->buffers);
>> +
>> +	hwc->channels = iio_channel_get_all(dev);
>> +	if (IS_ERR(hwc->channels)) {
>> +		ret = PTR_ERR(hwc->channels);
>> +		goto err_free_hwc;
>> +	}
>> +
>> +	chan = &hwc->channels[0];
>> +	while (chan->indio_dev) {
>> +		buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev);
>> +		if (buf == NULL) {
>> +			ret = -ENOMEM;
>> +			goto err_put_buffers;
>> +		}
>> +		set_bit(chan->channel->scan_index, buf->buffer.scan_mask);
>> +		chan++;
>> +	}
>> +
>> +	return hwc;
>> +
>> +err_put_buffers:
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_buffer_put(&buf->buffer);
>> +	iio_channel_release_all(hwc->channels);
>> +err_free_hwc:
>> +	kfree(hwc);
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_alloc);
>> +
>> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	iio_channel_release_all(hwc->channels);
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_buffer_put(&buf->buffer);
>> +	kfree(hwc);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_free);
>> +
>> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +	int ret;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head) {
>> +		ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL);
>> +		if (ret)
>> +			goto err_disable_buffers;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_disable_buffers:
>> +	list_for_each_entry_continue_reverse(buf, &hwc->buffers, head)
>> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_enable);
>> +
>> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc)
>> +{
>> +	struct hw_consumer_buffer *buf;
>> +
>> +	list_for_each_entry(buf, &hwc->buffers, head)
>> +		iio_update_buffers(buf->indio_dev, NULL, &buf->buffer);
>> +}
>> +EXPORT_SYMBOL_GPL(iio_hw_consumer_disable);
>> diff --git a/include/linux/iio/hw_consumer.h b/include/linux/iio/hw_consumer.h
>> new file mode 100644
>> index 0000000..f12653d
>> --- /dev/null
>> +++ b/include/linux/iio/hw_consumer.h
>> @@ -0,0 +1,12 @@
>> +#ifndef LINUX_IIO_HW_CONSUMER_BUFFER_H
>> +#define LINUX_IIO_HW_CONSUMER_BUFFER_H
>> +
>> +struct device;
>> +struct iio_hw_consumer;
>> +
>> +struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev);
>> +void iio_hw_consumer_free(struct iio_hw_consumer *hwc);
>> +int iio_hw_consumer_enable(struct iio_hw_consumer *hwc);
>> +void iio_hw_consumer_disable(struct iio_hw_consumer *hwc);
>> +
>> +#endif
>>

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

end of thread, other threads:[~2017-09-11  9:03 UTC | newest]

Thread overview: 109+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-17 14:08 [PATCH v3 00/11] Add STM32 DFSDM support Arnaud Pouliquen
2017-03-17 14:08 ` Arnaud Pouliquen
2017-03-17 14:08 ` Arnaud Pouliquen
2017-03-17 14:08 ` [PATCH v3 01/11] iio: Add hardware consumer support Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-20  7:48   ` Peter Meerwald-Stadler
2017-06-06 10:15   ` Arnaud Pouliquen
2017-06-06 10:15     ` Arnaud Pouliquen
2017-06-06 10:15     ` Arnaud Pouliquen
2017-09-11  9:01     ` Arnaud Pouliquen
2017-09-11  9:01       ` Arnaud Pouliquen
2017-09-11  9:01       ` Arnaud Pouliquen
2017-03-17 14:08 ` [PATCH v3 02/11] IIO: Add DT bindings for sigma delta adc modulator Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
     [not found]   ` <1489759704-30217-3-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-03-24 14:21     ` Rob Herring
2017-03-24 14:21       ` Rob Herring
2017-03-24 14:21       ` Rob Herring
2017-03-17 14:08 ` [PATCH v3 03/11] IIO: ADC: add sigma delta modulator support Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-20  6:24   ` [alsa-devel] " kbuild test robot
2017-03-20  6:24     ` kbuild test robot
2017-03-20  6:24     ` kbuild test robot
2017-03-20  6:51   ` kbuild test robot
2017-03-20  6:51     ` kbuild test robot
2017-03-20  6:51     ` kbuild test robot
2017-03-17 14:08 ` [PATCH v3 04/11] IIO: add DT bindings for stm32 DFSDM filter Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-24 14:37   ` Rob Herring
2017-03-24 14:37     ` Rob Herring
2017-03-24 14:37     ` Rob Herring
2017-03-17 14:08 ` [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
     [not found]   ` <1489759704-30217-6-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-03-19 22:25     ` Jonathan Cameron
2017-03-19 22:25       ` Jonathan Cameron
2017-03-19 22:25       ` Jonathan Cameron
2017-03-20 11:24       ` Arnaud Pouliquen
2017-03-20 11:24         ` Arnaud Pouliquen
2017-03-20 11:24         ` Arnaud Pouliquen
2017-03-25 15:53         ` Jonathan Cameron
2017-03-25 15:53           ` Jonathan Cameron
2017-03-25 15:53           ` Jonathan Cameron
2017-03-20  7:22     ` [alsa-devel] " kbuild test robot
2017-03-20  7:22       ` kbuild test robot
2017-03-20  8:04     ` kbuild test robot
2017-03-20  8:04       ` kbuild test robot
2017-03-20  8:04       ` kbuild test robot
2017-03-17 14:08 ` [PATCH v3 06/11] IIO: ADC: add stm32 DFSDM support for PDM microphone Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
     [not found]   ` <1489759704-30217-7-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-03-19 22:38     ` Jonathan Cameron
2017-03-19 22:38       ` Jonathan Cameron
2017-03-19 22:38       ` Jonathan Cameron
2017-03-20 11:29       ` Arnaud Pouliquen
2017-03-20 11:29         ` Arnaud Pouliquen
2017-03-20 11:29         ` Arnaud Pouliquen
2017-03-25 15:59         ` Jonathan Cameron
2017-03-25 15:59           ` Jonathan Cameron
2017-03-25 15:59           ` Jonathan Cameron
2017-03-28  7:45           ` Arnaud Pouliquen
2017-03-28  7:45             ` Arnaud Pouliquen
2017-03-28  7:45             ` Arnaud Pouliquen
2017-03-17 14:08 ` [PATCH v3 07/11] IIO: consumer: allow to set buffer sizes Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
     [not found]   ` <1489759704-30217-8-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-03-19 22:44     ` Jonathan Cameron
2017-03-19 22:44       ` Jonathan Cameron
2017-03-19 22:44       ` Jonathan Cameron
2017-03-20 11:30       ` Arnaud Pouliquen
2017-03-20 11:30         ` Arnaud Pouliquen
2017-03-20 11:30         ` Arnaud Pouliquen
2017-03-25 16:01         ` Jonathan Cameron
2017-03-25 16:01           ` Jonathan Cameron
2017-03-25 16:01           ` Jonathan Cameron
2017-03-20  6:22     ` [alsa-devel] " kbuild test robot
2017-03-20  6:22       ` kbuild test robot
2017-03-20  6:22       ` kbuild test robot
2017-03-20  8:08   ` Peter Meerwald-Stadler
2017-03-17 14:08 ` [PATCH v3 08/11] ASoC: Add bindings for DMIC codec driver Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-24 14:46   ` Rob Herring
2017-03-24 14:46     ` Rob Herring
2017-03-24 14:46     ` Rob Herring
2017-03-27 11:59     ` Mark Brown
2017-03-27 11:59       ` Mark Brown
2017-03-27 11:59       ` Mark Brown
2017-03-17 14:08 ` [PATCH v3 09/11] ASoC: codec: add DT support in dmic codec Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08 ` [PATCH v3 10/11] ASoC: add bindings for stm32 DFSDM filter Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-24 14:52   ` Rob Herring
2017-03-24 14:52     ` Rob Herring
2017-03-24 14:52     ` Rob Herring
2017-03-29 12:42     ` Arnaud Pouliquen
2017-03-29 12:42       ` Arnaud Pouliquen
2017-03-29 12:42       ` Arnaud Pouliquen
2017-03-17 14:08 ` [PATCH v3 11/11] ASoC: stm32: add DFSDM DAI support Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 14:08   ` Arnaud Pouliquen
2017-03-17 16:36 ` [PATCH v3 00/11] Add STM32 DFSDM support Arnaud Pouliquen
2017-03-17 16:36   ` Arnaud Pouliquen
2017-03-17 16:36   ` Arnaud Pouliquen

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.