All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v2 0/7]  Add STM32 DFSDM support
@ 2017-02-13 16:38 ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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,

This RFC is following first patch-set sent for DFSDM driver
(https://www.spinics.net/lists/arm-kernel/msg557133.html).
AS MFD driver has been rejected, a new way to integrate DFSDM has to 
be defined.

Aim of this RFC is to provide a base to discuss this redesign.

1) DFSDM hardware overview:
-----------------------
The Digital Filter for Sigma Delta  is a module dedicated to interface external sigma 
delta modulators to STM32 micro-controllers. It is targeted for:
	- Audio mode signal with Pulse density modulation (PDM) microphone
	- motor and Sensing mode with sigma delta ADC modulator

Main Features:
 - Up to 8 multiplexed input digital serial channel (SIT)
	. SPI (PDM compatible) or Manchester interface
	.  Slave or master ( one main SPI CLK OUT for all interface) 
 - Alternative inputs for 8 internal digital parallel channels (PIT)
	. 16 bits resolution
	. internal sources: ADC or memory
 - Digital signal processing:
	. 4 instances
	. Sincx filter (x : order 1 to 5) oversampling ratio up to 1024
	. integrator: oversampling ratio up to 256
 - Channel multiplexer
	. allow to connect SIT or PIT to filter
	. channel n can be connected SIT n or SIT n+1
 - up to 24-bit output resolution
 	. signed
        . sampling rate  and resolution depend on filter parameters.
 - data offset correction
 - detector:
	. analog watchdog
	. short circuit detection
	. clock absence detection
	. extremes

For details on DFSDM IP, please refer to STM32F413 data-sheet chapter 15:
http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf

2) SW design proposal:
---------------------
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.

Regards
Arnaud

Arnaud Pouliquen (6):
  iio: Add hardware consumer support
  IIO: Add bindings for simple sigma delta adc
  IIO: ADC: add sigma delta modulator support
  ASoC: stm32: add DFSDM DAI support
  IIO: add bindings for stm32 DFSDM filter
  IIO: ADC: add stm32 DFSDM support

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

 .../devicetree/bindings/iio/adc/simple_sd_adc.txt  |  13 +
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 ++++++
 drivers/iio/Kconfig                                |   6 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/adc/Kconfig                            |  24 +
 drivers/iio/adc/Makefile                           |   2 +
 drivers/iio/adc/simple_sd_adc.c                    | 112 +++++
 drivers/iio/adc/stm32-dfsdm-adc.c                  | 483 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c                 | 273 ++++++++++++
 drivers/iio/adc/stm32-dfsdm.h                      | 141 ++++++
 drivers/iio/hw_consumer.c                          | 156 +++++++
 include/linux/iio/hw_consumer.h                    |  12 +
 include/sound/dmaengine_pcm.h                      |   3 +
 include/sound/stm32-adfsdm.h                       |  80 ++++
 sound/soc/Kconfig                                  |   1 +
 sound/soc/Makefile                                 |   1 +
 sound/soc/soc-generic-dmaengine-pcm.c              |  37 +-
 sound/soc/stm/Kconfig                              |  10 +
 sound/soc/stm/Makefile                             |   2 +
 sound/soc/stm/stm32_adfsdm.c                       | 365 ++++++++++++++++
 20 files changed, 1845 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
 create mode 100644 drivers/iio/adc/simple_sd_adc.c
 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
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/hw_consumer.h
 create mode 100644 include/sound/stm32-adfsdm.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

-- 
1.9.1

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

* [RFC v2 0/7]  Add STM32 DFSDM support
@ 2017-02-13 16:38 ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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,

This RFC is following first patch-set sent for DFSDM driver
(https://www.spinics.net/lists/arm-kernel/msg557133.html).
AS MFD driver has been rejected, a new way to integrate DFSDM has to 
be defined.

Aim of this RFC is to provide a base to discuss this redesign.

1) DFSDM hardware overview:
-----------------------
The Digital Filter for Sigma Delta  is a module dedicated to interface external sigma 
delta modulators to STM32 micro-controllers. It is targeted for:
	- Audio mode signal with Pulse density modulation (PDM) microphone
	- motor and Sensing mode with sigma delta ADC modulator

Main Features:
 - Up to 8 multiplexed input digital serial channel (SIT)
	. SPI (PDM compatible) or Manchester interface
	.  Slave or master ( one main SPI CLK OUT for all interface) 
 - Alternative inputs for 8 internal digital parallel channels (PIT)
	. 16 bits resolution
	. internal sources: ADC or memory
 - Digital signal processing:
	. 4 instances
	. Sincx filter (x : order 1 to 5) oversampling ratio up to 1024
	. integrator: oversampling ratio up to 256
 - Channel multiplexer
	. allow to connect SIT or PIT to filter
	. channel n can be connected SIT n or SIT n+1
 - up to 24-bit output resolution
 	. signed
        . sampling rate  and resolution depend on filter parameters.
 - data offset correction
 - detector:
	. analog watchdog
	. short circuit detection
	. clock absence detection
	. extremes

For details on DFSDM IP, please refer to STM32F413 data-sheet chapter 15:
http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf

2) SW design proposal:
---------------------
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.

Regards
Arnaud

Arnaud Pouliquen (6):
  iio: Add hardware consumer support
  IIO: Add bindings for simple sigma delta adc
  IIO: ADC: add sigma delta modulator support
  ASoC: stm32: add DFSDM DAI support
  IIO: add bindings for stm32 DFSDM filter
  IIO: ADC: add stm32 DFSDM support

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

 .../devicetree/bindings/iio/adc/simple_sd_adc.txt  |  13 +
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 ++++++
 drivers/iio/Kconfig                                |   6 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/adc/Kconfig                            |  24 +
 drivers/iio/adc/Makefile                           |   2 +
 drivers/iio/adc/simple_sd_adc.c                    | 112 +++++
 drivers/iio/adc/stm32-dfsdm-adc.c                  | 483 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c                 | 273 ++++++++++++
 drivers/iio/adc/stm32-dfsdm.h                      | 141 ++++++
 drivers/iio/hw_consumer.c                          | 156 +++++++
 include/linux/iio/hw_consumer.h                    |  12 +
 include/sound/dmaengine_pcm.h                      |   3 +
 include/sound/stm32-adfsdm.h                       |  80 ++++
 sound/soc/Kconfig                                  |   1 +
 sound/soc/Makefile                                 |   1 +
 sound/soc/soc-generic-dmaengine-pcm.c              |  37 +-
 sound/soc/stm/Kconfig                              |  10 +
 sound/soc/stm/Makefile                             |   2 +
 sound/soc/stm/stm32_adfsdm.c                       | 365 ++++++++++++++++
 20 files changed, 1845 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
 create mode 100644 drivers/iio/adc/simple_sd_adc.c
 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
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/hw_consumer.h
 create mode 100644 include/sound/stm32-adfsdm.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

-- 
1.9.1


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

* [RFC v2 0/7]  Add STM32 DFSDM support
@ 2017-02-13 16:38 ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This RFC is following first patch-set sent for DFSDM driver
(https://www.spinics.net/lists/arm-kernel/msg557133.html).
AS MFD driver has been rejected, a new way to integrate DFSDM has to 
be defined.

Aim of this RFC is to provide a base to discuss this redesign.

1) DFSDM hardware overview:
-----------------------
The Digital Filter for Sigma Delta  is a module dedicated to interface external sigma 
delta modulators to STM32 micro-controllers. It is targeted for:
	- Audio mode signal with Pulse density modulation (PDM) microphone
	- motor and Sensing mode with sigma delta ADC modulator

Main Features:
 - Up to 8 multiplexed input digital serial channel (SIT)
	. SPI (PDM compatible) or Manchester interface
	.  Slave or master ( one main SPI CLK OUT for all interface) 
 - Alternative inputs for 8 internal digital parallel channels (PIT)
	. 16 bits resolution
	. internal sources: ADC or memory
 - Digital signal processing:
	. 4 instances
	. Sincx filter (x : order 1 to 5) oversampling ratio up to 1024
	. integrator: oversampling ratio up to 256
 - Channel multiplexer
	. allow to connect SIT or PIT to filter
	. channel n can be connected SIT n or SIT n+1
 - up to 24-bit output resolution
 	. signed
        . sampling rate  and resolution depend on filter parameters.
 - data offset correction
 - detector:
	. analog watchdog
	. short circuit detection
	. clock absence detection
	. extremes

For details on DFSDM IP, please refer to STM32F413 data-sheet chapter 15:
http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf

2) SW design proposal:
---------------------
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.

Regards
Arnaud

Arnaud Pouliquen (6):
  iio: Add hardware consumer support
  IIO: Add bindings for simple sigma delta adc
  IIO: ADC: add sigma delta modulator support
  ASoC: stm32: add DFSDM DAI support
  IIO: add bindings for stm32 DFSDM filter
  IIO: ADC: add stm32 DFSDM support

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

 .../devicetree/bindings/iio/adc/simple_sd_adc.txt  |  13 +
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 ++++++
 drivers/iio/Kconfig                                |   6 +
 drivers/iio/Makefile                               |   1 +
 drivers/iio/adc/Kconfig                            |  24 +
 drivers/iio/adc/Makefile                           |   2 +
 drivers/iio/adc/simple_sd_adc.c                    | 112 +++++
 drivers/iio/adc/stm32-dfsdm-adc.c                  | 483 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c                 | 273 ++++++++++++
 drivers/iio/adc/stm32-dfsdm.h                      | 141 ++++++
 drivers/iio/hw_consumer.c                          | 156 +++++++
 include/linux/iio/hw_consumer.h                    |  12 +
 include/sound/dmaengine_pcm.h                      |   3 +
 include/sound/stm32-adfsdm.h                       |  80 ++++
 sound/soc/Kconfig                                  |   1 +
 sound/soc/Makefile                                 |   1 +
 sound/soc/soc-generic-dmaengine-pcm.c              |  37 +-
 sound/soc/stm/Kconfig                              |  10 +
 sound/soc/stm/Makefile                             |   2 +
 sound/soc/stm/stm32_adfsdm.c                       | 365 ++++++++++++++++
 20 files changed, 1845 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
 create mode 100644 drivers/iio/adc/simple_sd_adc.c
 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
 create mode 100644 drivers/iio/hw_consumer.c
 create mode 100644 include/linux/iio/hw_consumer.h
 create mode 100644 include/sound/stm32-adfsdm.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

-- 
1.9.1

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

* [RFC v2 1/7] iio: Add hardware consumer support
  2017-02-13 16:38 ` Arnaud Pouliquen
  (?)
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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

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

* [RFC v2 1/7] iio: Add hardware consumer support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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

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

* [RFC v2 1/7] iio: Add hardware consumer support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 UTC (permalink / raw)
  To: linux-arm-kernel

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

* [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
  2017-02-13 16:38 ` Arnaud Pouliquen
  (?)
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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>
---
 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
new file mode 100644
index 0000000..2b3968a
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
@@ -0,0 +1,13 @@
+Device-Tree bindings for simple sigma delta adc
+
+Required properties:
+- compatible: should be "sd-modulator".
+- #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>;
+		status = "okay";
+	};
-- 
1.9.1

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

* [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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>
---
 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
new file mode 100644
index 0000000..2b3968a
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
@@ -0,0 +1,13 @@
+Device-Tree bindings for simple sigma delta adc
+
+Required properties:
+- compatible: should be "sd-modulator".
+- #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>;
+		status = "okay";
+	};
-- 
1.9.1


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

* [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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>
---
 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
new file mode 100644
index 0000000..2b3968a
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
@@ -0,0 +1,13 @@
+Device-Tree bindings for simple sigma delta adc
+
+Required properties:
+- compatible: should be "sd-modulator".
+- #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>;
+		status = "okay";
+	};
-- 
1.9.1

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

* [RFC v2 3/7] IIO: ADC: add sigma delta modulator support
  2017-02-13 16:38 ` Arnaud Pouliquen
  (?)
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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 dummy driver to support sigma delta modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig         |  11 ++++
 drivers/iio/adc/Makefile        |   1 +
 drivers/iio/adc/simple_sd_adc.c | 112 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 drivers/iio/adc/simple_sd_adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..d4366ac 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 SIMPLE_SD_ADC
+	tristate "Simple 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..bd67144 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_SIMPLE_SD_ADC) += simple_sd_adc.o
diff --git a/drivers/iio/adc/simple_sd_adc.c b/drivers/iio/adc/simple_sd_adc.c
new file mode 100644
index 0000000..4bc8b3c
--- /dev/null
+++ b/drivers/iio/adc/simple_sd_adc.c
@@ -0,0 +1,112 @@
+/*
+ * simple 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 simple_sd_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 simple_sd_iio_info = {
+	.of_xlate = simple_sd_of_xlate,
+};
+
+static const struct iio_buffer_setup_ops simple_sd_buffer_ops;
+
+static int simple_sd_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *iio;
+	struct iio_chan_spec *ch;
+
+	dev_dbg(&pdev->dev, "%s:\n", __func__);
+	iio = devm_iio_device_alloc(dev, 0);
+	if (!iio)
+		return -ENOMEM;
+
+	/* Define one channel */
+	ch = devm_kzalloc(&iio->dev, sizeof(*ch), GFP_KERNEL);
+	if (!ch)
+		return -ENOMEM;
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = dev->of_node;
+	iio->name = dev_name(dev);
+	iio->info = &simple_sd_iio_info;
+	iio->modes = INDIO_BUFFER_HARDWARE;
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = 0;
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 1;
+	ch->scan_type.storagebits = 1;
+	ch->scan_type.shift = 0;
+
+	iio->num_channels = 1;
+	iio->channels = ch;
+
+	platform_set_drvdata(pdev, iio);
+
+	return iio_device_register(iio);
+}
+
+static int simple_sd_remove(struct platform_device *pdev)
+{
+	struct iio_dev *iio = platform_get_drvdata(pdev);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+static const struct of_device_id sd_adc_of_match[] = {
+	{ .compatible = "sd-modulator" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adc081c_of_match);
+
+static struct platform_driver simple_sd_adc = {
+	.driver = {
+		.name = "simple_sd_adc",
+		.of_match_table = of_match_ptr(sd_adc_of_match),
+	},
+	.probe = simple_sd_probe,
+	.remove = simple_sd_remove,
+};
+
+module_platform_driver(simple_sd_adc);
+
+MODULE_DESCRIPTION("simple signma delta modulator");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* [RFC v2 3/7] IIO: ADC: add sigma delta modulator support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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 dummy driver to support sigma delta modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig         |  11 ++++
 drivers/iio/adc/Makefile        |   1 +
 drivers/iio/adc/simple_sd_adc.c | 112 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 drivers/iio/adc/simple_sd_adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..d4366ac 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 SIMPLE_SD_ADC
+	tristate "Simple 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..bd67144 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_SIMPLE_SD_ADC) += simple_sd_adc.o
diff --git a/drivers/iio/adc/simple_sd_adc.c b/drivers/iio/adc/simple_sd_adc.c
new file mode 100644
index 0000000..4bc8b3c
--- /dev/null
+++ b/drivers/iio/adc/simple_sd_adc.c
@@ -0,0 +1,112 @@
+/*
+ * simple 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 simple_sd_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 simple_sd_iio_info = {
+	.of_xlate = simple_sd_of_xlate,
+};
+
+static const struct iio_buffer_setup_ops simple_sd_buffer_ops;
+
+static int simple_sd_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *iio;
+	struct iio_chan_spec *ch;
+
+	dev_dbg(&pdev->dev, "%s:\n", __func__);
+	iio = devm_iio_device_alloc(dev, 0);
+	if (!iio)
+		return -ENOMEM;
+
+	/* Define one channel */
+	ch = devm_kzalloc(&iio->dev, sizeof(*ch), GFP_KERNEL);
+	if (!ch)
+		return -ENOMEM;
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = dev->of_node;
+	iio->name = dev_name(dev);
+	iio->info = &simple_sd_iio_info;
+	iio->modes = INDIO_BUFFER_HARDWARE;
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = 0;
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 1;
+	ch->scan_type.storagebits = 1;
+	ch->scan_type.shift = 0;
+
+	iio->num_channels = 1;
+	iio->channels = ch;
+
+	platform_set_drvdata(pdev, iio);
+
+	return iio_device_register(iio);
+}
+
+static int simple_sd_remove(struct platform_device *pdev)
+{
+	struct iio_dev *iio = platform_get_drvdata(pdev);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+static const struct of_device_id sd_adc_of_match[] = {
+	{ .compatible = "sd-modulator" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adc081c_of_match);
+
+static struct platform_driver simple_sd_adc = {
+	.driver = {
+		.name = "simple_sd_adc",
+		.of_match_table = of_match_ptr(sd_adc_of_match),
+	},
+	.probe = simple_sd_probe,
+	.remove = simple_sd_remove,
+};
+
+module_platform_driver(simple_sd_adc);
+
+MODULE_DESCRIPTION("simple signma delta modulator");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


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

* [RFC v2 3/7] IIO: ADC: add sigma delta modulator support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 UTC (permalink / raw)
  To: linux-arm-kernel

Add dummy driver to support sigma delta modulators.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig         |  11 ++++
 drivers/iio/adc/Makefile        |   1 +
 drivers/iio/adc/simple_sd_adc.c | 112 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 drivers/iio/adc/simple_sd_adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..d4366ac 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 SIMPLE_SD_ADC
+	tristate "Simple 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..bd67144 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_SIMPLE_SD_ADC) += simple_sd_adc.o
diff --git a/drivers/iio/adc/simple_sd_adc.c b/drivers/iio/adc/simple_sd_adc.c
new file mode 100644
index 0000000..4bc8b3c
--- /dev/null
+++ b/drivers/iio/adc/simple_sd_adc.c
@@ -0,0 +1,112 @@
+/*
+ * simple 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 simple_sd_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 simple_sd_iio_info = {
+	.of_xlate = simple_sd_of_xlate,
+};
+
+static const struct iio_buffer_setup_ops simple_sd_buffer_ops;
+
+static int simple_sd_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *iio;
+	struct iio_chan_spec *ch;
+
+	dev_dbg(&pdev->dev, "%s:\n", __func__);
+	iio = devm_iio_device_alloc(dev, 0);
+	if (!iio)
+		return -ENOMEM;
+
+	/* Define one channel */
+	ch = devm_kzalloc(&iio->dev, sizeof(*ch), GFP_KERNEL);
+	if (!ch)
+		return -ENOMEM;
+
+	iio->dev.parent = dev;
+	iio->dev.of_node = dev->of_node;
+	iio->name = dev_name(dev);
+	iio->info = &simple_sd_iio_info;
+	iio->modes = INDIO_BUFFER_HARDWARE;
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = 0;
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 1;
+	ch->scan_type.storagebits = 1;
+	ch->scan_type.shift = 0;
+
+	iio->num_channels = 1;
+	iio->channels = ch;
+
+	platform_set_drvdata(pdev, iio);
+
+	return iio_device_register(iio);
+}
+
+static int simple_sd_remove(struct platform_device *pdev)
+{
+	struct iio_dev *iio = platform_get_drvdata(pdev);
+
+	iio_device_unregister(iio);
+
+	return 0;
+}
+
+static const struct of_device_id sd_adc_of_match[] = {
+	{ .compatible = "sd-modulator" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adc081c_of_match);
+
+static struct platform_driver simple_sd_adc = {
+	.driver = {
+		.name = "simple_sd_adc",
+		.of_match_table = of_match_ptr(sd_adc_of_match),
+	},
+	.probe = simple_sd_probe,
+	.remove = simple_sd_remove,
+};
+
+module_platform_driver(simple_sd_adc);
+
+MODULE_DESCRIPTION("simple signma delta modulator");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
  2017-02-13 16:38 ` Arnaud Pouliquen
  (?)
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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: olivier moysan <olivier.moysan@st.com>

Add copy support in pcm damengine operations.
As example, this allows to:
- process data before rendering (IEC status insertion),
- process captured sample ( sample mask and shift).

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

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

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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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: olivier moysan <olivier.moysan@st.com>

Add copy support in pcm damengine operations.
As example, this allows to:
- process data before rendering (IEC status insertion),
- process captured sample ( sample mask and shift).

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

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


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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 UTC (permalink / raw)
  To: linux-arm-kernel

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

Add copy support in pcm damengine operations.
As example, this allows to:
- process data before rendering (IEC status insertion),
- process captured sample ( sample mask and shift).

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

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

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

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-13 16:38 ` Arnaud Pouliquen
  (?)
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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>
---
 include/sound/stm32-adfsdm.h |  80 ++++++++++
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 ++
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 365 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 459 insertions(+)
 create mode 100644 include/sound/stm32-adfsdm.h
 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/include/sound/stm32-adfsdm.h b/include/sound/stm32-adfsdm.h
new file mode 100644
index 0000000..ff5899d
--- /dev/null
+++ b/include/sound/stm32-adfsdm.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver API
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+#ifndef STM32_ADFSDM_H
+#define STM32_ADFSDM_H
+
+struct stm32_dfsdm_adc;
+
+/**
+ * struct stm32_dfsdm_hw_param - stm32 audio hardware params
+ * @rate:		sampling rate
+ * @sample_bits:	sample size in bits
+ * @max_scaling:	effective scaling in bit computed by iio driver
+ */
+struct stm32_dfsdm_hw_param {
+	unsigned int rate;
+	unsigned int sample_bits;
+	unsigned int *max_scaling;
+};
+
+/*
+ * Potential improvement:
+ * Following structure and functions could be generic and declared in
+ * an asoc-iio.h
+ */
+struct stm32_adfsdm_codec_ops {
+	/*
+	 * Set the SPI or manchester input Frequency
+	 * Optional: not use if DFSDM is master on SPI
+	 */
+	void (*set_sysclk)(struct stm32_dfsdm_adc *adc, unsigned int freq);
+
+	/*
+	 * Set expected audio sampling rate and format.
+	 * Precision is returned to allow to rescale samples
+	 */
+	int (*set_hwparam)(struct stm32_dfsdm_adc *adc,
+			   struct stm32_dfsdm_hw_param *params);
+
+	/* Called when ASoC starts an audio stream setup. */
+	int (*audio_startup)(struct stm32_dfsdm_adc *adc);
+
+	/* Shuts down the audio stream. */
+	void (*audio_shutdown)(struct stm32_dfsdm_adc *adc);
+
+	/*
+	 * Provides DMA source physicla addr to allow ALsa to handle DMA
+	 * transfers.
+	 */
+	dma_addr_t (*get_dma_source)(struct stm32_dfsdm_adc *adc);
+
+	/* Register callback to treat overrun issues */
+	void (*register_xrun_cb)(struct stm32_dfsdm_adc *adc,
+				 void (*overrun_cb)(void *context),
+				 void *context);
+
+};
+
+/* stm32 dfsdm initalization data */
+struct stm32_adfsdm_pdata {
+	const struct stm32_adfsdm_codec_ops *ops;
+	struct stm32_dfsdm_adc *adc;
+};
+
+#define STM32_ADFSDM_DRV_NAME "stm32-dfsdm-audio"
+#endif
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..4488461
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,365 @@
+/*
+ * This file is part of STM32 DFSDM ASoC DAI driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ *          Olivier Moysan <olivier.moysan@st.com>
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/stm32-adfsdm.h>
+
+#define STM32_ADFSDM_DATA_MASK	GENMASK(31, 8)
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai_drv;
+	struct stm32_adfsdm_pdata *pdata; /* platform data set by IIO driver */
+	struct snd_dmaengine_dai_dma_data dma_data;  /* dma config */
+	struct snd_pcm_substream *substream;  
+	struct snd_pcm_hw_constraint_list rates_const;
+	unsigned long dmic_clk; /* SPI or manchester input clock frequency */
+	unsigned int fl_id;   /* filter instance ID */
+	unsigned int order; /* filter order */
+	unsigned int max_scaling;  /* max scaling for audio samples */
+};
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+};
+
+static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
+	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
+	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
+	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+	    SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S24_LE,
+
+	.rate_min = 8000,
+	.rate_max = 32000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = 48,
+
+	.period_bytes_min = 40, /* 8 khz 5 ms */
+	.period_bytes_max = 4 * PAGE_SIZE,
+	.buffer_bytes_max = 16 * PAGE_SIZE
+};
+
+static int stm32_adfsdm_get_supported_rates(struct snd_soc_dai *dai,
+					    unsigned int *rates)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+	struct stm32_dfsdm_hw_param params;
+	unsigned int max_scaling, i;
+	int ret;
+
+	*rates = 0;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		/* 
+		 * Check that clkout_freq is compatible
+		 * Try to find one solution for filter and integrator
+		 * oversampling ratio.
+		 */
+
+		params.rate = stm32_dfsdm_filter[i].freq;
+		params.sample_bits = 24;
+		params.max_scaling = &max_scaling;
+
+		ret = pdata->ops->set_hwparam(pdata->adc, &params);
+		if (!ret) {
+			*rates |= 1 << i;
+			dev_err(dai->dev, "%s: %d rate supported\n", __func__,
+				stm32_dfsdm_filter[i].freq);
+		}
+	}
+
+	if (!*rates) {
+		dev_err(dai->dev, "%s: no matched rate found\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
+			     snd_pcm_uframes_t pos,
+			     void __user *buf, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
+	ssize_t bytes = frames_to_bytes(runtime, count);
+	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
+	unsigned int shift = 24 -priv->max_scaling;
+	
+	/*
+	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
+	 * We need to mask 8 LSB control bits...
+	 * Additionnaly sample scaling depends on decimation and can need shift
+	 * to be aligned on 32-bit word MSB.
+	 */
+	if (shift > 0) {
+		do {
+			*ptr <<= shift & STM32_ADFSDM_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	} else {
+		do {
+			*ptr &= STM32_ADFSDM_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	}
+
+	return copy_to_user(buf, hwbuf, bytes);
+}
+
+static void stm32_dfsdm_xrun(void *context)
+{
+	struct snd_soc_dai *dai = context;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	snd_pcm_stream_lock(priv->substream);
+	dev_dbg(dai->dev, "%s:unexpected overrun\n", __func__);
+	/* Stop the player */
+	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
+	snd_pcm_stream_unlock(priv->substream);
+}
+
+static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = substream;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	return 0;
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  &priv->rates_const);
+}
+
+static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	priv->substream = NULL;
+}
+
+static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_dmaengine_dai_dma_data *dma_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+	struct stm32_dfsdm_hw_param df_params;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	dma_data = snd_soc_dai_get_dma_data(dai, substream);
+	dma_data->maxburst = 1;
+
+	df_params.rate = substream->runtime->rate;
+	df_params.sample_bits = substream->runtime->sample_bits;
+	df_params.max_scaling = &priv->max_scaling;
+
+	return pdata->ops->set_hwparam(pdata->adc, &df_params);
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return pdata->ops->audio_startup(pdata->adc);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		pdata->ops->audio_shutdown(pdata->adc);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	if ((cb == SND_SOC_DAIFMT_CBM_CFM) || (cb == SND_SOC_DAIFMT_CBM_CFS)) {
+		/* Digital microphone is clocked by external clock */
+		if (!priv->dmic_clk) {
+			dev_err(dai->dev,
+				"system-clock-frequency not defined\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+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);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+	if (dir == SND_SOC_CLOCK_IN) {
+		pdata->ops->set_sysclk(pdata->adc, freq);
+		priv->dmic_clk = freq;
+	}
+
+	/* Determine supported rate which depends on SPI/manchester clock */
+	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.startup = stm32_adfsdm_startup,
+	.shutdown = stm32_adfsdm_shutdown,
+	.hw_params = stm32_adfsdm_dai_hw_params,
+	.set_fmt = stm32_adfsdm_set_dai_fmt,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+	.trigger = stm32_adfsdm_trigger,
+};
+
+static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	/* DMA settings */
+	snd_soc_dai_init_dma_data(dai, NULL, dma);
+	dma->addr = pdata->ops->get_dma_source(pdata->adc);
+	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	pdata->ops->register_xrun_cb(priv->pdata->adc, stm32_dfsdm_xrun, dai);
+
+	return 0;
+}
+
+static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
+{
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S24_LE,
+		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			      SNDRV_PCM_RATE_32000),
+		    },
+	.probe = stm32_adfsdm_dai_probe,
+	.remove = stm32_adfsdm_dai_remove,
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "sti_cpu_dai",
+};
+
+static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
+	.pcm_hardware = &stm32_adfsdm_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.copy = stm32_adfsdm_copy,
+};
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	struct stm32_adfsdm_pdata *pdata = pdev->dev.platform_data;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s: enter for node %p\n", __func__,
+		pdev->dev.parent->of_node->name);
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdata = pdata;
+
+	priv->dai_drv = stm32_adfsdm_dai;
+	priv->dai_drv.name = pdev->dev.parent->of_node->name;
+	priv->dai_drv.capture.stream_name = pdev->dev.parent->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;
+
+	ret = devm_snd_dmaengine_pcm_register(pdev->dev.parent,
+					      &dmaengine_pcm_config, 0);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to register dma pcm config\n");
+
+	return ret;
+}
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.driver = {
+		   .name = STM32_ADFSDM_DRV_NAME,
+		   },
+	.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] 107+ messages in thread

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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>
---
 include/sound/stm32-adfsdm.h |  80 ++++++++++
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 ++
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 365 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 459 insertions(+)
 create mode 100644 include/sound/stm32-adfsdm.h
 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/include/sound/stm32-adfsdm.h b/include/sound/stm32-adfsdm.h
new file mode 100644
index 0000000..ff5899d
--- /dev/null
+++ b/include/sound/stm32-adfsdm.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver API
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+#ifndef STM32_ADFSDM_H
+#define STM32_ADFSDM_H
+
+struct stm32_dfsdm_adc;
+
+/**
+ * struct stm32_dfsdm_hw_param - stm32 audio hardware params
+ * @rate:		sampling rate
+ * @sample_bits:	sample size in bits
+ * @max_scaling:	effective scaling in bit computed by iio driver
+ */
+struct stm32_dfsdm_hw_param {
+	unsigned int rate;
+	unsigned int sample_bits;
+	unsigned int *max_scaling;
+};
+
+/*
+ * Potential improvement:
+ * Following structure and functions could be generic and declared in
+ * an asoc-iio.h
+ */
+struct stm32_adfsdm_codec_ops {
+	/*
+	 * Set the SPI or manchester input Frequency
+	 * Optional: not use if DFSDM is master on SPI
+	 */
+	void (*set_sysclk)(struct stm32_dfsdm_adc *adc, unsigned int freq);
+
+	/*
+	 * Set expected audio sampling rate and format.
+	 * Precision is returned to allow to rescale samples
+	 */
+	int (*set_hwparam)(struct stm32_dfsdm_adc *adc,
+			   struct stm32_dfsdm_hw_param *params);
+
+	/* Called when ASoC starts an audio stream setup. */
+	int (*audio_startup)(struct stm32_dfsdm_adc *adc);
+
+	/* Shuts down the audio stream. */
+	void (*audio_shutdown)(struct stm32_dfsdm_adc *adc);
+
+	/*
+	 * Provides DMA source physicla addr to allow ALsa to handle DMA
+	 * transfers.
+	 */
+	dma_addr_t (*get_dma_source)(struct stm32_dfsdm_adc *adc);
+
+	/* Register callback to treat overrun issues */
+	void (*register_xrun_cb)(struct stm32_dfsdm_adc *adc,
+				 void (*overrun_cb)(void *context),
+				 void *context);
+
+};
+
+/* stm32 dfsdm initalization data */
+struct stm32_adfsdm_pdata {
+	const struct stm32_adfsdm_codec_ops *ops;
+	struct stm32_dfsdm_adc *adc;
+};
+
+#define STM32_ADFSDM_DRV_NAME "stm32-dfsdm-audio"
+#endif
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..4488461
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,365 @@
+/*
+ * This file is part of STM32 DFSDM ASoC DAI driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ *          Olivier Moysan <olivier.moysan@st.com>
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/stm32-adfsdm.h>
+
+#define STM32_ADFSDM_DATA_MASK	GENMASK(31, 8)
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai_drv;
+	struct stm32_adfsdm_pdata *pdata; /* platform data set by IIO driver */
+	struct snd_dmaengine_dai_dma_data dma_data;  /* dma config */
+	struct snd_pcm_substream *substream;  
+	struct snd_pcm_hw_constraint_list rates_const;
+	unsigned long dmic_clk; /* SPI or manchester input clock frequency */
+	unsigned int fl_id;   /* filter instance ID */
+	unsigned int order; /* filter order */
+	unsigned int max_scaling;  /* max scaling for audio samples */
+};
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+};
+
+static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
+	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
+	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
+	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+	    SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S24_LE,
+
+	.rate_min = 8000,
+	.rate_max = 32000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = 48,
+
+	.period_bytes_min = 40, /* 8 khz 5 ms */
+	.period_bytes_max = 4 * PAGE_SIZE,
+	.buffer_bytes_max = 16 * PAGE_SIZE
+};
+
+static int stm32_adfsdm_get_supported_rates(struct snd_soc_dai *dai,
+					    unsigned int *rates)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+	struct stm32_dfsdm_hw_param params;
+	unsigned int max_scaling, i;
+	int ret;
+
+	*rates = 0;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		/* 
+		 * Check that clkout_freq is compatible
+		 * Try to find one solution for filter and integrator
+		 * oversampling ratio.
+		 */
+
+		params.rate = stm32_dfsdm_filter[i].freq;
+		params.sample_bits = 24;
+		params.max_scaling = &max_scaling;
+
+		ret = pdata->ops->set_hwparam(pdata->adc, &params);
+		if (!ret) {
+			*rates |= 1 << i;
+			dev_err(dai->dev, "%s: %d rate supported\n", __func__,
+				stm32_dfsdm_filter[i].freq);
+		}
+	}
+
+	if (!*rates) {
+		dev_err(dai->dev, "%s: no matched rate found\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
+			     snd_pcm_uframes_t pos,
+			     void __user *buf, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
+	ssize_t bytes = frames_to_bytes(runtime, count);
+	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
+	unsigned int shift = 24 -priv->max_scaling;
+	
+	/*
+	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
+	 * We need to mask 8 LSB control bits...
+	 * Additionnaly sample scaling depends on decimation and can need shift
+	 * to be aligned on 32-bit word MSB.
+	 */
+	if (shift > 0) {
+		do {
+			*ptr <<= shift & STM32_ADFSDM_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	} else {
+		do {
+			*ptr &= STM32_ADFSDM_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	}
+
+	return copy_to_user(buf, hwbuf, bytes);
+}
+
+static void stm32_dfsdm_xrun(void *context)
+{
+	struct snd_soc_dai *dai = context;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	snd_pcm_stream_lock(priv->substream);
+	dev_dbg(dai->dev, "%s:unexpected overrun\n", __func__);
+	/* Stop the player */
+	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
+	snd_pcm_stream_unlock(priv->substream);
+}
+
+static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = substream;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	return 0;
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  &priv->rates_const);
+}
+
+static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	priv->substream = NULL;
+}
+
+static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_dmaengine_dai_dma_data *dma_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+	struct stm32_dfsdm_hw_param df_params;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	dma_data = snd_soc_dai_get_dma_data(dai, substream);
+	dma_data->maxburst = 1;
+
+	df_params.rate = substream->runtime->rate;
+	df_params.sample_bits = substream->runtime->sample_bits;
+	df_params.max_scaling = &priv->max_scaling;
+
+	return pdata->ops->set_hwparam(pdata->adc, &df_params);
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return pdata->ops->audio_startup(pdata->adc);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		pdata->ops->audio_shutdown(pdata->adc);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	if ((cb == SND_SOC_DAIFMT_CBM_CFM) || (cb == SND_SOC_DAIFMT_CBM_CFS)) {
+		/* Digital microphone is clocked by external clock */
+		if (!priv->dmic_clk) {
+			dev_err(dai->dev,
+				"system-clock-frequency not defined\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+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);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+	if (dir == SND_SOC_CLOCK_IN) {
+		pdata->ops->set_sysclk(pdata->adc, freq);
+		priv->dmic_clk = freq;
+	}
+
+	/* Determine supported rate which depends on SPI/manchester clock */
+	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.startup = stm32_adfsdm_startup,
+	.shutdown = stm32_adfsdm_shutdown,
+	.hw_params = stm32_adfsdm_dai_hw_params,
+	.set_fmt = stm32_adfsdm_set_dai_fmt,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+	.trigger = stm32_adfsdm_trigger,
+};
+
+static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	/* DMA settings */
+	snd_soc_dai_init_dma_data(dai, NULL, dma);
+	dma->addr = pdata->ops->get_dma_source(pdata->adc);
+	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	pdata->ops->register_xrun_cb(priv->pdata->adc, stm32_dfsdm_xrun, dai);
+
+	return 0;
+}
+
+static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
+{
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S24_LE,
+		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			      SNDRV_PCM_RATE_32000),
+		    },
+	.probe = stm32_adfsdm_dai_probe,
+	.remove = stm32_adfsdm_dai_remove,
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "sti_cpu_dai",
+};
+
+static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
+	.pcm_hardware = &stm32_adfsdm_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.copy = stm32_adfsdm_copy,
+};
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	struct stm32_adfsdm_pdata *pdata = pdev->dev.platform_data;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s: enter for node %p\n", __func__,
+		pdev->dev.parent->of_node->name);
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdata = pdata;
+
+	priv->dai_drv = stm32_adfsdm_dai;
+	priv->dai_drv.name = pdev->dev.parent->of_node->name;
+	priv->dai_drv.capture.stream_name = pdev->dev.parent->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;
+
+	ret = devm_snd_dmaengine_pcm_register(pdev->dev.parent,
+					      &dmaengine_pcm_config, 0);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to register dma pcm config\n");
+
+	return ret;
+}
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.driver = {
+		   .name = STM32_ADFSDM_DRV_NAME,
+		   },
+	.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] 107+ messages in thread

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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>
---
 include/sound/stm32-adfsdm.h |  80 ++++++++++
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 ++
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 365 +++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 459 insertions(+)
 create mode 100644 include/sound/stm32-adfsdm.h
 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/include/sound/stm32-adfsdm.h b/include/sound/stm32-adfsdm.h
new file mode 100644
index 0000000..ff5899d
--- /dev/null
+++ b/include/sound/stm32-adfsdm.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver API
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+#ifndef STM32_ADFSDM_H
+#define STM32_ADFSDM_H
+
+struct stm32_dfsdm_adc;
+
+/**
+ * struct stm32_dfsdm_hw_param - stm32 audio hardware params
+ * @rate:		sampling rate
+ * @sample_bits:	sample size in bits
+ * @max_scaling:	effective scaling in bit computed by iio driver
+ */
+struct stm32_dfsdm_hw_param {
+	unsigned int rate;
+	unsigned int sample_bits;
+	unsigned int *max_scaling;
+};
+
+/*
+ * Potential improvement:
+ * Following structure and functions could be generic and declared in
+ * an asoc-iio.h
+ */
+struct stm32_adfsdm_codec_ops {
+	/*
+	 * Set the SPI or manchester input Frequency
+	 * Optional: not use if DFSDM is master on SPI
+	 */
+	void (*set_sysclk)(struct stm32_dfsdm_adc *adc, unsigned int freq);
+
+	/*
+	 * Set expected audio sampling rate and format.
+	 * Precision is returned to allow to rescale samples
+	 */
+	int (*set_hwparam)(struct stm32_dfsdm_adc *adc,
+			   struct stm32_dfsdm_hw_param *params);
+
+	/* Called when ASoC starts an audio stream setup. */
+	int (*audio_startup)(struct stm32_dfsdm_adc *adc);
+
+	/* Shuts down the audio stream. */
+	void (*audio_shutdown)(struct stm32_dfsdm_adc *adc);
+
+	/*
+	 * Provides DMA source physicla addr to allow ALsa to handle DMA
+	 * transfers.
+	 */
+	dma_addr_t (*get_dma_source)(struct stm32_dfsdm_adc *adc);
+
+	/* Register callback to treat overrun issues */
+	void (*register_xrun_cb)(struct stm32_dfsdm_adc *adc,
+				 void (*overrun_cb)(void *context),
+				 void *context);
+
+};
+
+/* stm32 dfsdm initalization data */
+struct stm32_adfsdm_pdata {
+	const struct stm32_adfsdm_codec_ops *ops;
+	struct stm32_dfsdm_adc *adc;
+};
+
+#define STM32_ADFSDM_DRV_NAME "stm32-dfsdm-audio"
+#endif
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..4488461
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,365 @@
+/*
+ * This file is part of STM32 DFSDM ASoC DAI driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ *          Olivier Moysan <olivier.moysan@st.com>
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/stm32-adfsdm.h>
+
+#define STM32_ADFSDM_DATA_MASK	GENMASK(31, 8)
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai_drv;
+	struct stm32_adfsdm_pdata *pdata; /* platform data set by IIO driver */
+	struct snd_dmaengine_dai_dma_data dma_data;  /* dma config */
+	struct snd_pcm_substream *substream;  
+	struct snd_pcm_hw_constraint_list rates_const;
+	unsigned long dmic_clk; /* SPI or manchester input clock frequency */
+	unsigned int fl_id;   /* filter instance ID */
+	unsigned int order; /* filter order */
+	unsigned int max_scaling;  /* max scaling for audio samples */
+};
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+};
+
+static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
+	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
+	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
+	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+	    SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S24_LE,
+
+	.rate_min = 8000,
+	.rate_max = 32000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = 48,
+
+	.period_bytes_min = 40, /* 8 khz 5 ms */
+	.period_bytes_max = 4 * PAGE_SIZE,
+	.buffer_bytes_max = 16 * PAGE_SIZE
+};
+
+static int stm32_adfsdm_get_supported_rates(struct snd_soc_dai *dai,
+					    unsigned int *rates)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+	struct stm32_dfsdm_hw_param params;
+	unsigned int max_scaling, i;
+	int ret;
+
+	*rates = 0;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		/* 
+		 * Check that clkout_freq is compatible
+		 * Try to find one solution for filter and integrator
+		 * oversampling ratio.
+		 */
+
+		params.rate = stm32_dfsdm_filter[i].freq;
+		params.sample_bits = 24;
+		params.max_scaling = &max_scaling;
+
+		ret = pdata->ops->set_hwparam(pdata->adc, &params);
+		if (!ret) {
+			*rates |= 1 << i;
+			dev_err(dai->dev, "%s: %d rate supported\n", __func__,
+				stm32_dfsdm_filter[i].freq);
+		}
+	}
+
+	if (!*rates) {
+		dev_err(dai->dev, "%s: no matched rate found\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
+			     snd_pcm_uframes_t pos,
+			     void __user *buf, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
+	ssize_t bytes = frames_to_bytes(runtime, count);
+	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
+	unsigned int shift = 24 -priv->max_scaling;
+	
+	/*
+	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
+	 * We need to mask 8 LSB control bits...
+	 * Additionnaly sample scaling depends on decimation and can need shift
+	 * to be aligned on 32-bit word MSB.
+	 */
+	if (shift > 0) {
+		do {
+			*ptr <<= shift & STM32_ADFSDM_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	} else {
+		do {
+			*ptr &= STM32_ADFSDM_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	}
+
+	return copy_to_user(buf, hwbuf, bytes);
+}
+
+static void stm32_dfsdm_xrun(void *context)
+{
+	struct snd_soc_dai *dai = context;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	snd_pcm_stream_lock(priv->substream);
+	dev_dbg(dai->dev, "%s:unexpected overrun\n", __func__);
+	/* Stop the player */
+	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
+	snd_pcm_stream_unlock(priv->substream);
+}
+
+static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = substream;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	return 0;
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  &priv->rates_const);
+}
+
+static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	priv->substream = NULL;
+}
+
+static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_dmaengine_dai_dma_data *dma_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+	struct stm32_dfsdm_hw_param df_params;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	dma_data = snd_soc_dai_get_dma_data(dai, substream);
+	dma_data->maxburst = 1;
+
+	df_params.rate = substream->runtime->rate;
+	df_params.sample_bits = substream->runtime->sample_bits;
+	df_params.max_scaling = &priv->max_scaling;
+
+	return pdata->ops->set_hwparam(pdata->adc, &df_params);
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return pdata->ops->audio_startup(pdata->adc);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		pdata->ops->audio_shutdown(pdata->adc);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	if ((cb == SND_SOC_DAIFMT_CBM_CFM) || (cb == SND_SOC_DAIFMT_CBM_CFS)) {
+		/* Digital microphone is clocked by external clock */
+		if (!priv->dmic_clk) {
+			dev_err(dai->dev,
+				"system-clock-frequency not defined\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+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);
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+	if (dir == SND_SOC_CLOCK_IN) {
+		pdata->ops->set_sysclk(pdata->adc, freq);
+		priv->dmic_clk = freq;
+	}
+
+	/* Determine supported rate which depends on SPI/manchester clock */
+	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.startup = stm32_adfsdm_startup,
+	.shutdown = stm32_adfsdm_shutdown,
+	.hw_params = stm32_adfsdm_dai_hw_params,
+	.set_fmt = stm32_adfsdm_set_dai_fmt,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+	.trigger = stm32_adfsdm_trigger,
+};
+
+static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
+	struct stm32_adfsdm_pdata *pdata = priv->pdata;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	/* DMA settings */
+	snd_soc_dai_init_dma_data(dai, NULL, dma);
+	dma->addr = pdata->ops->get_dma_source(pdata->adc);
+	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	pdata->ops->register_xrun_cb(priv->pdata->adc, stm32_dfsdm_xrun, dai);
+
+	return 0;
+}
+
+static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
+{
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S24_LE,
+		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			      SNDRV_PCM_RATE_32000),
+		    },
+	.probe = stm32_adfsdm_dai_probe,
+	.remove = stm32_adfsdm_dai_remove,
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "sti_cpu_dai",
+};
+
+static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
+	.pcm_hardware = &stm32_adfsdm_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.copy = stm32_adfsdm_copy,
+};
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	struct stm32_adfsdm_pdata *pdata = pdev->dev.platform_data;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s: enter for node %p\n", __func__,
+		pdev->dev.parent->of_node->name);
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdata = pdata;
+
+	priv->dai_drv = stm32_adfsdm_dai;
+	priv->dai_drv.name = pdev->dev.parent->of_node->name;
+	priv->dai_drv.capture.stream_name = pdev->dev.parent->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;
+
+	ret = devm_snd_dmaengine_pcm_register(pdev->dev.parent,
+					      &dmaengine_pcm_config, 0);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to register dma pcm config\n");
+
+	return ret;
+}
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.driver = {
+		   .name = STM32_ADFSDM_DRV_NAME,
+		   },
+	.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] 107+ messages in thread

* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
  2017-02-13 16:38 ` Arnaud Pouliquen
  (?)
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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 describe Digital Filter for Sigma Delta
Modulators. DFSDM allows to connect sigma delta
modulators and/or PDM microphones.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
 1 file changed, 125 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..83937cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
@@ -0,0 +1,125 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
+interface external sigma delta modulators to stm32 micro controlers.
+it is mainly targeted for:
+- Sigma delta modulators ( motor control, metering...)
+- PDM microphones ( audio digital microphone)
+
+It featured with up to 8 serial digital interface (SPI or Manchester) and
+up to 4 filters.
+
+Each instance of the sub-drivers uses one filter instance.
+
+Contents of a stm32 dfsdmc root node:
+-------------------------------------
+Required properties:
+- compatible: Should be "st,stm32-dfsdm-adc".
+- 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.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+
+Optional properties:
+- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
+- reg: Specifies the DFSDM filter instance.
+- 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 channels Name.
+- st,dai-filter-order:  SinC filter order from 0 to 5.
+			0: FastSinC
+			[1-5]: order 1 to 5.
+			For audio purpose it is recommended to use order 3 to 5.
+
+Required properties for "st,stm32-dfsdm-adc" compatibility:
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+- io-channels: from common iio binding. use to pipe external sigma delta
+		modulator or internal ADC output to dfsdm channel.
+
+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. Default value is 0.
+			- "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. default value is 1.
+			  - "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.
+		   to used for multi microphone synchronization.
+
+Example of a sigma delta adc purpose:
+	ads1202: simple_sd_adc@0 {
+		compatible = "sd-modulator";
+		#io-channel-cells = <1>;
+		status = "okay";
+	};
+	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 = "in0";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+			io-channels = <&ads1202 0>;
+	};
+
+Example of a pdm microphone purpose:
+	dfsdm: dfsdm@40017000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm";
+
+		dfsdm_pdm0: dfsdm-pdm@0 {
+			compatible = "st,stm32-dfsdm-pdm";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			interrupts = <110>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,adc-channels = <0>;
+			st,adc-channel-names = "pdm1";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+		};
+	}
-- 
1.9.1

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

* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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 describe Digital Filter for Sigma Delta
Modulators. DFSDM allows to connect sigma delta
modulators and/or PDM microphones.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
 1 file changed, 125 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..83937cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
@@ -0,0 +1,125 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
+interface external sigma delta modulators to stm32 micro controlers.
+it is mainly targeted for:
+- Sigma delta modulators ( motor control, metering...)
+- PDM microphones ( audio digital microphone)
+
+It featured with up to 8 serial digital interface (SPI or Manchester) and
+up to 4 filters.
+
+Each instance of the sub-drivers uses one filter instance.
+
+Contents of a stm32 dfsdmc root node:
+-------------------------------------
+Required properties:
+- compatible: Should be "st,stm32-dfsdm-adc".
+- 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.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+
+Optional properties:
+- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
+- reg: Specifies the DFSDM filter instance.
+- 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 channels Name.
+- st,dai-filter-order:  SinC filter order from 0 to 5.
+			0: FastSinC
+			[1-5]: order 1 to 5.
+			For audio purpose it is recommended to use order 3 to 5.
+
+Required properties for "st,stm32-dfsdm-adc" compatibility:
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+- io-channels: from common iio binding. use to pipe external sigma delta
+		modulator or internal ADC output to dfsdm channel.
+
+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. Default value is 0.
+			- "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. default value is 1.
+			  - "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.
+		   to used for multi microphone synchronization.
+
+Example of a sigma delta adc purpose:
+	ads1202: simple_sd_adc@0 {
+		compatible = "sd-modulator";
+		#io-channel-cells = <1>;
+		status = "okay";
+	};
+	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 = "in0";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+			io-channels = <&ads1202 0>;
+	};
+
+Example of a pdm microphone purpose:
+	dfsdm: dfsdm@40017000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm";
+
+		dfsdm_pdm0: dfsdm-pdm@0 {
+			compatible = "st,stm32-dfsdm-pdm";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			interrupts = <110>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,adc-channels = <0>;
+			st,adc-channel-names = "pdm1";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+		};
+	}
-- 
1.9.1


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

* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 UTC (permalink / raw)
  To: linux-arm-kernel

Add bindings that describe Digital Filter for Sigma Delta
Modulators. DFSDM allows to connect sigma delta
modulators and/or PDM microphones.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
 1 file changed, 125 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..83937cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
@@ -0,0 +1,125 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
+interface external sigma delta modulators to stm32 micro controlers.
+it is mainly targeted for:
+- Sigma delta modulators ( motor control, metering...)
+- PDM microphones ( audio digital microphone)
+
+It featured with up to 8 serial digital interface (SPI or Manchester) and
+up to 4 filters.
+
+Each instance of the sub-drivers uses one filter instance.
+
+Contents of a stm32 dfsdmc root node:
+-------------------------------------
+Required properties:
+- compatible: Should be "st,stm32-dfsdm-adc".
+- 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.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+
+Optional properties:
+- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
+- reg: Specifies the DFSDM filter instance.
+- 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 channels Name.
+- st,dai-filter-order:  SinC filter order from 0 to 5.
+			0: FastSinC
+			[1-5]: order 1 to 5.
+			For audio purpose it is recommended to use order 3 to 5.
+
+Required properties for "st,stm32-dfsdm-adc" compatibility:
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+- io-channels: from common iio binding. use to pipe external sigma delta
+		modulator or internal ADC output to dfsdm channel.
+
+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. Default value is 0.
+			- "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. default value is 1.
+			  - "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.
+		   to used for multi microphone synchronization.
+
+Example of a sigma delta adc purpose:
+	ads1202: simple_sd_adc at 0 {
+		compatible = "sd-modulator";
+		#io-channel-cells = <1>;
+		status = "okay";
+	};
+	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 = "in0";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+			io-channels = <&ads1202 0>;
+	};
+
+Example of a pdm microphone purpose:
+	dfsdm: dfsdm at 40017000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm";
+
+		dfsdm_pdm0: dfsdm-pdm at 0 {
+			compatible = "st,stm32-dfsdm-pdm";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			interrupts = <110>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,adc-channels = <0>;
+			st,adc-channel-names = "pdm1";
+			st,adc-channel-types = "SPI_R";
+			st,adc-channel-clk-src = "CLKOUT";
+		};
+	}
-- 
1.9.1

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

* [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
  2017-02-13 16:38 ` Arnaud Pouliquen
  (?)
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig            |  13 +
 drivers/iio/adc/Makefile           |   1 +
 drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
 5 files changed, 911 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 d4366ac..ab917b6 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -452,6 +452,19 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 dfsdm adc"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select REGMAP
+	select REGMAP_MMIO
+	select IIO_HW_CONSUMER
+	help
+	  Select this option to enable the  driver for STMicroelectronics
+	  STM32 digital filter for sigma delta converter (ADC).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-adc-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 bd67144..5bcad23 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
 obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
 obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,483 @@
+/*
+ * This file is part of STM32 DFSDM ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <sound/stm32-adfsdm.h>
+
+#include "stm32-dfsdm.h"
+
+enum stm32_dfsdm_mode {
+	DFSDM_ADC, /* ADC mode, access through IIO ABI */
+	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
+};
+
+struct stm32_dfsdm_adc {
+	struct stm32_dfsdm *common;
+
+	unsigned int fl_id;
+	unsigned int oversamp;
+	unsigned int clk_freq;
+
+	enum stm32_dfsdm_mode mode;
+	struct platform_device *audio_pdev;
+
+	void (*overrun_cb)(void *context);
+	void *cb_context;
+
+	/* Hardware consumer structure for Front End iio */
+	struct iio_hw_consumer *hwc;
+};
+
+static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
+static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
+
+struct stm32_dfsdm_adc_devdata {
+	enum stm32_dfsdm_mode mode;
+	const struct iio_info *info;
+};
+
+static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
+				unsigned int oversamp)
+{
+	/*
+	 * TODO
+	 * This function tries to compute filter oversampling and integrator
+	 * oversampling, base on oversampling ratio requested by user.
+	 */
+
+	return 0;
+};
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan, int *res)
+{
+	/* TODO: Perform conversion instead of sending fake value */
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+
+	*res = chan->channel + 0xFFFF00;
+	return 0;
+}
+
+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);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		ret = stm32_dfsdm_set_osrs(adc, 0, val);
+		if (!ret)
+			adc->oversamp = val;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (adc->mode == DFSDM_AUDIO)
+			ret = stm32_dfsdm_set_osrs(adc, 0, val);
+		else
+			ret = -EINVAL;
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	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;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (adc->hwc) {
+			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;
+		}
+
+		if (adc->hwc)
+			iio_hw_consumer_disable(adc->hwc);
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*val = adc->oversamp;
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
+	.mode = DFSDM_ADC,
+	.info = &stm32_dfsdm_info_adc,
+};
+
+const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
+	.mode = DFSDM_AUDIO,
+	.info = &stm32_dfsdm_info_audio,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	/* TODO */
+	return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
+				   unsigned int freq)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s:\n", __func__);
+
+	adc->clk_freq = freq;
+};
+
+	/* Set expected audio sampling rate */
+static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
+				   struct stm32_dfsdm_hw_param *params)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
+
+	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
+};
+
+	/* Called when ASoC starts an audio stream setup. */
+static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+
+	return 0;
+};
+
+	/* Shuts down the audio stream. */
+static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+};
+
+	/*
+	 * Provides DMA source physicla addr to allow ALsa to handle DMA
+	 * transfers.
+	 */
+static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+
+	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
+};
+
+/* Register callback to treat underrun and overrun issues */
+static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
+					 void (*overrun_cb)(void *context),
+					 void *context)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+	adc->overrun_cb = overrun_cb;
+	adc->cb_context = context;
+};
+
+const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
+	.set_sysclk = stm32_dfsdm_set_sysclk,
+	.set_hwparam = stm32_dfsdm_set_hwparam,
+	.audio_startup = stm32_dfsdm_audio_startup,
+	.audio_shutdown = stm32_dfsdm_audio_shutdown,
+	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
+	.get_dma_source = stm32_dfsdm_get_dma_source
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int chan_idx)
+{
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channels", chan_idx,
+					 &ch->channel);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channels' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-names", chan_idx,
+					    &ch->datasheet_name);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channel-names' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = chan_idx;
+	if (adc->mode == DFSDM_ADC) {
+		/*
+		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
+		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
+		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
+		 */
+		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+	}
+
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+
+	return 0;
+}
+
+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->common->num_chs) {
+		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+		return num_ch < 0 ? num_ch : -EINVAL;
+	}
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	if (adc->mode == DFSDM_ADC) {
+		/*
+		 * Bind to sd modulator iio device for ADC only.
+		 * For Audio the PDM microphone will be handled by ASoC
+		 */
+		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
+		if (IS_ERR(adc->hwc)) {
+			dev_err(&indio_dev->dev, "no backend found\n");
+			return PTR_ERR(adc->hwc);
+		}
+	}
+
+	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:
+	if (adc->hwc)
+		iio_hw_consumer_free(adc->hwc);
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc",
+	  .data = &stm32_dfsdm_devdata_adc},
+	{ .compatible = "st,stm32-dfsdm-pdm",
+	  .data = &stm32_dfsdm_devdata_audio},
+	{}
+};
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	const struct of_device_id *of_id;
+	struct device_node *np = dev->of_node;
+	const struct stm32_dfsdm_adc_devdata *devdata;
+	struct iio_dev *iio;
+	int ret, irq;
+
+	dev_dbg(dev, "%s:\n", __func__);
+
+	iio = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: failed to allocate iio", __func__);
+		return PTR_ERR(iio);
+	}
+
+	adc = iio_priv(iio);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: failed to allocate adc", __func__);
+		return PTR_ERR(adc);
+	}
+	adc->common = dev_get_drvdata(dev->parent);
+
+	/* Populate data structure depending on compatibility */
+	of_id = of_match_node(stm32_dfsdm_adc_match, np);
+	if (!of_id->data) {
+		dev_err(&pdev->dev, "Data associated to device is missing\n");
+		return -EINVAL;
+	}
+
+	devdata = (const struct stm32_dfsdm_adc_devdata *)of_id->data;
+	adc->mode = devdata->mode;
+
+	iio->name = np->name;
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = devdata->info;
+	iio->modes = INDIO_DIRECT_MODE;
+
+	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;
+	}
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 * In a second step IRQ domain should be used for filter 0 when feature
+	 * like Watchdog, clock absence detection,... will be integrated.
+	 */
+	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 = stm32_dfsdm_adc_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	ret = iio_device_register(iio);
+	if (ret) {
+		dev_err(dev, "failed to register iio device\n");
+		return ret;
+	}
+
+	if (adc->mode == DFSDM_AUDIO) {
+		struct stm32_adfsdm_pdata dai_data = {
+			.ops = &stm32_dfsdm_audio_ops,
+			.adc = adc,
+		};
+
+		adc->audio_pdev = platform_device_register_data(
+						dev, STM32_ADFSDM_DRV_NAME,
+						PLATFORM_DEVID_AUTO,
+						&dai_data, sizeof(dai_data));
+
+		if (IS_ERR(adc->audio_pdev))
+			return PTR_ERR(adc->audio_pdev);
+	}
+
+	return 0;
+}
+
+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..195245d
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -0,0 +1,273 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "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
+
+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 -  stm32 dfsdm  private data
+ * @pdev:		platform device
+ * @stm32_dfsdm:	common data exported for all instances
+ * @regmap:		register map of the device;
+ * @clkout_div:		SPI clkout divider value.
+ * @n_active_ch:	atomic active channel counter.
+ */
+struct dfsdm_priv {
+	struct platform_device *pdev;
+
+	struct stm32_dfsdm dfsdm;
+	struct regmap *regmap;
+
+	unsigned int clkout_div;
+	atomic_t n_active_ch;
+};
+
+/**
+ * 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);
+	int ret;
+	int div = priv->clkout_div;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		/* TODO: enable clocks */
+
+		/* Output the SPI CLKOUT (if clkout_div == 0 clok if OFF) */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(div));
+		if (ret < 0)
+			return ret;
+
+		/* Global enable of DFSDM interface */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(1));
+		if (ret < 0)
+			return ret;
+	}
+
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+}
+
+/**
+ * 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(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(0));
+		if (ret < 0)
+			return ret;
+
+		/* Stop SPI CLKOUT */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(0));
+		if (ret < 0)
+			return ret;
+
+		/* TODO: disable clocks */
+	}
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	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;
+
+	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);
+
+	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_kzalloc(&pdev->dev, sizeof(*dfsdm->fl_list),
+				      GFP_KERNEL);
+	if (!dfsdm->fl_list)
+		return -ENOMEM;
+
+	dfsdm->num_fls = dev_data->num_filters;
+	dfsdm->ch_list = devm_kzalloc(&pdev->dev, sizeof(*dfsdm->ch_list),
+				      GFP_KERNEL);
+	if (!dfsdm->ch_list)
+		return -ENOMEM;
+	dfsdm->num_chs = dev_data->num_channels;
+		dev_err(&pdev->dev, "%s: dfsdm->num_ch: %d\n",
+			__func__, dfsdm->num_chs);
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
+					    &stm32h7_dfsdm_regmap_cfg);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	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);
+
+	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..38ab15e
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -0,0 +1,141 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+#ifndef MDF_STM32_DFSDM__H
+#define MDF_STM32_DFSDM__H
+
+#include <linux/bitfield.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)
+
+/*
+ * 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)
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_filter {
+	unsigned int id;
+};
+
+/**
+ * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;
+};
+
+/**
+ * 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 stm32_dfsdm_filter *fl_list;
+	int num_fls;
+	struct stm32_dfsdm_channel *ch_list;
+	int num_chs;
+};
+
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
+
+#endif
-- 
1.9.1

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

* [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig            |  13 +
 drivers/iio/adc/Makefile           |   1 +
 drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
 5 files changed, 911 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 d4366ac..ab917b6 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -452,6 +452,19 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 dfsdm adc"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select REGMAP
+	select REGMAP_MMIO
+	select IIO_HW_CONSUMER
+	help
+	  Select this option to enable the  driver for STMicroelectronics
+	  STM32 digital filter for sigma delta converter (ADC).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-adc-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 bd67144..5bcad23 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
 obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
 obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,483 @@
+/*
+ * This file is part of STM32 DFSDM ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <sound/stm32-adfsdm.h>
+
+#include "stm32-dfsdm.h"
+
+enum stm32_dfsdm_mode {
+	DFSDM_ADC, /* ADC mode, access through IIO ABI */
+	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
+};
+
+struct stm32_dfsdm_adc {
+	struct stm32_dfsdm *common;
+
+	unsigned int fl_id;
+	unsigned int oversamp;
+	unsigned int clk_freq;
+
+	enum stm32_dfsdm_mode mode;
+	struct platform_device *audio_pdev;
+
+	void (*overrun_cb)(void *context);
+	void *cb_context;
+
+	/* Hardware consumer structure for Front End iio */
+	struct iio_hw_consumer *hwc;
+};
+
+static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
+static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
+
+struct stm32_dfsdm_adc_devdata {
+	enum stm32_dfsdm_mode mode;
+	const struct iio_info *info;
+};
+
+static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
+				unsigned int oversamp)
+{
+	/*
+	 * TODO
+	 * This function tries to compute filter oversampling and integrator
+	 * oversampling, base on oversampling ratio requested by user.
+	 */
+
+	return 0;
+};
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan, int *res)
+{
+	/* TODO: Perform conversion instead of sending fake value */
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+
+	*res = chan->channel + 0xFFFF00;
+	return 0;
+}
+
+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);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		ret = stm32_dfsdm_set_osrs(adc, 0, val);
+		if (!ret)
+			adc->oversamp = val;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (adc->mode == DFSDM_AUDIO)
+			ret = stm32_dfsdm_set_osrs(adc, 0, val);
+		else
+			ret = -EINVAL;
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	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;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (adc->hwc) {
+			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;
+		}
+
+		if (adc->hwc)
+			iio_hw_consumer_disable(adc->hwc);
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*val = adc->oversamp;
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
+	.mode = DFSDM_ADC,
+	.info = &stm32_dfsdm_info_adc,
+};
+
+const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
+	.mode = DFSDM_AUDIO,
+	.info = &stm32_dfsdm_info_audio,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	/* TODO */
+	return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
+				   unsigned int freq)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s:\n", __func__);
+
+	adc->clk_freq = freq;
+};
+
+	/* Set expected audio sampling rate */
+static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
+				   struct stm32_dfsdm_hw_param *params)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
+
+	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
+};
+
+	/* Called when ASoC starts an audio stream setup. */
+static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+
+	return 0;
+};
+
+	/* Shuts down the audio stream. */
+static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+};
+
+	/*
+	 * Provides DMA source physicla addr to allow ALsa to handle DMA
+	 * transfers.
+	 */
+static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+
+	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
+};
+
+/* Register callback to treat underrun and overrun issues */
+static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
+					 void (*overrun_cb)(void *context),
+					 void *context)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+	adc->overrun_cb = overrun_cb;
+	adc->cb_context = context;
+};
+
+const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
+	.set_sysclk = stm32_dfsdm_set_sysclk,
+	.set_hwparam = stm32_dfsdm_set_hwparam,
+	.audio_startup = stm32_dfsdm_audio_startup,
+	.audio_shutdown = stm32_dfsdm_audio_shutdown,
+	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
+	.get_dma_source = stm32_dfsdm_get_dma_source
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int chan_idx)
+{
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channels", chan_idx,
+					 &ch->channel);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channels' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-names", chan_idx,
+					    &ch->datasheet_name);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channel-names' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = chan_idx;
+	if (adc->mode == DFSDM_ADC) {
+		/*
+		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
+		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
+		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
+		 */
+		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+	}
+
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+
+	return 0;
+}
+
+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->common->num_chs) {
+		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+		return num_ch < 0 ? num_ch : -EINVAL;
+	}
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	if (adc->mode == DFSDM_ADC) {
+		/*
+		 * Bind to sd modulator iio device for ADC only.
+		 * For Audio the PDM microphone will be handled by ASoC
+		 */
+		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
+		if (IS_ERR(adc->hwc)) {
+			dev_err(&indio_dev->dev, "no backend found\n");
+			return PTR_ERR(adc->hwc);
+		}
+	}
+
+	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:
+	if (adc->hwc)
+		iio_hw_consumer_free(adc->hwc);
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc",
+	  .data = &stm32_dfsdm_devdata_adc},
+	{ .compatible = "st,stm32-dfsdm-pdm",
+	  .data = &stm32_dfsdm_devdata_audio},
+	{}
+};
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	const struct of_device_id *of_id;
+	struct device_node *np = dev->of_node;
+	const struct stm32_dfsdm_adc_devdata *devdata;
+	struct iio_dev *iio;
+	int ret, irq;
+
+	dev_dbg(dev, "%s:\n", __func__);
+
+	iio = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: failed to allocate iio", __func__);
+		return PTR_ERR(iio);
+	}
+
+	adc = iio_priv(iio);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: failed to allocate adc", __func__);
+		return PTR_ERR(adc);
+	}
+	adc->common = dev_get_drvdata(dev->parent);
+
+	/* Populate data structure depending on compatibility */
+	of_id = of_match_node(stm32_dfsdm_adc_match, np);
+	if (!of_id->data) {
+		dev_err(&pdev->dev, "Data associated to device is missing\n");
+		return -EINVAL;
+	}
+
+	devdata = (const struct stm32_dfsdm_adc_devdata *)of_id->data;
+	adc->mode = devdata->mode;
+
+	iio->name = np->name;
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = devdata->info;
+	iio->modes = INDIO_DIRECT_MODE;
+
+	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;
+	}
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 * In a second step IRQ domain should be used for filter 0 when feature
+	 * like Watchdog, clock absence detection,... will be integrated.
+	 */
+	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 = stm32_dfsdm_adc_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	ret = iio_device_register(iio);
+	if (ret) {
+		dev_err(dev, "failed to register iio device\n");
+		return ret;
+	}
+
+	if (adc->mode == DFSDM_AUDIO) {
+		struct stm32_adfsdm_pdata dai_data = {
+			.ops = &stm32_dfsdm_audio_ops,
+			.adc = adc,
+		};
+
+		adc->audio_pdev = platform_device_register_data(
+						dev, STM32_ADFSDM_DRV_NAME,
+						PLATFORM_DEVID_AUTO,
+						&dai_data, sizeof(dai_data));
+
+		if (IS_ERR(adc->audio_pdev))
+			return PTR_ERR(adc->audio_pdev);
+	}
+
+	return 0;
+}
+
+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..195245d
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -0,0 +1,273 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "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
+
+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 -  stm32 dfsdm  private data
+ * @pdev:		platform device
+ * @stm32_dfsdm:	common data exported for all instances
+ * @regmap:		register map of the device;
+ * @clkout_div:		SPI clkout divider value.
+ * @n_active_ch:	atomic active channel counter.
+ */
+struct dfsdm_priv {
+	struct platform_device *pdev;
+
+	struct stm32_dfsdm dfsdm;
+	struct regmap *regmap;
+
+	unsigned int clkout_div;
+	atomic_t n_active_ch;
+};
+
+/**
+ * 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);
+	int ret;
+	int div = priv->clkout_div;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		/* TODO: enable clocks */
+
+		/* Output the SPI CLKOUT (if clkout_div == 0 clok if OFF) */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(div));
+		if (ret < 0)
+			return ret;
+
+		/* Global enable of DFSDM interface */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(1));
+		if (ret < 0)
+			return ret;
+	}
+
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+}
+
+/**
+ * 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(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(0));
+		if (ret < 0)
+			return ret;
+
+		/* Stop SPI CLKOUT */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(0));
+		if (ret < 0)
+			return ret;
+
+		/* TODO: disable clocks */
+	}
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	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;
+
+	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);
+
+	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_kzalloc(&pdev->dev, sizeof(*dfsdm->fl_list),
+				      GFP_KERNEL);
+	if (!dfsdm->fl_list)
+		return -ENOMEM;
+
+	dfsdm->num_fls = dev_data->num_filters;
+	dfsdm->ch_list = devm_kzalloc(&pdev->dev, sizeof(*dfsdm->ch_list),
+				      GFP_KERNEL);
+	if (!dfsdm->ch_list)
+		return -ENOMEM;
+	dfsdm->num_chs = dev_data->num_channels;
+		dev_err(&pdev->dev, "%s: dfsdm->num_ch: %d\n",
+			__func__, dfsdm->num_chs);
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
+					    &stm32h7_dfsdm_regmap_cfg);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	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);
+
+	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..38ab15e
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -0,0 +1,141 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+#ifndef MDF_STM32_DFSDM__H
+#define MDF_STM32_DFSDM__H
+
+#include <linux/bitfield.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)
+
+/*
+ * 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)
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_filter {
+	unsigned int id;
+};
+
+/**
+ * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;
+};
+
+/**
+ * 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 stm32_dfsdm_filter *fl_list;
+	int num_fls;
+	struct stm32_dfsdm_channel *ch_list;
+	int num_chs;
+};
+
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
+
+#endif
-- 
1.9.1


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

* [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
@ 2017-02-13 16:38   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-13 16:38 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.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig            |  13 +
 drivers/iio/adc/Makefile           |   1 +
 drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
 drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
 5 files changed, 911 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 d4366ac..ab917b6 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -452,6 +452,19 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 dfsdm adc"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select REGMAP
+	select REGMAP_MMIO
+	select IIO_HW_CONSUMER
+	help
+	  Select this option to enable the  driver for STMicroelectronics
+	  STM32 digital filter for sigma delta converter (ADC).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-adc-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 bd67144..5bcad23 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
 obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
 obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,483 @@
+/*
+ * This file is part of STM32 DFSDM ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/hw_consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <sound/stm32-adfsdm.h>
+
+#include "stm32-dfsdm.h"
+
+enum stm32_dfsdm_mode {
+	DFSDM_ADC, /* ADC mode, access through IIO ABI */
+	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
+};
+
+struct stm32_dfsdm_adc {
+	struct stm32_dfsdm *common;
+
+	unsigned int fl_id;
+	unsigned int oversamp;
+	unsigned int clk_freq;
+
+	enum stm32_dfsdm_mode mode;
+	struct platform_device *audio_pdev;
+
+	void (*overrun_cb)(void *context);
+	void *cb_context;
+
+	/* Hardware consumer structure for Front End iio */
+	struct iio_hw_consumer *hwc;
+};
+
+static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
+static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
+
+struct stm32_dfsdm_adc_devdata {
+	enum stm32_dfsdm_mode mode;
+	const struct iio_info *info;
+};
+
+static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
+				unsigned int oversamp)
+{
+	/*
+	 * TODO
+	 * This function tries to compute filter oversampling and integrator
+	 * oversampling, base on oversampling ratio requested by user.
+	 */
+
+	return 0;
+};
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan, int *res)
+{
+	/* TODO: Perform conversion instead of sending fake value */
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+
+	*res = chan->channel + 0xFFFF00;
+	return 0;
+}
+
+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);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		ret = stm32_dfsdm_set_osrs(adc, 0, val);
+		if (!ret)
+			adc->oversamp = val;
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		if (adc->mode == DFSDM_AUDIO)
+			ret = stm32_dfsdm_set_osrs(adc, 0, val);
+		else
+			ret = -EINVAL;
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	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;
+
+	dev_dbg(&indio_dev->dev, "%s\n", __func__);
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (adc->hwc) {
+			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;
+		}
+
+		if (adc->hwc)
+			iio_hw_consumer_disable(adc->hwc);
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*val = adc->oversamp;
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
+	.mode = DFSDM_ADC,
+	.info = &stm32_dfsdm_info_adc,
+};
+
+const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
+	.mode = DFSDM_AUDIO,
+	.info = &stm32_dfsdm_info_audio,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	/* TODO */
+	return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
+				   unsigned int freq)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s:\n", __func__);
+
+	adc->clk_freq = freq;
+};
+
+	/* Set expected audio sampling rate */
+static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
+				   struct stm32_dfsdm_hw_param *params)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
+
+	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
+};
+
+	/* Called when ASoC starts an audio stream setup. */
+static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+
+	return 0;
+};
+
+	/* Shuts down the audio stream. */
+static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+};
+
+	/*
+	 * Provides DMA source physicla addr to allow ALsa to handle DMA
+	 * transfers.
+	 */
+static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+
+	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
+};
+
+/* Register callback to treat underrun and overrun issues */
+static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
+					 void (*overrun_cb)(void *context),
+					 void *context)
+{
+	struct iio_dev *iio = iio_priv_to_dev(adc);
+
+	dev_dbg(&iio->dev, "%s\n", __func__);
+	adc->overrun_cb = overrun_cb;
+	adc->cb_context = context;
+};
+
+const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
+	.set_sysclk = stm32_dfsdm_set_sysclk,
+	.set_hwparam = stm32_dfsdm_set_hwparam,
+	.audio_startup = stm32_dfsdm_audio_startup,
+	.audio_shutdown = stm32_dfsdm_audio_shutdown,
+	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
+	.get_dma_source = stm32_dfsdm_get_dma_source
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int chan_idx)
+{
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channels", chan_idx,
+					 &ch->channel);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channels' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-names", chan_idx,
+					    &ch->datasheet_name);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channel-names' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = chan_idx;
+	if (adc->mode == DFSDM_ADC) {
+		/*
+		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
+		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
+		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
+		 */
+		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+	}
+
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = 24;
+	ch->scan_type.storagebits = 32;
+
+	return 0;
+}
+
+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->common->num_chs) {
+		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+		return num_ch < 0 ? num_ch : -EINVAL;
+	}
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	if (adc->mode == DFSDM_ADC) {
+		/*
+		 * Bind to sd modulator iio device for ADC only.
+		 * For Audio the PDM microphone will be handled by ASoC
+		 */
+		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
+		if (IS_ERR(adc->hwc)) {
+			dev_err(&indio_dev->dev, "no backend found\n");
+			return PTR_ERR(adc->hwc);
+		}
+	}
+
+	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:
+	if (adc->hwc)
+		iio_hw_consumer_free(adc->hwc);
+	return ret;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc",
+	  .data = &stm32_dfsdm_devdata_adc},
+	{ .compatible = "st,stm32-dfsdm-pdm",
+	  .data = &stm32_dfsdm_devdata_audio},
+	{}
+};
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	const struct of_device_id *of_id;
+	struct device_node *np = dev->of_node;
+	const struct stm32_dfsdm_adc_devdata *devdata;
+	struct iio_dev *iio;
+	int ret, irq;
+
+	dev_dbg(dev, "%s:\n", __func__);
+
+	iio = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(iio)) {
+		dev_err(dev, "%s: failed to allocate iio", __func__);
+		return PTR_ERR(iio);
+	}
+
+	adc = iio_priv(iio);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: failed to allocate adc", __func__);
+		return PTR_ERR(adc);
+	}
+	adc->common = dev_get_drvdata(dev->parent);
+
+	/* Populate data structure depending on compatibility */
+	of_id = of_match_node(stm32_dfsdm_adc_match, np);
+	if (!of_id->data) {
+		dev_err(&pdev->dev, "Data associated to device is missing\n");
+		return -EINVAL;
+	}
+
+	devdata = (const struct stm32_dfsdm_adc_devdata *)of_id->data;
+	adc->mode = devdata->mode;
+
+	iio->name = np->name;
+	iio->dev.parent = dev;
+	iio->dev.of_node = np;
+	iio->info = devdata->info;
+	iio->modes = INDIO_DIRECT_MODE;
+
+	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;
+	}
+
+	/*
+	 * In a first step IRQs generated for channels are not treated.
+	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+	 * In a second step IRQ domain should be used for filter 0 when feature
+	 * like Watchdog, clock absence detection,... will be integrated.
+	 */
+	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 = stm32_dfsdm_adc_chan_init(iio);
+	if (ret < 0)
+		return ret;
+
+	ret = iio_device_register(iio);
+	if (ret) {
+		dev_err(dev, "failed to register iio device\n");
+		return ret;
+	}
+
+	if (adc->mode == DFSDM_AUDIO) {
+		struct stm32_adfsdm_pdata dai_data = {
+			.ops = &stm32_dfsdm_audio_ops,
+			.adc = adc,
+		};
+
+		adc->audio_pdev = platform_device_register_data(
+						dev, STM32_ADFSDM_DRV_NAME,
+						PLATFORM_DEVID_AUTO,
+						&dai_data, sizeof(dai_data));
+
+		if (IS_ERR(adc->audio_pdev))
+			return PTR_ERR(adc->audio_pdev);
+	}
+
+	return 0;
+}
+
+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..195245d
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -0,0 +1,273 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "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
+
+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 -  stm32 dfsdm  private data
+ * @pdev:		platform device
+ * @stm32_dfsdm:	common data exported for all instances
+ * @regmap:		register map of the device;
+ * @clkout_div:		SPI clkout divider value.
+ * @n_active_ch:	atomic active channel counter.
+ */
+struct dfsdm_priv {
+	struct platform_device *pdev;
+
+	struct stm32_dfsdm dfsdm;
+	struct regmap *regmap;
+
+	unsigned int clkout_div;
+	atomic_t n_active_ch;
+};
+
+/**
+ * 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);
+	int ret;
+	int div = priv->clkout_div;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		/* TODO: enable clocks */
+
+		/* Output the SPI CLKOUT (if clkout_div == 0 clok if OFF) */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(div));
+		if (ret < 0)
+			return ret;
+
+		/* Global enable of DFSDM interface */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(1));
+		if (ret < 0)
+			return ret;
+	}
+
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+}
+
+/**
+ * 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(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_DFSDMEN_MASK,
+					 DFSDM_CHCFGR1_DFSDMEN(0));
+		if (ret < 0)
+			return ret;
+
+		/* Stop SPI CLKOUT */
+		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
+					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
+					 DFSDM_CHCFGR1_CKOUTDIV(0));
+		if (ret < 0)
+			return ret;
+
+		/* TODO: disable clocks */
+	}
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	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;
+
+	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);
+
+	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_kzalloc(&pdev->dev, sizeof(*dfsdm->fl_list),
+				      GFP_KERNEL);
+	if (!dfsdm->fl_list)
+		return -ENOMEM;
+
+	dfsdm->num_fls = dev_data->num_filters;
+	dfsdm->ch_list = devm_kzalloc(&pdev->dev, sizeof(*dfsdm->ch_list),
+				      GFP_KERNEL);
+	if (!dfsdm->ch_list)
+		return -ENOMEM;
+	dfsdm->num_chs = dev_data->num_channels;
+		dev_err(&pdev->dev, "%s: dfsdm->num_ch: %d\n",
+			__func__, dfsdm->num_chs);
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
+					    &stm32h7_dfsdm_regmap_cfg);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	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);
+
+	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..38ab15e
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -0,0 +1,141 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+#ifndef MDF_STM32_DFSDM__H
+#define MDF_STM32_DFSDM__H
+
+#include <linux/bitfield.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)
+
+/*
+ * 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)
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_filter {
+	unsigned int id;
+};
+
+/**
+ * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
+ * TODO: complete structure.
+ * @id:		filetr ID,
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;
+};
+
+/**
+ * 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 stm32_dfsdm_filter *fl_list;
+	int num_fls;
+	struct stm32_dfsdm_channel *ch_list;
+	int num_chs;
+};
+
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
+
+#endif
-- 
1.9.1

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
  2017-02-13 16:38   ` Arnaud Pouliquen
@ 2017-02-13 18:05       ` Peter Meerwald-Stadler
  -1 siblings, 0 replies; 107+ messages in thread
From: Peter Meerwald-Stadler @ 2017-02-13 18:05 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Jonathan Cameron, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA

On Mon, 13 Feb 2017, Arnaud Pouliquen wrote:

> Add bindings that describe Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators and/or PDM microphones.

nitpicking on typos below
 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>  1 file changed, 125 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..83937cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,125 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
> +interface external sigma delta modulators to stm32 micro controlers.

controllers

> +it is mainly targeted for:

It

> +- Sigma delta modulators ( motor control, metering...)

(motor

> +- PDM microphones ( audio digital microphone)

(audio

> +
> +It featured with up to 8 serial digital interface (SPI or Manchester) and

It features up

> +up to 4 filters.
> +
> +Each instance of the sub-drivers uses one filter instance.
> +
> +Contents of a stm32 dfsdmc root node:

what is the c in dfsdmc?

> +-------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32-dfsdm-adc".
> +- 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.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +
> +Optional properties:
> +- st,clkout-freq: needed for SPI master mode
> +		  SPI clock OUT frequency (Hz).This clock must be set according

. This

> +		  to "clock" property. Frequency must be a multiple of the rcc
> +		  clock frequency. If not, 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-pdm" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance.
> +- 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 channels Name.

channel names

> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- io-channels: from common iio binding. use to pipe external sigma delta
> +		modulator or internal ADC output to dfsdm channel.
> +
> +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")

(specified

> +- dma-names: must be "rx"
> +
> +Optional properties:
> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
> +			- "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. default value is 1.

Default

> +			  - "CLKIN": External SPI clock (CLKIN x)

external

> +			  - "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

two

> +			  connected on same SPI input.
> +			  If not set channel n is connected to SPI input n.

If not set, 

> +			  If set channel n is connected to SPI input n + 1.

If set, 

> +
> +- st,filter0-sync: Set to 1 to synchronize with DFSDM filter instance 0.
> +		   to used for multi microphone synchronization.

Used for

> +
> +Example of a sigma delta adc purpose:
> +	ads1202: simple_sd_adc@0 {
> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +		status = "okay";
> +	};
> +	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 = "in0";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +			io-channels = <&ads1202 0>;
> +	};
> +
> +Example of a pdm microphone purpose:
> +	dfsdm: dfsdm@40017000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm";
> +
> +		dfsdm_pdm0: dfsdm-pdm@0 {
> +			compatible = "st,stm32-dfsdm-pdm";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			interrupts = <110>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,adc-channels = <0>;
> +			st,adc-channel-names = "pdm1";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +		};
> +	}
> 

-- 

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

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-13 18:05       ` Peter Meerwald-Stadler
  0 siblings, 0 replies; 107+ messages in thread
From: Peter Meerwald-Stadler @ 2017-02-13 18:05 UTC (permalink / raw)
  To: Arnaud Pouliquen; +Cc: Rob Herring, Jonathan Cameron, devicetree, linux-iio

On Mon, 13 Feb 2017, Arnaud Pouliquen wrote:

> Add bindings that describe Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators and/or PDM microphones.

nitpicking on typos below
 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>  1 file changed, 125 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..83937cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,125 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
> +interface external sigma delta modulators to stm32 micro controlers.

controllers

> +it is mainly targeted for:

It

> +- Sigma delta modulators ( motor control, metering...)

(motor

> +- PDM microphones ( audio digital microphone)

(audio

> +
> +It featured with up to 8 serial digital interface (SPI or Manchester) and

It features up

> +up to 4 filters.
> +
> +Each instance of the sub-drivers uses one filter instance.
> +
> +Contents of a stm32 dfsdmc root node:

what is the c in dfsdmc?

> +-------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32-dfsdm-adc".
> +- 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.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +
> +Optional properties:
> +- st,clkout-freq: needed for SPI master mode
> +		  SPI clock OUT frequency (Hz).This clock must be set according

. This

> +		  to "clock" property. Frequency must be a multiple of the rcc
> +		  clock frequency. If not, 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-pdm" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance.
> +- 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 channels Name.

channel names

> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- io-channels: from common iio binding. use to pipe external sigma delta
> +		modulator or internal ADC output to dfsdm channel.
> +
> +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")

(specified

> +- dma-names: must be "rx"
> +
> +Optional properties:
> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
> +			- "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. default value is 1.

Default

> +			  - "CLKIN": External SPI clock (CLKIN x)

external

> +			  - "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

two

> +			  connected on same SPI input.
> +			  If not set channel n is connected to SPI input n.

If not set, 

> +			  If set channel n is connected to SPI input n + 1.

If set, 

> +
> +- st,filter0-sync: Set to 1 to synchronize with DFSDM filter instance 0.
> +		   to used for multi microphone synchronization.

Used for

> +
> +Example of a sigma delta adc purpose:
> +	ads1202: simple_sd_adc@0 {
> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +		status = "okay";
> +	};
> +	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 = "in0";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +			io-channels = <&ads1202 0>;
> +	};
> +
> +Example of a pdm microphone purpose:
> +	dfsdm: dfsdm@40017000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm";
> +
> +		dfsdm_pdm0: dfsdm-pdm@0 {
> +			compatible = "st,stm32-dfsdm-pdm";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			interrupts = <110>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,adc-channels = <0>;
> +			st,adc-channel-names = "pdm1";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +		};
> +	}
> 

-- 

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

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-13 16:38   ` Arnaud Pouliquen
@ 2017-02-13 18:13       ` Peter Meerwald-Stadler
  -1 siblings, 0 replies; 107+ messages in thread
From: Peter Meerwald-Stadler @ 2017-02-13 18:13 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Jonathan Cameron, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA

On Mon, 13 Feb 2017, Arnaud Pouliquen wrote:

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

Modulators
more typos below
 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> ---
>  include/sound/stm32-adfsdm.h |  80 ++++++++++
>  sound/soc/Kconfig            |   1 +
>  sound/soc/Makefile           |   1 +
>  sound/soc/stm/Kconfig        |  10 ++
>  sound/soc/stm/Makefile       |   2 +
>  sound/soc/stm/stm32_adfsdm.c | 365 +++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 459 insertions(+)
>  create mode 100644 include/sound/stm32-adfsdm.h
>  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/include/sound/stm32-adfsdm.h b/include/sound/stm32-adfsdm.h
> new file mode 100644
> index 0000000..ff5899d
> --- /dev/null
> +++ b/include/sound/stm32-adfsdm.h
> @@ -0,0 +1,80 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver API
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
> + *
> + * License terms: GPL V2.0.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + */
> +#ifndef STM32_ADFSDM_H
> +#define STM32_ADFSDM_H
> +
> +struct stm32_dfsdm_adc;
> +
> +/**
> + * struct stm32_dfsdm_hw_param - stm32 audio hardware params
> + * @rate:		sampling rate
> + * @sample_bits:	sample size in bits
> + * @max_scaling:	effective scaling in bit computed by iio driver

bits?

> + */
> +struct stm32_dfsdm_hw_param {
> +	unsigned int rate;
> +	unsigned int sample_bits;
> +	unsigned int *max_scaling;
> +};
> +
> +/*
> + * Potential improvement:
> + * Following structure and functions could be generic and declared in
> + * an asoc-iio.h
> + */
> +struct stm32_adfsdm_codec_ops {
> +	/*
> +	 * Set the SPI or manchester input Frequency

frequency

> +	 * Optional: not use if DFSDM is master on SPI
> +	 */
> +	void (*set_sysclk)(struct stm32_dfsdm_adc *adc, unsigned int freq);
> +
> +	/*
> +	 * Set expected audio sampling rate and format.
> +	 * Precision is returned to allow to rescale samples
> +	 */
> +	int (*set_hwparam)(struct stm32_dfsdm_adc *adc,
> +			   struct stm32_dfsdm_hw_param *params);
> +
> +	/* Called when ASoC starts an audio stream setup. */
> +	int (*audio_startup)(struct stm32_dfsdm_adc *adc);
> +
> +	/* Shuts down the audio stream. */
> +	void (*audio_shutdown)(struct stm32_dfsdm_adc *adc);
> +
> +	/*
> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA

physical
ALSA

> +	 * transfers.
> +	 */
> +	dma_addr_t (*get_dma_source)(struct stm32_dfsdm_adc *adc);
> +
> +	/* Register callback to treat overrun issues */
> +	void (*register_xrun_cb)(struct stm32_dfsdm_adc *adc,
> +				 void (*overrun_cb)(void *context),
> +				 void *context);
> +
> +};
> +
> +/* stm32 dfsdm initalization data */
> +struct stm32_adfsdm_pdata {
> +	const struct stm32_adfsdm_codec_ops *ops;
> +	struct stm32_dfsdm_adc *adc;
> +};
> +
> +#define STM32_ADFSDM_DRV_NAME "stm32-dfsdm-audio"
> +#endif
> 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..4488461
> --- /dev/null
> +++ b/sound/soc/stm/stm32_adfsdm.c
> @@ -0,0 +1,365 @@
> +/*
> + * This file is part of STM32 DFSDM ASoC DAI driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Authors: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> + *          Olivier Moysan <olivier.moysan-qxv4g6HH51o@public.gmane.org>
> + *
> + * License type: GPLv2
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.
> + * See the GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <sound/dmaengine_pcm.h>
> +#include <sound/stm32-adfsdm.h>
> +
> +#define STM32_ADFSDM_DATA_MASK	GENMASK(31, 8)
> +
> +struct stm32_adfsdm_priv {
> +	struct snd_soc_dai_driver dai_drv;
> +	struct stm32_adfsdm_pdata *pdata; /* platform data set by IIO driver */
> +	struct snd_dmaengine_dai_dma_data dma_data;  /* dma config */
> +	struct snd_pcm_substream *substream;  
> +	struct snd_pcm_hw_constraint_list rates_const;
> +	unsigned long dmic_clk; /* SPI or manchester input clock frequency */
> +	unsigned int fl_id;   /* filter instance ID */
> +	unsigned int order; /* filter order */
> +	unsigned int max_scaling;  /* max scaling for audio samples */
> +};
> +
> +struct stm32_adfsdm_data {
> +	unsigned int rate;	/* SNDRV_PCM_RATE value */
> +	unsigned int freq;	/* frequency in Hz */
> +};
> +
> +static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
> +	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
> +	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
> +	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
> +};
> +
> +static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
> +	    SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_S24_LE,
> +
> +	.rate_min = 8000,
> +	.rate_max = 32000,
> +
> +	.channels_min = 1,
> +	.channels_max = 1,
> +
> +	.periods_min = 2,
> +	.periods_max = 48,
> +
> +	.period_bytes_min = 40, /* 8 khz 5 ms */
> +	.period_bytes_max = 4 * PAGE_SIZE,
> +	.buffer_bytes_max = 16 * PAGE_SIZE
> +};
> +
> +static int stm32_adfsdm_get_supported_rates(struct snd_soc_dai *dai,
> +					    unsigned int *rates)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +	struct stm32_dfsdm_hw_param params;
> +	unsigned int max_scaling, i;
> +	int ret;
> +
> +	*rates = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
> +		/* 
> +		 * Check that clkout_freq is compatible
> +		 * Try to find one solution for filter and integrator
> +		 * oversampling ratio.
> +		 */
> +
> +		params.rate = stm32_dfsdm_filter[i].freq;
> +		params.sample_bits = 24;
> +		params.max_scaling = &max_scaling;
> +
> +		ret = pdata->ops->set_hwparam(pdata->adc, &params);
> +		if (!ret) {
> +			*rates |= 1 << i;
> +			dev_err(dai->dev, "%s: %d rate supported\n", __func__,
> +				stm32_dfsdm_filter[i].freq);
> +		}
> +	}
> +
> +	if (!*rates) {
> +		dev_err(dai->dev, "%s: no matched rate found\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
> +			     snd_pcm_uframes_t pos,
> +			     void __user *buf, snd_pcm_uframes_t count)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
> +	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
> +	ssize_t bytes = frames_to_bytes(runtime, count);
> +	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
> +	unsigned int shift = 24 -priv->max_scaling;

24 - priv->max_scaling

> +	
> +	/*
> +	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
> +	 * We need to mask 8 LSB control bits...
> +	 * Additionnaly sample scaling depends on decimation and can need shift

Additionally

> +	 * to be aligned on 32-bit word MSB.
> +	 */
> +	if (shift > 0) {
> +		do {
> +			*ptr <<= shift & STM32_ADFSDM_DATA_MASK;
> +			ptr++;
> +		} while (--sample_cnt);
> +	} else {
> +		do {
> +			*ptr &= STM32_ADFSDM_DATA_MASK;
> +			ptr++;
> +		} while (--sample_cnt);
> +	}
> +
> +	return copy_to_user(buf, hwbuf, bytes);
> +}
> +
> +static void stm32_dfsdm_xrun(void *context)
> +{
> +	struct snd_soc_dai *dai = context;
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +
> +	snd_pcm_stream_lock(priv->substream);
> +	dev_dbg(dai->dev, "%s:unexpected overrun\n", __func__);

newline

> +	/* Stop the player */
> +	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
> +	snd_pcm_stream_unlock(priv->substream);
> +}
> +
> +static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
> +				struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +
> +	priv->substream = substream;
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	return 0;

????

> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_RATE,
> +					  &priv->rates_const);
> +}
> +
> +static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
> +				  struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	priv->substream = NULL;
> +}
> +
> +static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
> +				      struct snd_pcm_hw_params *params,
> +				      struct snd_soc_dai *dai)
> +{
> +	struct snd_dmaengine_dai_dma_data *dma_data;
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +	struct stm32_dfsdm_hw_param df_params;
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	dma_data = snd_soc_dai_get_dma_data(dai, substream);
> +	dma_data->maxburst = 1;
> +
> +	df_params.rate = substream->runtime->rate;
> +	df_params.sample_bits = substream->runtime->sample_bits;
> +	df_params.max_scaling = &priv->max_scaling;
> +
> +	return pdata->ops->set_hwparam(pdata->adc, &df_params);
> +}
> +
> +static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
> +				struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +		return pdata->ops->audio_startup(pdata->adc);
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		pdata->ops->audio_shutdown(pdata->adc);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +
> +	if ((cb == SND_SOC_DAIFMT_CBM_CFM) || (cb == SND_SOC_DAIFMT_CBM_CFS)) {
> +		/* Digital microphone is clocked by external clock */
> +		if (!priv->dmic_clk) {
> +			dev_err(dai->dev,
> +				"system-clock-frequency not defined\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +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);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +	if (dir == SND_SOC_CLOCK_IN) {
> +		pdata->ops->set_sysclk(pdata->adc, freq);
> +		priv->dmic_clk = freq;
> +	}
> +
> +	/* Determine supported rate which depends on SPI/manchester clock */
> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
> +}
> +
> +static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
> +	.startup = stm32_adfsdm_startup,
> +	.shutdown = stm32_adfsdm_shutdown,
> +	.hw_params = stm32_adfsdm_dai_hw_params,
> +	.set_fmt = stm32_adfsdm_set_dai_fmt,
> +	.set_sysclk = stm32_adfsdm_set_sysclk,
> +	.trigger = stm32_adfsdm_trigger,
> +};
> +
> +static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +
> +	/* DMA settings */
> +	snd_soc_dai_init_dma_data(dai, NULL, dma);
> +	dma->addr = pdata->ops->get_dma_source(pdata->adc);
> +	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +
> +	pdata->ops->register_xrun_cb(priv->pdata->adc, stm32_dfsdm_xrun, dai);
> +
> +	return 0;
> +}
> +
> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
> +{
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +
> +	return 0;
> +}
> +
> +static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
> +	.capture = {
> +		    .channels_min = 1,
> +		    .channels_max = 1,
> +		    .formats = SNDRV_PCM_FMTBIT_S24_LE,
> +		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
> +			      SNDRV_PCM_RATE_32000),
> +		    },
> +	.probe = stm32_adfsdm_dai_probe,
> +	.remove = stm32_adfsdm_dai_remove,
> +	.ops = &stm32_adfsdm_dai_ops,
> +};
> +
> +static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
> +	.name = "sti_cpu_dai",
> +};
> +
> +static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
> +	.pcm_hardware = &stm32_adfsdm_pcm_hw,
> +	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
> +	.copy = stm32_adfsdm_copy,
> +};
> +
> +static int stm32_adfsdm_probe(struct platform_device *pdev)
> +{
> +	struct stm32_adfsdm_priv *priv;
> +	struct stm32_adfsdm_pdata *pdata = pdev->dev.platform_data;
> +	int ret;
> +
> +	dev_dbg(&pdev->dev, "%s: enter for node %p\n", __func__,
> +		pdev->dev.parent->of_node->name);
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pdata = pdata;
> +
> +	priv->dai_drv = stm32_adfsdm_dai;
> +	priv->dai_drv.name = pdev->dev.parent->of_node->name;
> +	priv->dai_drv.capture.stream_name = pdev->dev.parent->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;
> +
> +	ret = devm_snd_dmaengine_pcm_register(pdev->dev.parent,
> +					      &dmaengine_pcm_config, 0);
> +	if (ret < 0)
> +		dev_err(&pdev->dev, "failed to register dma pcm config\n");
> +
> +	return ret;
> +}
> +
> +static struct platform_driver stm32_adfsdm_driver = {
> +	.driver = {
> +		   .name = STM32_ADFSDM_DRV_NAME,
> +		   },
> +	.probe = stm32_adfsdm_probe,
> +};
> +
> +module_platform_driver(stm32_adfsdm_driver);
> +
> +MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);
> 

-- 

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

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-13 18:13       ` Peter Meerwald-Stadler
  0 siblings, 0 replies; 107+ messages in thread
From: Peter Meerwald-Stadler @ 2017-02-13 18:13 UTC (permalink / raw)
  To: Arnaud Pouliquen; +Cc: Rob Herring, Jonathan Cameron, devicetree, linux-iio

On Mon, 13 Feb 2017, Arnaud Pouliquen wrote:

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

Modulators
more typos below
 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  include/sound/stm32-adfsdm.h |  80 ++++++++++
>  sound/soc/Kconfig            |   1 +
>  sound/soc/Makefile           |   1 +
>  sound/soc/stm/Kconfig        |  10 ++
>  sound/soc/stm/Makefile       |   2 +
>  sound/soc/stm/stm32_adfsdm.c | 365 +++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 459 insertions(+)
>  create mode 100644 include/sound/stm32-adfsdm.h
>  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/include/sound/stm32-adfsdm.h b/include/sound/stm32-adfsdm.h
> new file mode 100644
> index 0000000..ff5899d
> --- /dev/null
> +++ b/include/sound/stm32-adfsdm.h
> @@ -0,0 +1,80 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver API
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License terms: GPL V2.0.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + */
> +#ifndef STM32_ADFSDM_H
> +#define STM32_ADFSDM_H
> +
> +struct stm32_dfsdm_adc;
> +
> +/**
> + * struct stm32_dfsdm_hw_param - stm32 audio hardware params
> + * @rate:		sampling rate
> + * @sample_bits:	sample size in bits
> + * @max_scaling:	effective scaling in bit computed by iio driver

bits?

> + */
> +struct stm32_dfsdm_hw_param {
> +	unsigned int rate;
> +	unsigned int sample_bits;
> +	unsigned int *max_scaling;
> +};
> +
> +/*
> + * Potential improvement:
> + * Following structure and functions could be generic and declared in
> + * an asoc-iio.h
> + */
> +struct stm32_adfsdm_codec_ops {
> +	/*
> +	 * Set the SPI or manchester input Frequency

frequency

> +	 * Optional: not use if DFSDM is master on SPI
> +	 */
> +	void (*set_sysclk)(struct stm32_dfsdm_adc *adc, unsigned int freq);
> +
> +	/*
> +	 * Set expected audio sampling rate and format.
> +	 * Precision is returned to allow to rescale samples
> +	 */
> +	int (*set_hwparam)(struct stm32_dfsdm_adc *adc,
> +			   struct stm32_dfsdm_hw_param *params);
> +
> +	/* Called when ASoC starts an audio stream setup. */
> +	int (*audio_startup)(struct stm32_dfsdm_adc *adc);
> +
> +	/* Shuts down the audio stream. */
> +	void (*audio_shutdown)(struct stm32_dfsdm_adc *adc);
> +
> +	/*
> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA

physical
ALSA

> +	 * transfers.
> +	 */
> +	dma_addr_t (*get_dma_source)(struct stm32_dfsdm_adc *adc);
> +
> +	/* Register callback to treat overrun issues */
> +	void (*register_xrun_cb)(struct stm32_dfsdm_adc *adc,
> +				 void (*overrun_cb)(void *context),
> +				 void *context);
> +
> +};
> +
> +/* stm32 dfsdm initalization data */
> +struct stm32_adfsdm_pdata {
> +	const struct stm32_adfsdm_codec_ops *ops;
> +	struct stm32_dfsdm_adc *adc;
> +};
> +
> +#define STM32_ADFSDM_DRV_NAME "stm32-dfsdm-audio"
> +#endif
> 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..4488461
> --- /dev/null
> +++ b/sound/soc/stm/stm32_adfsdm.c
> @@ -0,0 +1,365 @@
> +/*
> + * This file is part of STM32 DFSDM ASoC DAI driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> + *          Olivier Moysan <olivier.moysan@st.com>
> + *
> + * License type: GPLv2
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.
> + * See the GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <sound/dmaengine_pcm.h>
> +#include <sound/stm32-adfsdm.h>
> +
> +#define STM32_ADFSDM_DATA_MASK	GENMASK(31, 8)
> +
> +struct stm32_adfsdm_priv {
> +	struct snd_soc_dai_driver dai_drv;
> +	struct stm32_adfsdm_pdata *pdata; /* platform data set by IIO driver */
> +	struct snd_dmaengine_dai_dma_data dma_data;  /* dma config */
> +	struct snd_pcm_substream *substream;  
> +	struct snd_pcm_hw_constraint_list rates_const;
> +	unsigned long dmic_clk; /* SPI or manchester input clock frequency */
> +	unsigned int fl_id;   /* filter instance ID */
> +	unsigned int order; /* filter order */
> +	unsigned int max_scaling;  /* max scaling for audio samples */
> +};
> +
> +struct stm32_adfsdm_data {
> +	unsigned int rate;	/* SNDRV_PCM_RATE value */
> +	unsigned int freq;	/* frequency in Hz */
> +};
> +
> +static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
> +	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
> +	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
> +	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
> +};
> +
> +static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
> +	    SNDRV_PCM_INFO_MMAP_VALID,
> +	.formats = SNDRV_PCM_FMTBIT_S24_LE,
> +
> +	.rate_min = 8000,
> +	.rate_max = 32000,
> +
> +	.channels_min = 1,
> +	.channels_max = 1,
> +
> +	.periods_min = 2,
> +	.periods_max = 48,
> +
> +	.period_bytes_min = 40, /* 8 khz 5 ms */
> +	.period_bytes_max = 4 * PAGE_SIZE,
> +	.buffer_bytes_max = 16 * PAGE_SIZE
> +};
> +
> +static int stm32_adfsdm_get_supported_rates(struct snd_soc_dai *dai,
> +					    unsigned int *rates)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +	struct stm32_dfsdm_hw_param params;
> +	unsigned int max_scaling, i;
> +	int ret;
> +
> +	*rates = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
> +		/* 
> +		 * Check that clkout_freq is compatible
> +		 * Try to find one solution for filter and integrator
> +		 * oversampling ratio.
> +		 */
> +
> +		params.rate = stm32_dfsdm_filter[i].freq;
> +		params.sample_bits = 24;
> +		params.max_scaling = &max_scaling;
> +
> +		ret = pdata->ops->set_hwparam(pdata->adc, &params);
> +		if (!ret) {
> +			*rates |= 1 << i;
> +			dev_err(dai->dev, "%s: %d rate supported\n", __func__,
> +				stm32_dfsdm_filter[i].freq);
> +		}
> +	}
> +
> +	if (!*rates) {
> +		dev_err(dai->dev, "%s: no matched rate found\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
> +			     snd_pcm_uframes_t pos,
> +			     void __user *buf, snd_pcm_uframes_t count)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
> +	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
> +	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
> +	ssize_t bytes = frames_to_bytes(runtime, count);
> +	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
> +	unsigned int shift = 24 -priv->max_scaling;

24 - priv->max_scaling

> +	
> +	/*
> +	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
> +	 * We need to mask 8 LSB control bits...
> +	 * Additionnaly sample scaling depends on decimation and can need shift

Additionally

> +	 * to be aligned on 32-bit word MSB.
> +	 */
> +	if (shift > 0) {
> +		do {
> +			*ptr <<= shift & STM32_ADFSDM_DATA_MASK;
> +			ptr++;
> +		} while (--sample_cnt);
> +	} else {
> +		do {
> +			*ptr &= STM32_ADFSDM_DATA_MASK;
> +			ptr++;
> +		} while (--sample_cnt);
> +	}
> +
> +	return copy_to_user(buf, hwbuf, bytes);
> +}
> +
> +static void stm32_dfsdm_xrun(void *context)
> +{
> +	struct snd_soc_dai *dai = context;
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +
> +	snd_pcm_stream_lock(priv->substream);
> +	dev_dbg(dai->dev, "%s:unexpected overrun\n", __func__);

newline

> +	/* Stop the player */
> +	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
> +	snd_pcm_stream_unlock(priv->substream);
> +}
> +
> +static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
> +				struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +
> +	priv->substream = substream;
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	return 0;

????

> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_RATE,
> +					  &priv->rates_const);
> +}
> +
> +static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
> +				  struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	priv->substream = NULL;
> +}
> +
> +static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
> +				      struct snd_pcm_hw_params *params,
> +				      struct snd_soc_dai *dai)
> +{
> +	struct snd_dmaengine_dai_dma_data *dma_data;
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +	struct stm32_dfsdm_hw_param df_params;
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	dma_data = snd_soc_dai_get_dma_data(dai, substream);
> +	dma_data->maxburst = 1;
> +
> +	df_params.rate = substream->runtime->rate;
> +	df_params.sample_bits = substream->runtime->sample_bits;
> +	df_params.max_scaling = &priv->max_scaling;
> +
> +	return pdata->ops->set_hwparam(pdata->adc, &df_params);
> +}
> +
> +static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
> +				struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +		return pdata->ops->audio_startup(pdata->adc);
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		pdata->ops->audio_shutdown(pdata->adc);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
> +
> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +
> +	if ((cb == SND_SOC_DAIFMT_CBM_CFM) || (cb == SND_SOC_DAIFMT_CBM_CFS)) {
> +		/* Digital microphone is clocked by external clock */
> +		if (!priv->dmic_clk) {
> +			dev_err(dai->dev,
> +				"system-clock-frequency not defined\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +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);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +	if (dir == SND_SOC_CLOCK_IN) {
> +		pdata->ops->set_sysclk(pdata->adc, freq);
> +		priv->dmic_clk = freq;
> +	}
> +
> +	/* Determine supported rate which depends on SPI/manchester clock */
> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
> +}
> +
> +static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
> +	.startup = stm32_adfsdm_startup,
> +	.shutdown = stm32_adfsdm_shutdown,
> +	.hw_params = stm32_adfsdm_dai_hw_params,
> +	.set_fmt = stm32_adfsdm_set_dai_fmt,
> +	.set_sysclk = stm32_adfsdm_set_sysclk,
> +	.trigger = stm32_adfsdm_trigger,
> +};
> +
> +static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
> +{
> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
> +	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +
> +	/* DMA settings */
> +	snd_soc_dai_init_dma_data(dai, NULL, dma);
> +	dma->addr = pdata->ops->get_dma_source(pdata->adc);
> +	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +
> +	pdata->ops->register_xrun_cb(priv->pdata->adc, stm32_dfsdm_xrun, dai);
> +
> +	return 0;
> +}
> +
> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
> +{
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +
> +	return 0;
> +}
> +
> +static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
> +	.capture = {
> +		    .channels_min = 1,
> +		    .channels_max = 1,
> +		    .formats = SNDRV_PCM_FMTBIT_S24_LE,
> +		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
> +			      SNDRV_PCM_RATE_32000),
> +		    },
> +	.probe = stm32_adfsdm_dai_probe,
> +	.remove = stm32_adfsdm_dai_remove,
> +	.ops = &stm32_adfsdm_dai_ops,
> +};
> +
> +static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
> +	.name = "sti_cpu_dai",
> +};
> +
> +static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
> +	.pcm_hardware = &stm32_adfsdm_pcm_hw,
> +	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
> +	.copy = stm32_adfsdm_copy,
> +};
> +
> +static int stm32_adfsdm_probe(struct platform_device *pdev)
> +{
> +	struct stm32_adfsdm_priv *priv;
> +	struct stm32_adfsdm_pdata *pdata = pdev->dev.platform_data;
> +	int ret;
> +
> +	dev_dbg(&pdev->dev, "%s: enter for node %p\n", __func__,
> +		pdev->dev.parent->of_node->name);
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pdata = pdata;
> +
> +	priv->dai_drv = stm32_adfsdm_dai;
> +	priv->dai_drv.name = pdev->dev.parent->of_node->name;
> +	priv->dai_drv.capture.stream_name = pdev->dev.parent->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;
> +
> +	ret = devm_snd_dmaengine_pcm_register(pdev->dev.parent,
> +					      &dmaengine_pcm_config, 0);
> +	if (ret < 0)
> +		dev_err(&pdev->dev, "failed to register dma pcm config\n");
> +
> +	return ret;
> +}
> +
> +static struct platform_driver stm32_adfsdm_driver = {
> +	.driver = {
> +		   .name = STM32_ADFSDM_DRV_NAME,
> +		   },
> +	.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);
> 

-- 

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

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-13 18:13       ` Peter Meerwald-Stadler
@ 2017-02-14 11:09           ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-14 11:09 UTC (permalink / raw)
  To: Peter Meerwald-Stadler
  Cc: Rob Herring, Jonathan Cameron, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA

Hello Peter,

Thanks for your highlight on typos.
I did not pay to much attention on it, as i should for a patch candidate
for upstream. Surely wrongly...
For your information, aim of this RFC is to discuss the way to integrate
the DFSDM drivers in ASoC and IIO. So the patches associated can not be
upstreamed in current version.
When global design will be validated, i plan to complete/finalize the
patches and propose the series as "PATCH" instead of "RFC".

Nevertheless, be sure that I will integrate your remarks in next version.

Some answers in-line

Regards,

Arnaud

On 02/13/2017 07:13 PM, Peter Meerwald-Stadler wrote:
> On Mon, 13 Feb 2017, Arnaud Pouliquen wrote:
> 
>> Add driver to handle DAI interface for PDM microphones connected
>> to Digital Filter for Sigma Delta mModulators IP.
> 
> Modulators
> more typos below
>  
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
>> ---
>>  include/sound/stm32-adfsdm.h |  80 ++++++++++
>>  sound/soc/Kconfig            |   1 +
>>  sound/soc/Makefile           |   1 +
>>  sound/soc/stm/Kconfig        |  10 ++
>>  sound/soc/stm/Makefile       |   2 +
>>  sound/soc/stm/stm32_adfsdm.c | 365 +++++++++++++++++++++++++++++++++++++++++++
>>  6 files changed, 459 insertions(+)
>>  create mode 100644 include/sound/stm32-adfsdm.h
>>  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/include/sound/stm32-adfsdm.h b/include/sound/stm32-adfsdm.h
>> new file mode 100644
>> index 0000000..ff5899d
>> --- /dev/null
>> +++ b/include/sound/stm32-adfsdm.h
>> @@ -0,0 +1,80 @@
>> +/*
>> + * This file is part of STM32 DFSDM mfd driver API
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
>> + *
>> + * License terms: GPL V2.0.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
>> + * details.
>> + */
>> +#ifndef STM32_ADFSDM_H
>> +#define STM32_ADFSDM_H
>> +
>> +struct stm32_dfsdm_adc;
>> +
>> +/**
>> + * struct stm32_dfsdm_hw_param - stm32 audio hardware params
>> + * @rate:		sampling rate
>> + * @sample_bits:	sample size in bits
>> + * @max_scaling:	effective scaling in bit computed by iio driver
> 
> bits?
> 
>> + */
>> +struct stm32_dfsdm_hw_param {
>> +	unsigned int rate;
>> +	unsigned int sample_bits;
>> +	unsigned int *max_scaling;
>> +};
>> +
>> +/*
>> + * Potential improvement:
>> + * Following structure and functions could be generic and declared in
>> + * an asoc-iio.h
>> + */
>> +struct stm32_adfsdm_codec_ops {
>> +	/*
>> +	 * Set the SPI or manchester input Frequency
> 
> frequency
> 
>> +	 * Optional: not use if DFSDM is master on SPITest
>> +	 */
>> +	void (*set_sysclk)(struct stm32_dfsdm_adc *adc, unsigned int freq);
>> +
>> +	/*
>> +	 * Set expected audio sampling rate and format.
>> +	 * Precision is returned to allow to rescale samples
>> +	 */
>> +	int (*set_hwparam)(struct stm32_dfsdm_adc *adc,
>> +			   struct stm32_dfsdm_hw_param *params);
>> +
>> +	/* Called when ASoC starts an audio stream setup. */
>> +	int (*audio_startup)(struct stm32_dfsdm_adc *adc);
>> +
>> +	/* Shuts down the audio stream. */
>> +	void (*audio_shutdown)(struct stm32_dfsdm_adc *adc);
>> +
>> +	/*
>> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
> 
> physical
> ALSA
> 
>> +	 * transfers.
>> +	 */
>> +	dma_addr_t (*get_dma_source)(struct stm32_dfsdm_adc *adc);
>> +
>> +	/* Register callback to treat overrun issues */
>> +	void (*register_xrun_cb)(struct stm32_dfsdm_adc *adc,
>> +				 void (*overrun_cb)(void *context),
>> +				 void *context);
>> +
>> +};
>> +
>> +/* stm32 dfsdm initalization data */
>> +struct stm32_adfsdm_pdata {
>> +	const struct stm32_adfsdm_codec_ops *ops;
>> +	struct stm32_dfsdm_adc *adc;
>> +};
>> +
>> +#define STM32_ADFSDM_DRV_NAME "stm32-dfsdm-audio"
>> +#endif
>> 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..4488461
>> --- /dev/null
>> +++ b/sound/soc/stm/stm32_adfsdm.c
>> @@ -0,0 +1,365 @@
>> +/*
>> + * This file is part of STM32 DFSDM ASoC DAI driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Authors: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
>> + *          Olivier Moysan <olivier.moysan-qxv4g6HH51o@public.gmane.org>
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +#include <sound/dmaengine_pcm.h>
>> +#include <sound/stm32-adfsdm.h>
>> +
>> +#define STM32_ADFSDM_DATA_MASK	GENMASK(31, 8)
>> +
>> +struct stm32_adfsdm_priv {
>> +	struct snd_soc_dai_driver dai_drv;
>> +	struct stm32_adfsdm_pdata *pdata; /* platform data set by IIO driver */
>> +	struct snd_dmaengine_dai_dma_data dma_data;  /* dma config */
>> +	struct snd_pcm_substream *substream;  
>> +	struct snd_pcm_hw_constraint_list rates_const;
>> +	unsigned long dmic_clk; /* SPI or manchester input clock frequency */
>> +	unsigned int fl_id;   /* filter instance ID */
>> +	unsigned int order; /* filter order */
>> +	unsigned int max_scaling;  /* max scaling for audio samples */
>> +};
>> +
>> +struct stm32_adfsdm_data {
>> +	unsigned int rate;	/* SNDRV_PCM_RATE value */
>> +	unsigned int freq;	/* frequency in Hz */
>> +};
>> +
>> +static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
>> +	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
>> +	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
>> +	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
>> +};
>> +
>> +static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
>> +	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
>> +	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
>> +	    SNDRV_PCM_INFO_MMAP_VALID,
>> +	.formats = SNDRV_PCM_FMTBIT_S24_LE,
>> +
>> +	.rate_min = 8000,
>> +	.rate_max = 32000,
>> +
>> +	.channels_min = 1,
>> +	.channels_max = 1,
>> +
>> +	.periods_min = 2,
>> +	.periods_max = 48,
>> +
>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>> +	.period_bytes_max = 4 * PAGE_SIZE,
>> +	.buffer_bytes_max = 16 * PAGE_SIZE
>> +};
>> +
>> +static int stm32_adfsdm_get_supported_rates(struct snd_soc_dai *dai,
>> +					    unsigned int *rates)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +	struct stm32_dfsdm_hw_param params;
>> +	unsigned int max_scaling, i;
>> +	int ret;
>> +
>> +	*rates = 0;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
>> +		/* 
>> +		 * Check that clkout_freq is compatible
>> +		 * Try to find one solution for filter and integrator
>> +		 * oversampling ratio.
>> +		 */
>> +
>> +		params.rate = stm32_dfsdm_filter[i].freq;
>> +		params.sample_bits = 24;
>> +		params.max_scaling = &max_scaling;
>> +
>> +		ret = pdata->ops->set_hwparam(pdata->adc, &params);
>> +		if (!ret) {
>> +			*rates |= 1 << i;
>> +			dev_err(dai->dev, "%s: %d rate supported\n", __func__,
>> +				stm32_dfsdm_filter[i].freq);
>> +		}
>> +	}
>> +
>> +	if (!*rates) {
>> +		dev_err(dai->dev, "%s: no matched rate found\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
>> +			     snd_pcm_uframes_t pos,
>> +			     void __user *buf, snd_pcm_uframes_t count)
>> +{
>> +	struct snd_pcm_runtime *runtime = substream->runtime;
>> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> +	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
>> +	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
>> +	ssize_t bytes = frames_to_bytes(runtime, count);
>> +	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
>> +	unsigned int shift = 24 -priv->max_scaling;
> 
> 24 - priv->max_scaling
miss this when running checkpatch tool...sorry for this stupid basic error
>> +	
>> +	/*
>> +	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
>> +	 * We need to mask 8 LSB control bits...
>> +	 * Additionnaly sample scaling depends on decimation and can need shift
> 
> Additionally
> 
>> +	 * to be aligned on 32-bit word MSB.
>> +	 */
>> +	if (shift > 0) {
>> +		do {
>> +			*ptr <<= shift & STM32_ADFSDM_DATA_MASK;
>> +			ptr++;
>> +		} while (--sample_cnt);
>> +	} else {
>> +		do {
>> +			*ptr &= STM32_ADFSDM_DATA_MASK;
>> +			ptr++;
>> +		} while (--sample_cnt);
>> +	}
>> +
>> +	return copy_to_user(buf, hwbuf, bytes);
>> +}
>> +
>> +static void stm32_dfsdm_xrun(void *context)
>> +{
>> +	struct snd_soc_dai *dai = context;
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +
>> +	snd_pcm_stream_lock(priv->substream);
>> +	dev_dbg(dai->dev, "%s:unexpected overrun\n", __func__);
> 
> newline
> 
>> +	/* Stop the player */
>> +	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
>> +	snd_pcm_stream_unlock(priv->substream);
>> +}
>> +
>> +static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
>> +				struct snd_soc_dai *dai)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +
>> +	priv->substream = substream;
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	return 0;
> 
> ????
line added for debug that i forgot to clean...
> 
>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>> +					  SNDRV_PCM_HW_PARAM_RATE,
>> +					  &priv->rates_const);
>> +}
>> +
>> +static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
>> +				  struct snd_soc_dai *dai)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	priv->substream = NULL;
>> +}
>> +
>> +static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
>> +				      struct snd_pcm_hw_params *params,
>> +				      struct snd_soc_dai *dai)
>> +{
>> +	struct snd_dmaengine_dai_dma_data *dma_data;
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +	struct stm32_dfsdm_hw_param df_params;
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	dma_data = snd_soc_dai_get_dma_data(dai, substream);
>> +	dma_data->maxburst = 1;
>> +
>> +	df_params.rate = substream->runtime->rate;
>> +	df_params.sample_bits = substream->runtime->sample_bits;
>> +	df_params.max_scaling = &priv->max_scaling;
>> +
>> +	return pdata->ops->set_hwparam(pdata->adc, &df_params);
>> +}
>> +
>> +static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
>> +				struct snd_soc_dai *dai)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +
>> +	switch (cmd) {
>> +	case SNDRV_PCM_TRIGGER_START:
>> +	case SNDRV_PCM_TRIGGER_RESUME:
>> +		return pdata->ops->audio_startup(pdata->adc);
>> +	case SNDRV_PCM_TRIGGER_SUSPEND:
>> +	case SNDRV_PCM_TRIGGER_STOP:
>> +		pdata->ops->audio_shutdown(pdata->adc);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +
>> +	if ((cb == SND_SOC_DAIFMT_CBM_CFM) || (cb == SND_SOC_DAIFMT_CBM_CFS)) {
>> +		/* Digital microphone is clocked by external clock */
>> +		if (!priv->dmic_clk) {
>> +			dev_err(dai->dev,
>> +				"system-clock-frequency not defined\n");
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +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);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +	if (dir == SND_SOC_CLOCK_IN) {
>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>> +		priv->dmic_clk = freq;
>> +	}
>> +
>> +	/* Determine supported rate which depends on SPI/manchester clock */
>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
>> +}
>> +
>> +static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
>> +	.startup = stm32_adfsdm_startup,
>> +	.shutdown = stm32_adfsdm_shutdown,
>> +	.hw_params = stm32_adfsdm_dai_hw_params,
>> +	.set_fmt = stm32_adfsdm_set_dai_fmt,
>> +	.set_sysclk = stm32_adfsdm_set_sysclk,
>> +	.trigger = stm32_adfsdm_trigger,
>> +};
>> +
>> +static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +
>> +	/* DMA settings */
>> +	snd_soc_dai_init_dma_data(dai, NULL, dma);
>> +	dma->addr = pdata->ops->get_dma_source(pdata->adc);
>> +	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +
>> +	pdata->ops->register_xrun_cb(priv->pdata->adc, stm32_dfsdm_xrun, dai);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>> +{
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
>> +	.capture = {
>> +		    .channels_min = 1,
>> +		    .channels_max = 1,
>> +		    .formats = SNDRV_PCM_FMTBIT_S24_LE,
>> +		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
>> +			      SNDRV_PCM_RATE_32000),
>> +		    },
>> +	.probe = stm32_adfsdm_dai_probe,
>> +	.remove = stm32_adfsdm_dai_remove,
>> +	.ops = &stm32_adfsdm_dai_ops,
>> +};
>> +
>> +static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
>> +	.name = "sti_cpu_dai",
>> +};
>> +
>> +static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
>> +	.pcm_hardware = &stm32_adfsdm_pcm_hw,
>> +	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
>> +	.copy = stm32_adfsdm_copy,
>> +};
>> +
>> +static int stm32_adfsdm_probe(struct platform_device *pdev)
>> +{
>> +	struct stm32_adfsdm_priv *priv;
>> +	struct stm32_adfsdm_pdata *pdata = pdev->dev.platform_data;
>> +	int ret;
>> +
>> +	dev_dbg(&pdev->dev, "%s: enter for node %p\n", __func__,
>> +		pdev->dev.parent->of_node->name);
>> +
>> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> +	if (!priv)
>> +		return -ENOMEM;
>> +
>> +	priv->pdata = pdata;
>> +
>> +	priv->dai_drv = stm32_adfsdm_dai;
>> +	priv->dai_drv.name = pdev->dev.parent->of_node->name;
>> +	priv->dai_drv.capture.stream_name = pdev->dev.parent->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;
>> +
>> +	ret = devm_snd_dmaengine_pcm_register(pdev->dev.parent,
>> +					      &dmaengine_pcm_config, 0);
>> +	if (ret < 0)
>> +		dev_err(&pdev->dev, "failed to register dma pcm config\n");
>> +
>> +	return ret;
>> +}
>> +
>> +static struct platform_driver stm32_adfsdm_driver = {
>> +	.driver = {
>> +		   .name = STM32_ADFSDM_DRV_NAME,
>> +		   },
>> +	.probe = stm32_adfsdm_probe,
>> +};
>> +
>> +module_platform_driver(stm32_adfsdm_driver);
>> +
>> +MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:" STM32_ADFSDM_DRV_NAME);
>>
> 
--
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] 107+ messages in thread

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-14 11:09           ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-14 11:09 UTC (permalink / raw)
  To: Peter Meerwald-Stadler
  Cc: Rob Herring, Jonathan Cameron, devicetree, linux-iio

Hello Peter,

Thanks for your highlight on typos.
I did not pay to much attention on it, as i should for a patch candidate
for upstream. Surely wrongly...
For your information, aim of this RFC is to discuss the way to integrate
the DFSDM drivers in ASoC and IIO. So the patches associated can not be
upstreamed in current version.
When global design will be validated, i plan to complete/finalize the
patches and propose the series as "PATCH" instead of "RFC".

Nevertheless, be sure that I will integrate your remarks in next version.

Some answers in-line

Regards,

Arnaud

On 02/13/2017 07:13 PM, Peter Meerwald-Stadler wrote:
> On Mon, 13 Feb 2017, Arnaud Pouliquen wrote:
> 
>> Add driver to handle DAI interface for PDM microphones connected
>> to Digital Filter for Sigma Delta mModulators IP.
> 
> Modulators
> more typos below
>  
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  include/sound/stm32-adfsdm.h |  80 ++++++++++
>>  sound/soc/Kconfig            |   1 +
>>  sound/soc/Makefile           |   1 +
>>  sound/soc/stm/Kconfig        |  10 ++
>>  sound/soc/stm/Makefile       |   2 +
>>  sound/soc/stm/stm32_adfsdm.c | 365 +++++++++++++++++++++++++++++++++++++++++++
>>  6 files changed, 459 insertions(+)
>>  create mode 100644 include/sound/stm32-adfsdm.h
>>  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/include/sound/stm32-adfsdm.h b/include/sound/stm32-adfsdm.h
>> new file mode 100644
>> index 0000000..ff5899d
>> --- /dev/null
>> +++ b/include/sound/stm32-adfsdm.h
>> @@ -0,0 +1,80 @@
>> +/*
>> + * This file is part of STM32 DFSDM mfd driver API
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
>> + *
>> + * License terms: GPL V2.0.
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
>> + * details.
>> + */
>> +#ifndef STM32_ADFSDM_H
>> +#define STM32_ADFSDM_H
>> +
>> +struct stm32_dfsdm_adc;
>> +
>> +/**
>> + * struct stm32_dfsdm_hw_param - stm32 audio hardware params
>> + * @rate:		sampling rate
>> + * @sample_bits:	sample size in bits
>> + * @max_scaling:	effective scaling in bit computed by iio driver
> 
> bits?
> 
>> + */
>> +struct stm32_dfsdm_hw_param {
>> +	unsigned int rate;
>> +	unsigned int sample_bits;
>> +	unsigned int *max_scaling;
>> +};
>> +
>> +/*
>> + * Potential improvement:
>> + * Following structure and functions could be generic and declared in
>> + * an asoc-iio.h
>> + */
>> +struct stm32_adfsdm_codec_ops {
>> +	/*
>> +	 * Set the SPI or manchester input Frequency
> 
> frequency
> 
>> +	 * Optional: not use if DFSDM is master on SPITest
>> +	 */
>> +	void (*set_sysclk)(struct stm32_dfsdm_adc *adc, unsigned int freq);
>> +
>> +	/*
>> +	 * Set expected audio sampling rate and format.
>> +	 * Precision is returned to allow to rescale samples
>> +	 */
>> +	int (*set_hwparam)(struct stm32_dfsdm_adc *adc,
>> +			   struct stm32_dfsdm_hw_param *params);
>> +
>> +	/* Called when ASoC starts an audio stream setup. */
>> +	int (*audio_startup)(struct stm32_dfsdm_adc *adc);
>> +
>> +	/* Shuts down the audio stream. */
>> +	void (*audio_shutdown)(struct stm32_dfsdm_adc *adc);
>> +
>> +	/*
>> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
> 
> physical
> ALSA
> 
>> +	 * transfers.
>> +	 */
>> +	dma_addr_t (*get_dma_source)(struct stm32_dfsdm_adc *adc);
>> +
>> +	/* Register callback to treat overrun issues */
>> +	void (*register_xrun_cb)(struct stm32_dfsdm_adc *adc,
>> +				 void (*overrun_cb)(void *context),
>> +				 void *context);
>> +
>> +};
>> +
>> +/* stm32 dfsdm initalization data */
>> +struct stm32_adfsdm_pdata {
>> +	const struct stm32_adfsdm_codec_ops *ops;
>> +	struct stm32_dfsdm_adc *adc;
>> +};
>> +
>> +#define STM32_ADFSDM_DRV_NAME "stm32-dfsdm-audio"
>> +#endif
>> 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..4488461
>> --- /dev/null
>> +++ b/sound/soc/stm/stm32_adfsdm.c
>> @@ -0,0 +1,365 @@
>> +/*
>> + * This file is part of STM32 DFSDM ASoC DAI driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> + *          Olivier Moysan <olivier.moysan@st.com>
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +#include <sound/dmaengine_pcm.h>
>> +#include <sound/stm32-adfsdm.h>
>> +
>> +#define STM32_ADFSDM_DATA_MASK	GENMASK(31, 8)
>> +
>> +struct stm32_adfsdm_priv {
>> +	struct snd_soc_dai_driver dai_drv;
>> +	struct stm32_adfsdm_pdata *pdata; /* platform data set by IIO driver */
>> +	struct snd_dmaengine_dai_dma_data dma_data;  /* dma config */
>> +	struct snd_pcm_substream *substream;  
>> +	struct snd_pcm_hw_constraint_list rates_const;
>> +	unsigned long dmic_clk; /* SPI or manchester input clock frequency */
>> +	unsigned int fl_id;   /* filter instance ID */
>> +	unsigned int order; /* filter order */
>> +	unsigned int max_scaling;  /* max scaling for audio samples */
>> +};
>> +
>> +struct stm32_adfsdm_data {
>> +	unsigned int rate;	/* SNDRV_PCM_RATE value */
>> +	unsigned int freq;	/* frequency in Hz */
>> +};
>> +
>> +static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
>> +	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
>> +	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
>> +	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
>> +};
>> +
>> +static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
>> +	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
>> +	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
>> +	    SNDRV_PCM_INFO_MMAP_VALID,
>> +	.formats = SNDRV_PCM_FMTBIT_S24_LE,
>> +
>> +	.rate_min = 8000,
>> +	.rate_max = 32000,
>> +
>> +	.channels_min = 1,
>> +	.channels_max = 1,
>> +
>> +	.periods_min = 2,
>> +	.periods_max = 48,
>> +
>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>> +	.period_bytes_max = 4 * PAGE_SIZE,
>> +	.buffer_bytes_max = 16 * PAGE_SIZE
>> +};
>> +
>> +static int stm32_adfsdm_get_supported_rates(struct snd_soc_dai *dai,
>> +					    unsigned int *rates)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +	struct stm32_dfsdm_hw_param params;
>> +	unsigned int max_scaling, i;
>> +	int ret;
>> +
>> +	*rates = 0;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
>> +		/* 
>> +		 * Check that clkout_freq is compatible
>> +		 * Try to find one solution for filter and integrator
>> +		 * oversampling ratio.
>> +		 */
>> +
>> +		params.rate = stm32_dfsdm_filter[i].freq;
>> +		params.sample_bits = 24;
>> +		params.max_scaling = &max_scaling;
>> +
>> +		ret = pdata->ops->set_hwparam(pdata->adc, &params);
>> +		if (!ret) {
>> +			*rates |= 1 << i;
>> +			dev_err(dai->dev, "%s: %d rate supported\n", __func__,
>> +				stm32_dfsdm_filter[i].freq);
>> +		}
>> +	}
>> +
>> +	if (!*rates) {
>> +		dev_err(dai->dev, "%s: no matched rate found\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
>> +			     snd_pcm_uframes_t pos,
>> +			     void __user *buf, snd_pcm_uframes_t count)
>> +{
>> +	struct snd_pcm_runtime *runtime = substream->runtime;
>> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
>> +	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
>> +	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
>> +	ssize_t bytes = frames_to_bytes(runtime, count);
>> +	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
>> +	unsigned int shift = 24 -priv->max_scaling;
> 
> 24 - priv->max_scaling
miss this when running checkpatch tool...sorry for this stupid basic error
>> +	
>> +	/*
>> +	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
>> +	 * We need to mask 8 LSB control bits...
>> +	 * Additionnaly sample scaling depends on decimation and can need shift
> 
> Additionally
> 
>> +	 * to be aligned on 32-bit word MSB.
>> +	 */
>> +	if (shift > 0) {
>> +		do {
>> +			*ptr <<= shift & STM32_ADFSDM_DATA_MASK;
>> +			ptr++;
>> +		} while (--sample_cnt);
>> +	} else {
>> +		do {
>> +			*ptr &= STM32_ADFSDM_DATA_MASK;
>> +			ptr++;
>> +		} while (--sample_cnt);
>> +	}
>> +
>> +	return copy_to_user(buf, hwbuf, bytes);
>> +}
>> +
>> +static void stm32_dfsdm_xrun(void *context)
>> +{
>> +	struct snd_soc_dai *dai = context;
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +
>> +	snd_pcm_stream_lock(priv->substream);
>> +	dev_dbg(dai->dev, "%s:unexpected overrun\n", __func__);
> 
> newline
> 
>> +	/* Stop the player */
>> +	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
>> +	snd_pcm_stream_unlock(priv->substream);
>> +}
>> +
>> +static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
>> +				struct snd_soc_dai *dai)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +
>> +	priv->substream = substream;
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	return 0;
> 
> ????
line added for debug that i forgot to clean...
> 
>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>> +					  SNDRV_PCM_HW_PARAM_RATE,
>> +					  &priv->rates_const);
>> +}
>> +
>> +static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
>> +				  struct snd_soc_dai *dai)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	priv->substream = NULL;
>> +}
>> +
>> +static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
>> +				      struct snd_pcm_hw_params *params,
>> +				      struct snd_soc_dai *dai)
>> +{
>> +	struct snd_dmaengine_dai_dma_data *dma_data;
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +	struct stm32_dfsdm_hw_param df_params;
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	dma_data = snd_soc_dai_get_dma_data(dai, substream);
>> +	dma_data->maxburst = 1;
>> +
>> +	df_params.rate = substream->runtime->rate;
>> +	df_params.sample_bits = substream->runtime->sample_bits;
>> +	df_params.max_scaling = &priv->max_scaling;
>> +
>> +	return pdata->ops->set_hwparam(pdata->adc, &df_params);
>> +}
>> +
>> +static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
>> +				struct snd_soc_dai *dai)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +
>> +	switch (cmd) {
>> +	case SNDRV_PCM_TRIGGER_START:
>> +	case SNDRV_PCM_TRIGGER_RESUME:
>> +		return pdata->ops->audio_startup(pdata->adc);
>> +	case SNDRV_PCM_TRIGGER_SUSPEND:
>> +	case SNDRV_PCM_TRIGGER_STOP:
>> +		pdata->ops->audio_shutdown(pdata->adc);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
>> +
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +
>> +	if ((cb == SND_SOC_DAIFMT_CBM_CFM) || (cb == SND_SOC_DAIFMT_CBM_CFS)) {
>> +		/* Digital microphone is clocked by external clock */
>> +		if (!priv->dmic_clk) {
>> +			dev_err(dai->dev,
>> +				"system-clock-frequency not defined\n");
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +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);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +	if (dir == SND_SOC_CLOCK_IN) {
>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>> +		priv->dmic_clk = freq;
>> +	}
>> +
>> +	/* Determine supported rate which depends on SPI/manchester clock */
>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
>> +}
>> +
>> +static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
>> +	.startup = stm32_adfsdm_startup,
>> +	.shutdown = stm32_adfsdm_shutdown,
>> +	.hw_params = stm32_adfsdm_dai_hw_params,
>> +	.set_fmt = stm32_adfsdm_set_dai_fmt,
>> +	.set_sysclk = stm32_adfsdm_set_sysclk,
>> +	.trigger = stm32_adfsdm_trigger,
>> +};
>> +
>> +static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
>> +{
>> +	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
>> +	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +
>> +	/* DMA settings */
>> +	snd_soc_dai_init_dma_data(dai, NULL, dma);
>> +	dma->addr = pdata->ops->get_dma_source(pdata->adc);
>> +	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +
>> +	pdata->ops->register_xrun_cb(priv->pdata->adc, stm32_dfsdm_xrun, dai);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>> +{
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
>> +	.capture = {
>> +		    .channels_min = 1,
>> +		    .channels_max = 1,
>> +		    .formats = SNDRV_PCM_FMTBIT_S24_LE,
>> +		    .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
>> +			      SNDRV_PCM_RATE_32000),
>> +		    },
>> +	.probe = stm32_adfsdm_dai_probe,
>> +	.remove = stm32_adfsdm_dai_remove,
>> +	.ops = &stm32_adfsdm_dai_ops,
>> +};
>> +
>> +static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
>> +	.name = "sti_cpu_dai",
>> +};
>> +
>> +static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
>> +	.pcm_hardware = &stm32_adfsdm_pcm_hw,
>> +	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
>> +	.copy = stm32_adfsdm_copy,
>> +};
>> +
>> +static int stm32_adfsdm_probe(struct platform_device *pdev)
>> +{
>> +	struct stm32_adfsdm_priv *priv;
>> +	struct stm32_adfsdm_pdata *pdata = pdev->dev.platform_data;
>> +	int ret;
>> +
>> +	dev_dbg(&pdev->dev, "%s: enter for node %p\n", __func__,
>> +		pdev->dev.parent->of_node->name);
>> +
>> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> +	if (!priv)
>> +		return -ENOMEM;
>> +
>> +	priv->pdata = pdata;
>> +
>> +	priv->dai_drv = stm32_adfsdm_dai;
>> +	priv->dai_drv.name = pdev->dev.parent->of_node->name;
>> +	priv->dai_drv.capture.stream_name = pdev->dev.parent->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;
>> +
>> +	ret = devm_snd_dmaengine_pcm_register(pdev->dev.parent,
>> +					      &dmaengine_pcm_config, 0);
>> +	if (ret < 0)
>> +		dev_err(&pdev->dev, "failed to register dma pcm config\n");
>> +
>> +	return ret;
>> +}
>> +
>> +static struct platform_driver stm32_adfsdm_driver = {
>> +	.driver = {
>> +		   .name = STM32_ADFSDM_DRV_NAME,
>> +		   },
>> +	.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);
>>
> 

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-14 11:09           ` Arnaud Pouliquen
@ 2017-02-14 12:57               ` Peter Meerwald-Stadler
  -1 siblings, 0 replies; 107+ messages in thread
From: Peter Meerwald-Stadler @ 2017-02-14 12:57 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Jonathan Cameron, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA

Hallo Arnaud,

> Thanks for your highlight on typos.
> I did not pay to much attention on it, as i should for a patch candidate
> for upstream. Surely wrongly...
> For your information, aim of this RFC is to discuss the way to integrate
> the DFSDM drivers in ASoC and IIO. So the patches associated can not be
> upstreamed in current version.

I've seen the RFC tag in the subject; the extra effort to pick out some 
trivial mistakes is only little additional work when reading the code, I 
though it might be useful...

the overall design it quite hard to grasp...

regards, p.

-- 

Peter Meerwald-Stadler
+43-664-2444418 (mobile)
--
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] 107+ messages in thread

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-14 12:57               ` Peter Meerwald-Stadler
  0 siblings, 0 replies; 107+ messages in thread
From: Peter Meerwald-Stadler @ 2017-02-14 12:57 UTC (permalink / raw)
  To: Arnaud Pouliquen; +Cc: Rob Herring, Jonathan Cameron, devicetree, linux-iio

Hallo Arnaud,

> Thanks for your highlight on typos.
> I did not pay to much attention on it, as i should for a patch candidate
> for upstream. Surely wrongly...
> For your information, aim of this RFC is to discuss the way to integrate
> the DFSDM drivers in ASoC and IIO. So the patches associated can not be
> upstreamed in current version.

I've seen the RFC tag in the subject; the extra effort to pick out some 
trivial mistakes is only little additional work when reading the code, I 
though it might be useful...

the overall design it quite hard to grasp...

regards, p.

-- 

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

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
  2017-02-13 16:38   ` Arnaud Pouliquen
  (?)
@ 2017-02-14 17:16     ` Mark Brown
  -1 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-14 17:16 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, Rob Herring, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre Torgue


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

On Mon, Feb 13, 2017 at 05:38:26PM +0100, Arnaud Pouliquen wrote:
> From: olivier moysan <olivier.moysan@st.com>
> 
> Add copy support in pcm damengine operations.
> As example, this allows to:
> - process data before rendering (IEC status insertion),
> - process captured sample ( sample mask and shift).

dmaengine with copy...  so not DMA at all?  I'm a bit confused about why
we're extending the dmaengine code to have a copy operation rather than
just writing driver code.

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-14 17:16     ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-14 17:16 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, 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: 489 bytes --]

On Mon, Feb 13, 2017 at 05:38:26PM +0100, Arnaud Pouliquen wrote:
> From: olivier moysan <olivier.moysan@st.com>
> 
> Add copy support in pcm damengine operations.
> As example, this allows to:
> - process data before rendering (IEC status insertion),
> - process captured sample ( sample mask and shift).

dmaengine with copy...  so not DMA at all?  I'm a bit confused about why
we're extending the dmaengine code to have a copy operation rather than
just writing driver code.

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

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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-14 17:16     ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-14 17:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 13, 2017 at 05:38:26PM +0100, Arnaud Pouliquen wrote:
> From: olivier moysan <olivier.moysan@st.com>
> 
> Add copy support in pcm damengine operations.
> As example, this allows to:
> - process data before rendering (IEC status insertion),
> - process captured sample ( sample mask and shift).

dmaengine with copy...  so not DMA at all?  I'm a bit confused about why
we're extending the dmaengine code to have a copy operation rather than
just writing driver code.
-------------- 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/20170214/1121fff1/attachment.sig>

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-13 16:38   ` Arnaud Pouliquen
  (?)
@ 2017-02-14 17:45       ` Mark Brown
  -1 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-14 17:45 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

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

On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:

This looks basically fine as a system specific driver but as some of the
comments in here say there's bits of it could perhaps be genericised but
I'm not sure we need to do that right now.  I'm not sure the abstraction
is exactly comfortable but having another bit of hardware doing a bridge
to IIO might be the best way to figure out something better.

> +	.period_bytes_min = 40, /* 8 khz 5 ms */
> +	.period_bytes_max = 4 * PAGE_SIZE,
> +	.buffer_bytes_max = 16 * PAGE_SIZE

What's the physical minimum period limit?  The comment makes this sound
like it's just made up.

> +	unsigned int shift = 24 -priv->max_scaling;
> +	

Missing space after -.

> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	return 0;
> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_RATE,
> +					  &priv->rates_const);

Looks like debug changes got left in?

> +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);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +	if (dir == SND_SOC_CLOCK_IN) {
> +		pdata->ops->set_sysclk(pdata->adc, freq);
> +		priv->dmic_clk = freq;
> +	}
> +
> +	/* Determine supported rate which depends on SPI/manchester clock */
> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);

Since the DAI is unidirectional it doesn't matter but obviously if it
weren't then the fact that getting the supported rates involves setting
the hwparams means this could become disruptive.  If we're going to
genericise this to be a more general IIO/ASoC bridge that could matter.

> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
> +{
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +
> +	return 0;
> +}

Remove empty functions, though in this case I think you want to add
something to disconnect the XRUN callback just in order to be sure it
can't be mistakenly called.

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

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-14 17:45       ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-14 17:45 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, 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: 2194 bytes --]

On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:

This looks basically fine as a system specific driver but as some of the
comments in here say there's bits of it could perhaps be genericised but
I'm not sure we need to do that right now.  I'm not sure the abstraction
is exactly comfortable but having another bit of hardware doing a bridge
to IIO might be the best way to figure out something better.

> +	.period_bytes_min = 40, /* 8 khz 5 ms */
> +	.period_bytes_max = 4 * PAGE_SIZE,
> +	.buffer_bytes_max = 16 * PAGE_SIZE

What's the physical minimum period limit?  The comment makes this sound
like it's just made up.

> +	unsigned int shift = 24 -priv->max_scaling;
> +	

Missing space after -.

> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	return 0;
> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_RATE,
> +					  &priv->rates_const);

Looks like debug changes got left in?

> +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);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +	if (dir == SND_SOC_CLOCK_IN) {
> +		pdata->ops->set_sysclk(pdata->adc, freq);
> +		priv->dmic_clk = freq;
> +	}
> +
> +	/* Determine supported rate which depends on SPI/manchester clock */
> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);

Since the DAI is unidirectional it doesn't matter but obviously if it
weren't then the fact that getting the supported rates involves setting
the hwparams means this could become disruptive.  If we're going to
genericise this to be a more general IIO/ASoC bridge that could matter.

> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
> +{
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +
> +	return 0;
> +}

Remove empty functions, though in this case I think you want to add
something to disconnect the XRUN callback just in order to be sure it
can't be mistakenly called.

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

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

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-14 17:45       ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-14 17:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:

This looks basically fine as a system specific driver but as some of the
comments in here say there's bits of it could perhaps be genericised but
I'm not sure we need to do that right now.  I'm not sure the abstraction
is exactly comfortable but having another bit of hardware doing a bridge
to IIO might be the best way to figure out something better.

> +	.period_bytes_min = 40, /* 8 khz 5 ms */
> +	.period_bytes_max = 4 * PAGE_SIZE,
> +	.buffer_bytes_max = 16 * PAGE_SIZE

What's the physical minimum period limit?  The comment makes this sound
like it's just made up.

> +	unsigned int shift = 24 -priv->max_scaling;
> +	

Missing space after -.

> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
> +	return 0;
> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
> +					  SNDRV_PCM_HW_PARAM_RATE,
> +					  &priv->rates_const);

Looks like debug changes got left in?

> +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);
> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
> +
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +	if (dir == SND_SOC_CLOCK_IN) {
> +		pdata->ops->set_sysclk(pdata->adc, freq);
> +		priv->dmic_clk = freq;
> +	}
> +
> +	/* Determine supported rate which depends on SPI/manchester clock */
> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);

Since the DAI is unidirectional it doesn't matter but obviously if it
weren't then the fact that getting the supported rates involves setting
the hwparams means this could become disruptive.  If we're going to
genericise this to be a more general IIO/ASoC bridge that could matter.

> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
> +{
> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
> +
> +	return 0;
> +}

Remove empty functions, though in this case I think you want to add
something to disconnect the XRUN callback just in order to be sure it
can't be mistakenly called.
-------------- 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/20170214/934596be/attachment.sig>

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
  2017-02-14 17:16     ` Mark Brown
  (?)
@ 2017-02-15 13:59       ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 13:59 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Olivier MOYSAN, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Rob Herring, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre TORGUE

Hello Mark,

On 02/14/2017 06:16 PM, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:26PM +0100, Arnaud Pouliquen wrote:
>> From: olivier moysan <olivier.moysan@st.com>
>> 
>> Add copy support in pcm damengine operations. As example, this
>> allows to: - process data before rendering (IEC status
>> insertion), - process captured sample ( sample mask and shift).
> 
> dmaengine with copy...  so not DMA at all?  I'm a bit confused
> about why we're extending the dmaengine code to have a copy
> operation rather than just writing driver code.
> 

For DFSDM we need to re-arrange samples after DMA transfer, before
sending them to application.
For this, a copy ops(called in snd_pcm_lib_read_transfer) alreday exists
in snd_pcm_ops struct.
This ops is not available at soc dma engine level, so at ASoC driver
level.
Aim of this patch is to expose the copy in driver. DMA is still used,
copy ops just allows to handle the copy_to_user in driver.
Advantage of this add-on is that we still able to take benefice of the
generic DMA in DFSDM driver.
Now if you estimate that it is too stm32 SOC specific, the other
solution is to handle directly the PCM platform device in DFSDM driver.

Regards
Arnaud

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-15 13:59       ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 13:59 UTC (permalink / raw)
  To: Mark Brown
  Cc: Rob Herring, 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

Hello Mark,

On 02/14/2017 06:16 PM, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:26PM +0100, Arnaud Pouliquen wrote:
>> From: olivier moysan <olivier.moysan@st.com>
>> 
>> Add copy support in pcm damengine operations. As example, this
>> allows to: - process data before rendering (IEC status
>> insertion), - process captured sample ( sample mask and shift).
> 
> dmaengine with copy...  so not DMA at all?  I'm a bit confused
> about why we're extending the dmaengine code to have a copy
> operation rather than just writing driver code.
> 

For DFSDM we need to re-arrange samples after DMA transfer, before
sending them to application.
For this, a copy ops(called in snd_pcm_lib_read_transfer) alreday exists
in snd_pcm_ops struct.
This ops is not available at soc dma engine level, so at ASoC driver
level.
Aim of this patch is to expose the copy in driver. DMA is still used,
copy ops just allows to handle the copy_to_user in driver.
Advantage of this add-on is that we still able to take benefice of the
generic DMA in DFSDM driver.
Now if you estimate that it is too stm32 SOC specific, the other
solution is to handle directly the PCM platform device in DFSDM driver.

Regards
Arnaud

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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-15 13:59       ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 13:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

On 02/14/2017 06:16 PM, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:26PM +0100, Arnaud Pouliquen wrote:
>> From: olivier moysan <olivier.moysan@st.com>
>> 
>> Add copy support in pcm damengine operations. As example, this
>> allows to: - process data before rendering (IEC status
>> insertion), - process captured sample ( sample mask and shift).
> 
> dmaengine with copy...  so not DMA at all?  I'm a bit confused
> about why we're extending the dmaengine code to have a copy
> operation rather than just writing driver code.
> 

For DFSDM we need to re-arrange samples after DMA transfer, before
sending them to application.
For this, a copy ops(called in snd_pcm_lib_read_transfer) alreday exists
in snd_pcm_ops struct.
This ops is not available at soc dma engine level, so at ASoC driver
level.
Aim of this patch is to expose the copy in driver. DMA is still used,
copy ops just allows to handle the copy_to_user in driver.
Advantage of this add-on is that we still able to take benefice of the
generic DMA in DFSDM driver.
Now if you estimate that it is too stm32 SOC specific, the other
solution is to handle directly the PCM platform device in DFSDM driver.

Regards
Arnaud

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
  2017-02-15 13:59       ` Arnaud Pouliquen
  (?)
@ 2017-02-15 14:53           ` Mark Brown
  -1 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-15 14:53 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

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

On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:

> For DFSDM we need to re-arrange samples after DMA transfer, before
> sending them to application.
> For this, a copy ops(called in snd_pcm_lib_read_transfer) alreday exists
> in snd_pcm_ops struct.

No, copy() is an alternative to doing DMA rather than something that
works with DMA.  If you want to have some sort of post processing
operation it shoudn't be called copy().  It's probably worth considering
just making a new format and letting userspace convert things here.

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

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-15 14:53           ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-15 14:53 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, 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: 544 bytes --]

On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:

> For DFSDM we need to re-arrange samples after DMA transfer, before
> sending them to application.
> For this, a copy ops(called in snd_pcm_lib_read_transfer) alreday exists
> in snd_pcm_ops struct.

No, copy() is an alternative to doing DMA rather than something that
works with DMA.  If you want to have some sort of post processing
operation it shoudn't be called copy().  It's probably worth considering
just making a new format and letting userspace convert things here.

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

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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-15 14:53           ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-15 14:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:

> For DFSDM we need to re-arrange samples after DMA transfer, before
> sending them to application.
> For this, a copy ops(called in snd_pcm_lib_read_transfer) alreday exists
> in snd_pcm_ops struct.

No, copy() is an alternative to doing DMA rather than something that
works with DMA.  If you want to have some sort of post processing
operation it shoudn't be called copy().  It's probably worth considering
just making a new format and letting userspace convert things here.
-------------- 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/20170215/be4ffe3f/attachment.sig>

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
  2017-02-15 14:53           ` Mark Brown
  (?)
@ 2017-02-15 15:46             ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 15:46 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Olivier MOYSAN, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Rob Herring, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre TORGUE



On 02/15/2017 03:53 PM, Mark Brown wrote:
> On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:
> 
>> For DFSDM we need to re-arrange samples after DMA transfer,
>> before sending them to application. For this, a copy ops(called
>> in snd_pcm_lib_read_transfer) alreday exists in snd_pcm_ops
>> struct.
> 
> No, copy() is an alternative to doing DMA rather than something
> that works with DMA.  If you want to have some sort of post
> processing operation it shoudn't be called copy().  It's probably
> worth considering just making a new format and letting userspace
> convert things here.
> 
You mean using a plug-in alsa to do it? this seems not possible with
tiny-alsa...
Without a plug-in, driver will not be compatible with standard audio
application.

I'm newbies in alsa plugin But it seems quite tricky to do it. This
solution means that userspace has to be aware on hardware constraint.
Processing to do is a mask on 8 LSB + a left shift on bits. T shift
value depends on several parameters: the SPI frequency, the sampling
rate and the filter parameters.
That means an alsa control to get scaling, handled by the plugin.

So i would prefer to find a way to do it in driver...

I saw that blackfin driver implements this kind of mechanism
(bf5xx_pcm_copy) to support the TDM mode. if i well understood the code
they process the TDM buffer filled by DMA. Samples are extracted from
the good TDM slots and copied in buffer that will be sent to userland.

Do you think this would be reasonable if i implement something similar
in my driver, without using the soc generic DMA engine?

Regards
Arnaud

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-15 15:46             ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 15:46 UTC (permalink / raw)
  To: Mark Brown
  Cc: Rob Herring, 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



On 02/15/2017 03:53 PM, Mark Brown wrote:
> On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:
> 
>> For DFSDM we need to re-arrange samples after DMA transfer,
>> before sending them to application. For this, a copy ops(called
>> in snd_pcm_lib_read_transfer) alreday exists in snd_pcm_ops
>> struct.
> 
> No, copy() is an alternative to doing DMA rather than something
> that works with DMA.  If you want to have some sort of post
> processing operation it shoudn't be called copy().  It's probably
> worth considering just making a new format and letting userspace
> convert things here.
> 
You mean using a plug-in alsa to do it? this seems not possible with
tiny-alsa...
Without a plug-in, driver will not be compatible with standard audio
application.

I'm newbies in alsa plugin But it seems quite tricky to do it. This
solution means that userspace has to be aware on hardware constraint.
Processing to do is a mask on 8 LSB + a left shift on bits. T shift
value depends on several parameters: the SPI frequency, the sampling
rate and the filter parameters.
That means an alsa control to get scaling, handled by the plugin.

So i would prefer to find a way to do it in driver...

I saw that blackfin driver implements this kind of mechanism
(bf5xx_pcm_copy) to support the TDM mode. if i well understood the code
they process the TDM buffer filled by DMA. Samples are extracted from
the good TDM slots and copied in buffer that will be sent to userland.

Do you think this would be reasonable if i implement something similar
in my driver, without using the soc generic DMA engine?

Regards
Arnaud



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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-15 15:46             ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 15:46 UTC (permalink / raw)
  To: linux-arm-kernel



On 02/15/2017 03:53 PM, Mark Brown wrote:
> On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:
> 
>> For DFSDM we need to re-arrange samples after DMA transfer,
>> before sending them to application. For this, a copy ops(called
>> in snd_pcm_lib_read_transfer) alreday exists in snd_pcm_ops
>> struct.
> 
> No, copy() is an alternative to doing DMA rather than something
> that works with DMA.  If you want to have some sort of post
> processing operation it shoudn't be called copy().  It's probably
> worth considering just making a new format and letting userspace
> convert things here.
> 
You mean using a plug-in alsa to do it? this seems not possible with
tiny-alsa...
Without a plug-in, driver will not be compatible with standard audio
application.

I'm newbies in alsa plugin But it seems quite tricky to do it. This
solution means that userspace has to be aware on hardware constraint.
Processing to do is a mask on 8 LSB + a left shift on bits. T shift
value depends on several parameters: the SPI frequency, the sampling
rate and the filter parameters.
That means an alsa control to get scaling, handled by the plugin.

So i would prefer to find a way to do it in driver...

I saw that blackfin driver implements this kind of mechanism
(bf5xx_pcm_copy) to support the TDM mode. if i well understood the code
they process the TDM buffer filled by DMA. Samples are extracted from
the good TDM slots and copied in buffer that will be sent to userland.

Do you think this would be reasonable if i implement something similar
in my driver, without using the soc generic DMA engine?

Regards
Arnaud

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-14 17:45       ` Mark Brown
  (?)
@ 2017-02-15 16:39         ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 16:39 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Olivier MOYSAN, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Rob Herring, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre TORGUE



On 02/14/2017 06:45 PM, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
> 
> This looks basically fine as a system specific driver but as some 
> of the comments in here say there's bits of it could perhaps be 
> genericised but I'm not sure we need to do that right now.  I'm not
> sure the abstraction is exactly comfortable but having another bit
> of hardware doing a bridge to IIO might be the best way to figure
> out something better.
> 
Yes this is one point to clarify. I keep it as a specific API, as i
don't know if another hardware needs to support it...
I would say that objective of this version is to highlight interactions.
Then i can rework it in a way that could be  genericised in future (
i.e. using void* for params that would depend on platforms)...

Extend IIO customer API would be also another alternative, but i did
not find a generic way to do it. Some IIO attributes could be used for
Hw params but DMA and IRQs seems tricky to handle through this IIO
interface...




>> +	.period_bytes_min = 40, /* 8 khz 5 ms */ +	.period_bytes_max =
>>  4 * PAGE_SIZE, +	.buffer_bytes_max = 16 * PAGE_SIZE
> 
> What's the physical minimum period limit?  The comment makes this 
> sound like it's just made up.
I did not find physical minimum period limit, that why i considered
this value from a scheduling point of view. I will re-check these values.

> 
>> +	unsigned int shift = 24 -priv->max_scaling; +
> 
> Missing space after -.
> 
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__); +	return 0; + 
>> return snd_pcm_hw_constraint_list(substream->runtime, 0, + 
>> SNDRV_PCM_HW_PARAM_RATE, +					  &priv->rates_const);
> 
> Looks like debug changes got left in?
yes exactly...
> 
>> +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); +	struct
>>  stm32_adfsdm_pdata *pdata = priv->pdata; + +	dev_dbg(dai->dev, 
>> "%s: enter for dai %d\n", __func__, dai->id); +	if (dir == 
>> SND_SOC_CLOCK_IN) { +		pdata->ops->set_sysclk(pdata->adc, freq); 
>> +		priv->dmic_clk = freq; +	} + +	/* Determine supported rate 
>> which depends on SPI/manchester clock */ +	return 
>> stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
> 
> Since the DAI is unidirectional it doesn't matter but obviously if
>  it weren't then the fact that getting the supported rates involves
>  setting the hwparams means this could become disruptive.  If we're
>  going to genericise this to be a more general IIO/ASoC bridge that
>  could matter.
I think that the driver itself can not be generic. ST DFSDM is too
specific. Only API with IIO could be.

> 
>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai) +{ +
>> dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id); +
>> +	return 0; +}
> 
> Remove empty functions, though in this case I think you want to
> add something to disconnect the XRUN callback just in order to be
> sure it can't be mistakenly called.

Yes Completely unnecessary here. Furthermore the driver should be
removed by the IIO driver.

Regards
Arnaud

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-15 16:39         ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 16:39 UTC (permalink / raw)
  To: Mark Brown
  Cc: Rob Herring, 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



On 02/14/2017 06:45 PM, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
> 
> This looks basically fine as a system specific driver but as some 
> of the comments in here say there's bits of it could perhaps be 
> genericised but I'm not sure we need to do that right now.  I'm not
> sure the abstraction is exactly comfortable but having another bit
> of hardware doing a bridge to IIO might be the best way to figure
> out something better.
> 
Yes this is one point to clarify. I keep it as a specific API, as i
don't know if another hardware needs to support it...
I would say that objective of this version is to highlight interactions.
Then i can rework it in a way that could be  genericised in future (
i.e. using void* for params that would depend on platforms)...

Extend IIO customer API would be also another alternative, but i did
not find a generic way to do it. Some IIO attributes could be used for
Hw params but DMA and IRQs seems tricky to handle through this IIO
interface...




>> +	.period_bytes_min = 40, /* 8 khz 5 ms */ +	.period_bytes_max =
>>  4 * PAGE_SIZE, +	.buffer_bytes_max = 16 * PAGE_SIZE
> 
> What's the physical minimum period limit?  The comment makes this 
> sound like it's just made up.
I did not find physical minimum period limit, that why i considered
this value from a scheduling point of view. I will re-check these values.

> 
>> +	unsigned int shift = 24 -priv->max_scaling; +
> 
> Missing space after -.
> 
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__); +	return 0; + 
>> return snd_pcm_hw_constraint_list(substream->runtime, 0, + 
>> SNDRV_PCM_HW_PARAM_RATE, +					  &priv->rates_const);
> 
> Looks like debug changes got left in?
yes exactly...
> 
>> +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); +	struct
>>  stm32_adfsdm_pdata *pdata = priv->pdata; + +	dev_dbg(dai->dev, 
>> "%s: enter for dai %d\n", __func__, dai->id); +	if (dir == 
>> SND_SOC_CLOCK_IN) { +		pdata->ops->set_sysclk(pdata->adc, freq); 
>> +		priv->dmic_clk = freq; +	} + +	/* Determine supported rate 
>> which depends on SPI/manchester clock */ +	return 
>> stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
> 
> Since the DAI is unidirectional it doesn't matter but obviously if
>  it weren't then the fact that getting the supported rates involves
>  setting the hwparams means this could become disruptive.  If we're
>  going to genericise this to be a more general IIO/ASoC bridge that
>  could matter.
I think that the driver itself can not be generic. ST DFSDM is too
specific. Only API with IIO could be.

> 
>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai) +{ +
>> dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id); +
>> +	return 0; +}
> 
> Remove empty functions, though in this case I think you want to
> add something to disconnect the XRUN callback just in order to be
> sure it can't be mistakenly called.

Yes Completely unnecessary here. Furthermore the driver should be
removed by the IIO driver.

Regards
Arnaud

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

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-15 16:39         ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 16:39 UTC (permalink / raw)
  To: linux-arm-kernel



On 02/14/2017 06:45 PM, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
> 
> This looks basically fine as a system specific driver but as some 
> of the comments in here say there's bits of it could perhaps be 
> genericised but I'm not sure we need to do that right now.  I'm not
> sure the abstraction is exactly comfortable but having another bit
> of hardware doing a bridge to IIO might be the best way to figure
> out something better.
> 
Yes this is one point to clarify. I keep it as a specific API, as i
don't know if another hardware needs to support it...
I would say that objective of this version is to highlight interactions.
Then i can rework it in a way that could be  genericised in future (
i.e. using void* for params that would depend on platforms)...

Extend IIO customer API would be also another alternative, but i did
not find a generic way to do it. Some IIO attributes could be used for
Hw params but DMA and IRQs seems tricky to handle through this IIO
interface...




>> +	.period_bytes_min = 40, /* 8 khz 5 ms */ +	.period_bytes_max =
>>  4 * PAGE_SIZE, +	.buffer_bytes_max = 16 * PAGE_SIZE
> 
> What's the physical minimum period limit?  The comment makes this 
> sound like it's just made up.
I did not find physical minimum period limit, that why i considered
this value from a scheduling point of view. I will re-check these values.

> 
>> +	unsigned int shift = 24 -priv->max_scaling; +
> 
> Missing space after -.
> 
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__); +	return 0; + 
>> return snd_pcm_hw_constraint_list(substream->runtime, 0, + 
>> SNDRV_PCM_HW_PARAM_RATE, +					  &priv->rates_const);
> 
> Looks like debug changes got left in?
yes exactly...
> 
>> +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); +	struct
>>  stm32_adfsdm_pdata *pdata = priv->pdata; + +	dev_dbg(dai->dev, 
>> "%s: enter for dai %d\n", __func__, dai->id); +	if (dir == 
>> SND_SOC_CLOCK_IN) { +		pdata->ops->set_sysclk(pdata->adc, freq); 
>> +		priv->dmic_clk = freq; +	} + +	/* Determine supported rate 
>> which depends on SPI/manchester clock */ +	return 
>> stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
> 
> Since the DAI is unidirectional it doesn't matter but obviously if
>  it weren't then the fact that getting the supported rates involves
>  setting the hwparams means this could become disruptive.  If we're
>  going to genericise this to be a more general IIO/ASoC bridge that
>  could matter.
I think that the driver itself can not be generic. ST DFSDM is too
specific. Only API with IIO could be.

> 
>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai) +{ +
>> dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id); +
>> +	return 0; +}
> 
> Remove empty functions, though in this case I think you want to
> add something to disconnect the XRUN callback just in order to be
> sure it can't be mistakenly called.

Yes Completely unnecessary here. Furthermore the driver should be
removed by the IIO driver.

Regards
Arnaud

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-15 16:39         ` Arnaud Pouliquen
  (?)
@ 2017-02-15 16:53             ` Mark Brown
  -1 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-15 16:53 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

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

On Wed, Feb 15, 2017 at 05:39:53PM +0100, Arnaud Pouliquen wrote:
> On 02/14/2017 06:45 PM, Mark Brown wrote:

> > What's the physical minimum period limit?  The comment makes this 
> > sound like it's just made up.

> I did not find physical minimum period limit, that why i considered
> this value from a scheduling point of view. I will re-check these values.

If there's no physical limit the minimum is one byte, it's the
application's problem if it's not actually able to sustain what it
chooses.

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

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-15 16:53             ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-15 16:53 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, 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: 515 bytes --]

On Wed, Feb 15, 2017 at 05:39:53PM +0100, Arnaud Pouliquen wrote:
> On 02/14/2017 06:45 PM, Mark Brown wrote:

> > What's the physical minimum period limit?  The comment makes this 
> > sound like it's just made up.

> I did not find physical minimum period limit, that why i considered
> this value from a scheduling point of view. I will re-check these values.

If there's no physical limit the minimum is one byte, it's the
application's problem if it's not actually able to sustain what it
chooses.

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

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

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-15 16:53             ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-15 16:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Feb 15, 2017 at 05:39:53PM +0100, Arnaud Pouliquen wrote:
> On 02/14/2017 06:45 PM, Mark Brown wrote:

> > What's the physical minimum period limit?  The comment makes this 
> > sound like it's just made up.

> I did not find physical minimum period limit, that why i considered
> this value from a scheduling point of view. I will re-check these values.

If there's no physical limit the minimum is one byte, it's the
application's problem if it's not actually able to sustain what it
chooses.
-------------- 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/20170215/0479c802/attachment.sig>

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
  2017-02-15 15:46             ` Arnaud Pouliquen
  (?)
@ 2017-02-16 20:14                 ` Mark Brown
  -1 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-16 20:14 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

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

On Wed, Feb 15, 2017 at 04:46:44PM +0100, Arnaud Pouliquen wrote:
> On 02/15/2017 03:53 PM, Mark Brown wrote:
> > On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:

> > No, copy() is an alternative to doing DMA rather than something
> > that works with DMA.  If you want to have some sort of post
> > processing operation it shoudn't be called copy().  It's probably
> > worth considering just making a new format and letting userspace
> > convert things here.

> You mean using a plug-in alsa to do it? this seems not possible with
> tiny-alsa...
> Without a plug-in, driver will not be compatible with standard audio
> application.

Right.  But if you're using tinyalsa you've got some open coded system
specific stuff anyway!

> Do you think this would be reasonable if i implement something similar
> in my driver, without using the soc generic DMA engine?

Probably.  It might even be possible to fit it elegently into the
generic dmaengine code, just don't say it's a copy() operation since
that's a specific thing in ALSA.

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

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-16 20:14                 ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-16 20:14 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Rob Herring, 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: 1046 bytes --]

On Wed, Feb 15, 2017 at 04:46:44PM +0100, Arnaud Pouliquen wrote:
> On 02/15/2017 03:53 PM, Mark Brown wrote:
> > On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:

> > No, copy() is an alternative to doing DMA rather than something
> > that works with DMA.  If you want to have some sort of post
> > processing operation it shoudn't be called copy().  It's probably
> > worth considering just making a new format and letting userspace
> > convert things here.

> You mean using a plug-in alsa to do it? this seems not possible with
> tiny-alsa...
> Without a plug-in, driver will not be compatible with standard audio
> application.

Right.  But if you're using tinyalsa you've got some open coded system
specific stuff anyway!

> Do you think this would be reasonable if i implement something similar
> in my driver, without using the soc generic DMA engine?

Probably.  It might even be possible to fit it elegently into the
generic dmaengine code, just don't say it's a copy() operation since
that's a specific thing in ALSA.

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

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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-16 20:14                 ` Mark Brown
  0 siblings, 0 replies; 107+ messages in thread
From: Mark Brown @ 2017-02-16 20:14 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Feb 15, 2017 at 04:46:44PM +0100, Arnaud Pouliquen wrote:
> On 02/15/2017 03:53 PM, Mark Brown wrote:
> > On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen wrote:

> > No, copy() is an alternative to doing DMA rather than something
> > that works with DMA.  If you want to have some sort of post
> > processing operation it shoudn't be called copy().  It's probably
> > worth considering just making a new format and letting userspace
> > convert things here.

> You mean using a plug-in alsa to do it? this seems not possible with
> tiny-alsa...
> Without a plug-in, driver will not be compatible with standard audio
> application.

Right.  But if you're using tinyalsa you've got some open coded system
specific stuff anyway!

> Do you think this would be reasonable if i implement something similar
> in my driver, without using the soc generic DMA engine?

Probably.  It might even be possible to fit it elegently into the
generic dmaengine code, just don't say it's a copy() operation since
that's a specific thing in ALSA.
-------------- 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/20170216/2421aec1/attachment.sig>

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

* Re: [RFC v2 1/7] iio: Add hardware consumer support
  2017-02-13 16:38   ` Arnaud Pouliquen
  (?)
@ 2017-02-19 14:13       ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:13 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 13/02/17 16:38, Arnaud Pouliquen wrote:
> 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-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
Quick process point. If you are 'handling' a patch like this from someone
else you should also sign off on it as well as the original author. We
need to keep track of the path a patch took as well as who originally wrote
it.

Clearly this is an RFC so some of the comments below don't really apply at
this stage. It's easy to note them whilst reading though so I have done
so!

It's interesting to see explicit support in here for multiplexing streams
from different iio_devs.

Anyhow, guess I'd better look at how it is used before drawing too many
conclusions :)
> ---
>  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
Needs some proper help that actually tells us what it is for.
> +
> +
>  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,
> +};
> +
I would like to see some kernel-doc description here. In particular the
fact that it is only getting buffers if they haven't already been obtained
should be highlighted.
> +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);
Chances are these are all from the same provider device so this is a little
interesting at first glance.  Ah, the get_buffer is handling that. Fair enough
though will want comments to make that clear.
> +		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);
This seems to be putting a lot of buffers we don't (at first glance)
seem to have 'gotten' - hence please add a comment saying where they came
from.
> +	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);
This is interesting.  It will build the demux table etc, but I suppose
that doesn't matter if the very nature of thing is that we don't actually
have any data flowing...
> +		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
> 

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

* Re: [RFC v2 1/7] iio: Add hardware consumer support
@ 2017-02-19 14:13       ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:13 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 13/02/17 16:38, Arnaud Pouliquen wrote:
> 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>
Quick process point. If you are 'handling' a patch like this from someone
else you should also sign off on it as well as the original author. We
need to keep track of the path a patch took as well as who originally wrote
it.

Clearly this is an RFC so some of the comments below don't really apply at
this stage. It's easy to note them whilst reading though so I have done
so!

It's interesting to see explicit support in here for multiplexing streams
from different iio_devs.

Anyhow, guess I'd better look at how it is used before drawing too many
conclusions :)
> ---
>  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
Needs some proper help that actually tells us what it is for.
> +
> +
>  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,
> +};
> +
I would like to see some kernel-doc description here. In particular the
fact that it is only getting buffers if they haven't already been obtained
should be highlighted.
> +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);
Chances are these are all from the same provider device so this is a little
interesting at first glance.  Ah, the get_buffer is handling that. Fair enough
though will want comments to make that clear.
> +		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);
This seems to be putting a lot of buffers we don't (at first glance)
seem to have 'gotten' - hence please add a comment saying where they came
from.
> +	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);
This is interesting.  It will build the demux table etc, but I suppose
that doesn't matter if the very nature of thing is that we don't actually
have any data flowing...
> +		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] 107+ messages in thread

* [RFC v2 1/7] iio: Add hardware consumer support
@ 2017-02-19 14:13       ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:13 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/02/17 16:38, Arnaud Pouliquen wrote:
> 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>
Quick process point. If you are 'handling' a patch like this from someone
else you should also sign off on it as well as the original author. We
need to keep track of the path a patch took as well as who originally wrote
it.

Clearly this is an RFC so some of the comments below don't really apply at
this stage. It's easy to note them whilst reading though so I have done
so!

It's interesting to see explicit support in here for multiplexing streams
from different iio_devs.

Anyhow, guess I'd better look at how it is used before drawing too many
conclusions :)
> ---
>  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
Needs some proper help that actually tells us what it is for.
> +
> +
>  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,
> +};
> +
I would like to see some kernel-doc description here. In particular the
fact that it is only getting buffers if they haven't already been obtained
should be highlighted.
> +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);
Chances are these are all from the same provider device so this is a little
interesting at first glance.  Ah, the get_buffer is handling that. Fair enough
though will want comments to make that clear.
> +		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);
This seems to be putting a lot of buffers we don't (at first glance)
seem to have 'gotten' - hence please add a comment saying where they came
from.
> +	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);
This is interesting.  It will build the demux table etc, but I suppose
that doesn't matter if the very nature of thing is that we don't actually
have any data flowing...
> +		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] 107+ messages in thread

* Re: [RFC v2 3/7] IIO: ADC: add sigma delta modulator support
  2017-02-13 16:38   ` Arnaud Pouliquen
  (?)
@ 2017-02-19 14:20       ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:20 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 13/02/17 16:38, Arnaud Pouliquen wrote:
> Add dummy driver to support sigma delta modulators.
Interesting to see how you are pulling this together.

Trivial bits inline.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> ---
>  drivers/iio/adc/Kconfig         |  11 ++++
>  drivers/iio/adc/Makefile        |   1 +
>  drivers/iio/adc/simple_sd_adc.c | 112 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 124 insertions(+)
>  create mode 100644 drivers/iio/adc/simple_sd_adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e0b3c09..d4366ac 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 SIMPLE_SD_ADC
> +	tristate "Simple 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..bd67144 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_SIMPLE_SD_ADC) += simple_sd_adc.o
> diff --git a/drivers/iio/adc/simple_sd_adc.c b/drivers/iio/adc/simple_sd_adc.c
> new file mode 100644
> index 0000000..4bc8b3c
> --- /dev/null
> +++ b/drivers/iio/adc/simple_sd_adc.c
> @@ -0,0 +1,112 @@
> +/*
> + * simple sigma delta modulator driver
> + *
> + * Copyright (C) 2016, 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/iio/iio.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +
> +#include <linux/iio/triggered_buffer.h>
> +
> +static int simple_sd_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 simple_sd_iio_info = {
> +	.of_xlate = simple_sd_of_xlate,
> +};
> +
> +static const struct iio_buffer_setup_ops simple_sd_buffer_ops;
I guess we insist on having some setup ops. We could relax
that reasonably easily if this is going to become remotely
common (perhaps by providing such a default in the core).
> +
> +static int simple_sd_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct iio_dev *iio;
> +	struct iio_chan_spec *ch;
> +
> +	dev_dbg(&pdev->dev, "%s:\n", __func__);
it's an RFC so fair enough, but please remember to drop this
sort of output in the final version.

> +	iio = devm_iio_device_alloc(dev, 0);
> +	if (!iio)
> +		return -ENOMEM;
> +
> +	/* Define one channel */
> +	ch = devm_kzalloc(&iio->dev, sizeof(*ch), GFP_KERNEL);
> +	if (!ch)
> +		return -ENOMEM;
> +
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = dev->of_node;
> +	iio->name = dev_name(dev);
> +	iio->info = &simple_sd_iio_info;
> +	iio->modes = INDIO_BUFFER_HARDWARE;
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = 0;
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 1;
> +	ch->scan_type.storagebits = 1;
> +	ch->scan_type.shift = 0;
> +
> +	iio->num_channels = 1;
Static data (at lease currently) so do it as static const iio_chan_spec etc.
Interesting to see it being described like this.

I'd drop storagebits as it isn't 'stored' as such so this feels wrong and shift
is the default so drop that too.

> +	iio->channels = ch;
> +
> +	platform_set_drvdata(pdev, iio);
> +
> +	return iio_device_register(iio);
> +}
> +
> +static int simple_sd_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *iio = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(iio);
Having just the iio_device_unregister in a remove is a clear
indication that you could have gotten away with
devm_iio_device_register and dropped the remove entirely
as there would be nothing to do.
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id sd_adc_of_match[] = {
> +	{ .compatible = "sd-modulator" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, adc081c_of_match);
> +
> +static struct platform_driver simple_sd_adc = {
> +	.driver = {
> +		.name = "simple_sd_adc",
> +		.of_match_table = of_match_ptr(sd_adc_of_match),
> +	},
> +	.probe = simple_sd_probe,
> +	.remove = simple_sd_remove,
> +};
> +
> +module_platform_driver(simple_sd_adc);
> +
> +MODULE_DESCRIPTION("simple signma delta modulator");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [RFC v2 3/7] IIO: ADC: add sigma delta modulator support
@ 2017-02-19 14:20       ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:20 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 13/02/17 16:38, Arnaud Pouliquen wrote:
> Add dummy driver to support sigma delta modulators.
Interesting to see how you are pulling this together.

Trivial bits inline.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  drivers/iio/adc/Kconfig         |  11 ++++
>  drivers/iio/adc/Makefile        |   1 +
>  drivers/iio/adc/simple_sd_adc.c | 112 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 124 insertions(+)
>  create mode 100644 drivers/iio/adc/simple_sd_adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e0b3c09..d4366ac 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 SIMPLE_SD_ADC
> +	tristate "Simple 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..bd67144 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_SIMPLE_SD_ADC) += simple_sd_adc.o
> diff --git a/drivers/iio/adc/simple_sd_adc.c b/drivers/iio/adc/simple_sd_adc.c
> new file mode 100644
> index 0000000..4bc8b3c
> --- /dev/null
> +++ b/drivers/iio/adc/simple_sd_adc.c
> @@ -0,0 +1,112 @@
> +/*
> + * simple 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 simple_sd_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 simple_sd_iio_info = {
> +	.of_xlate = simple_sd_of_xlate,
> +};
> +
> +static const struct iio_buffer_setup_ops simple_sd_buffer_ops;
I guess we insist on having some setup ops. We could relax
that reasonably easily if this is going to become remotely
common (perhaps by providing such a default in the core).
> +
> +static int simple_sd_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct iio_dev *iio;
> +	struct iio_chan_spec *ch;
> +
> +	dev_dbg(&pdev->dev, "%s:\n", __func__);
it's an RFC so fair enough, but please remember to drop this
sort of output in the final version.

> +	iio = devm_iio_device_alloc(dev, 0);
> +	if (!iio)
> +		return -ENOMEM;
> +
> +	/* Define one channel */
> +	ch = devm_kzalloc(&iio->dev, sizeof(*ch), GFP_KERNEL);
> +	if (!ch)
> +		return -ENOMEM;
> +
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = dev->of_node;
> +	iio->name = dev_name(dev);
> +	iio->info = &simple_sd_iio_info;
> +	iio->modes = INDIO_BUFFER_HARDWARE;
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = 0;
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 1;
> +	ch->scan_type.storagebits = 1;
> +	ch->scan_type.shift = 0;
> +
> +	iio->num_channels = 1;
Static data (at lease currently) so do it as static const iio_chan_spec etc.
Interesting to see it being described like this.

I'd drop storagebits as it isn't 'stored' as such so this feels wrong and shift
is the default so drop that too.

> +	iio->channels = ch;
> +
> +	platform_set_drvdata(pdev, iio);
> +
> +	return iio_device_register(iio);
> +}
> +
> +static int simple_sd_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *iio = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(iio);
Having just the iio_device_unregister in a remove is a clear
indication that you could have gotten away with
devm_iio_device_register and dropped the remove entirely
as there would be nothing to do.
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id sd_adc_of_match[] = {
> +	{ .compatible = "sd-modulator" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, adc081c_of_match);
> +
> +static struct platform_driver simple_sd_adc = {
> +	.driver = {
> +		.name = "simple_sd_adc",
> +		.of_match_table = of_match_ptr(sd_adc_of_match),
> +	},
> +	.probe = simple_sd_probe,
> +	.remove = simple_sd_remove,
> +};
> +
> +module_platform_driver(simple_sd_adc);
> +
> +MODULE_DESCRIPTION("simple signma delta modulator");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> 


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

* [RFC v2 3/7] IIO: ADC: add sigma delta modulator support
@ 2017-02-19 14:20       ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:20 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/02/17 16:38, Arnaud Pouliquen wrote:
> Add dummy driver to support sigma delta modulators.
Interesting to see how you are pulling this together.

Trivial bits inline.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  drivers/iio/adc/Kconfig         |  11 ++++
>  drivers/iio/adc/Makefile        |   1 +
>  drivers/iio/adc/simple_sd_adc.c | 112 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 124 insertions(+)
>  create mode 100644 drivers/iio/adc/simple_sd_adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e0b3c09..d4366ac 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 SIMPLE_SD_ADC
> +	tristate "Simple 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..bd67144 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_SIMPLE_SD_ADC) += simple_sd_adc.o
> diff --git a/drivers/iio/adc/simple_sd_adc.c b/drivers/iio/adc/simple_sd_adc.c
> new file mode 100644
> index 0000000..4bc8b3c
> --- /dev/null
> +++ b/drivers/iio/adc/simple_sd_adc.c
> @@ -0,0 +1,112 @@
> +/*
> + * simple 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 simple_sd_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 simple_sd_iio_info = {
> +	.of_xlate = simple_sd_of_xlate,
> +};
> +
> +static const struct iio_buffer_setup_ops simple_sd_buffer_ops;
I guess we insist on having some setup ops. We could relax
that reasonably easily if this is going to become remotely
common (perhaps by providing such a default in the core).
> +
> +static int simple_sd_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct iio_dev *iio;
> +	struct iio_chan_spec *ch;
> +
> +	dev_dbg(&pdev->dev, "%s:\n", __func__);
it's an RFC so fair enough, but please remember to drop this
sort of output in the final version.

> +	iio = devm_iio_device_alloc(dev, 0);
> +	if (!iio)
> +		return -ENOMEM;
> +
> +	/* Define one channel */
> +	ch = devm_kzalloc(&iio->dev, sizeof(*ch), GFP_KERNEL);
> +	if (!ch)
> +		return -ENOMEM;
> +
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = dev->of_node;
> +	iio->name = dev_name(dev);
> +	iio->info = &simple_sd_iio_info;
> +	iio->modes = INDIO_BUFFER_HARDWARE;
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = 0;
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 1;
> +	ch->scan_type.storagebits = 1;
> +	ch->scan_type.shift = 0;
> +
> +	iio->num_channels = 1;
Static data (at lease currently) so do it as static const iio_chan_spec etc.
Interesting to see it being described like this.

I'd drop storagebits as it isn't 'stored' as such so this feels wrong and shift
is the default so drop that too.

> +	iio->channels = ch;
> +
> +	platform_set_drvdata(pdev, iio);
> +
> +	return iio_device_register(iio);
> +}
> +
> +static int simple_sd_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *iio = platform_get_drvdata(pdev);
> +
> +	iio_device_unregister(iio);
Having just the iio_device_unregister in a remove is a clear
indication that you could have gotten away with
devm_iio_device_register and dropped the remove entirely
as there would be nothing to do.
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id sd_adc_of_match[] = {
> +	{ .compatible = "sd-modulator" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, adc081c_of_match);
> +
> +static struct platform_driver simple_sd_adc = {
> +	.driver = {
> +		.name = "simple_sd_adc",
> +		.of_match_table = of_match_ptr(sd_adc_of_match),
> +	},
> +	.probe = simple_sd_probe,
> +	.remove = simple_sd_remove,
> +};
> +
> +module_platform_driver(simple_sd_adc);
> +
> +MODULE_DESCRIPTION("simple signma delta modulator");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
  2017-02-13 16:38   ` Arnaud Pouliquen
  (?)
@ 2017-02-19 14:46     ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:46 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 13/02/17 16:38, 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.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
I think this fits together rather nicely.  As before, various comments that
are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
relevant ones.

So as far as I'm concerned: Looking forward to the full version!
(as long as Mark and others are happy of course)

I definitely want to ultimately see buffered and dma support on the
ADC driver side of things as well but that can come later.

Jonathan
> ---
>  drivers/iio/adc/Kconfig            |  13 +
>  drivers/iio/adc/Makefile           |   1 +
>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>  5 files changed, 911 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 d4366ac..ab917b6 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -452,6 +452,19 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 dfsdm adc"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select REGMAP
> +	select REGMAP_MMIO
> +	select IIO_HW_CONSUMER
> +	help
> +	  Select this option to enable the  driver for STMicroelectronics
> +	  STM32 digital filter for sigma delta converter (ADC).
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,483 @@
> +/*
> + * This file is part of STM32 DFSDM ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.
> + * See the GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include <sound/stm32-adfsdm.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +enum stm32_dfsdm_mode {
> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
> +};
> +
> +struct stm32_dfsdm_adc {
> +	struct stm32_dfsdm *common;
> +
> +	unsigned int fl_id;
> +	unsigned int oversamp;
> +	unsigned int clk_freq;
> +
> +	enum stm32_dfsdm_mode mode;
> +	struct platform_device *audio_pdev;
> +
> +	void (*overrun_cb)(void *context);
> +	void *cb_context;
> +
> +	/* Hardware consumer structure for Front End iio */
IIO
> +	struct iio_hw_consumer *hwc;
> +};
> +
> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
> +
> +struct stm32_dfsdm_adc_devdata {
> +	enum stm32_dfsdm_mode mode;
> +	const struct iio_info *info;
> +};
> +
> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
> +				unsigned int oversamp)
> +{
> +	/*
> +	 * TODO
> +	 * This function tries to compute filter oversampling and integrator
> +	 * oversampling, base on oversampling ratio requested by user.
> +	 */
> +
> +	return 0;
> +};
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan, int *res)
> +{
> +	/* TODO: Perform conversion instead of sending fake value */
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
:( Definitely an RFC
> +
> +	*res = chan->channel + 0xFFFF00;
> +	return 0;
> +}
> +
> +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);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
> +		if (!ret)
> +			adc->oversamp = val;
If no reason to carry on,(i.e. nothing to unwind) return directly from within
the switch statement.
> +		break;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		if (adc->mode == DFSDM_AUDIO)
> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
> +		else
> +			ret = -EINVAL;
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +	}
> +
> +	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;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (adc->hwc) {
> +			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;
> +			}
Not an error if hwc not available?
> +		}
> +		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;
> +		}
> +
> +		if (adc->hwc)
> +			iio_hw_consumer_disable(adc->hwc);
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		*val = adc->oversamp;
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
Hohum. These two are the same, why two copies?  Mind you for the audio
you can't write or read anything so you could drop the callbacks.
> +
> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
> +	.mode = DFSDM_ADC,
> +	.info = &stm32_dfsdm_info_adc,
> +};
> +
> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
> +	.mode = DFSDM_AUDIO,
> +	.info = &stm32_dfsdm_info_audio,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	/* TODO */
> +	return IRQ_HANDLED;
> +}
> +
> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
> +				   unsigned int freq)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s:\n", __func__);
> +
> +	adc->clk_freq = freq;
> +};
> +
> +	/* Set expected audio sampling rate */
> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
> +				   struct stm32_dfsdm_hw_param *params)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
> +
> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
> +};
> +
> +	/* Called when ASoC starts an audio stream setup. */
> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +
> +	return 0;
> +};
> +
> +	/* Shuts down the audio stream. */
Odd indenting.
> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +};
> +
> +	/*
> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
> +	 * transfers.
physical - please run a spell checker over the comments.
> +	 */
> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +
> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
> +};
> +
> +/* Register callback to treat underrun and overrun issues */
> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
> +					 void (*overrun_cb)(void *context),
> +					 void *context)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +	adc->overrun_cb = overrun_cb;
> +	adc->cb_context = context;
> +};
> +
> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
> +	.set_sysclk = stm32_dfsdm_set_sysclk,
> +	.set_hwparam = stm32_dfsdm_set_hwparam,
> +	.audio_startup = stm32_dfsdm_audio_startup,
> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
> +	.get_dma_source = stm32_dfsdm_get_dma_source
> +};
Hmm. I'm wondering if it might make sense to farm the audio stuff off
to a separate file.  Potentially we'll have systems that are built with
no audio support at all, so would be odd to have this stuff still provided.

What do you think?  Also provides an obvious clean bit to be Mark's problem
(even if it's in the IIO directory ;)
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int chan_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channels", chan_idx,
> +					 &ch->channel);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channels' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-names", chan_idx,
> +					    &ch->datasheet_name);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channel-names' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = chan_idx;
> +	if (adc->mode == DFSDM_ADC) {
> +		/*
> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
> +		 */
> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
Very nice.  I was just thinking you should do this before I got here.
Channels with no properties but still with an existence from the point of
view of consumers using the buffered interface.  Potentially you 'could'
provide some of the info as read only but why bother...
> +	}
> +
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +
> +	return 0;
> +}
> +
> +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->common->num_chs) {
> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
> +		return num_ch < 0 ? num_ch : -EINVAL;
> +	}
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	if (adc->mode == DFSDM_ADC) {
> +		/*
> +		 * Bind to sd modulator iio device for ADC only.
> +		 * For Audio the PDM microphone will be handled by ASoC
> +		 */
> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
> +		if (IS_ERR(adc->hwc)) {
> +			dev_err(&indio_dev->dev, "no backend found\n");
Deferred probing a possibility? I'm not quite sure and you know what is going
on here better than me ;)
> +			return PTR_ERR(adc->hwc);
> +		}
> +	}
> +
> +	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:
> +	if (adc->hwc)
> +		iio_hw_consumer_free(adc->hwc);
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc",
> +	  .data = &stm32_dfsdm_devdata_adc},
> +	{ .compatible = "st,stm32-dfsdm-pdm",
> +	  .data = &stm32_dfsdm_devdata_audio},
> +	{}
> +};
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	const struct of_device_id *of_id;
> +	struct device_node *np = dev->of_node;
> +	const struct stm32_dfsdm_adc_devdata *devdata;
> +	struct iio_dev *iio;
> +	int ret, irq;
> +
> +	dev_dbg(dev, "%s:\n", __func__);
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: failed to allocate iio", __func__);
> +		return PTR_ERR(iio);
Returns NULL on failure rather than anything more useful.  I should look at
that one day, but the thought of a flag day to change all the drivers always
puts me off.  Mind you there aren't many ways it can fail and they all
correspond to -ENOMEM anyway so might never be worth the effort.

> +	}
> +
> +	adc = iio_priv(iio);
> +	if (IS_ERR(adc)) {
This one can't happen.  It's part of the same alloc as the struct iio_dev
above so it's both or neither.
> +		dev_err(dev, "%s: failed to allocate adc", __func__);
> +		return PTR_ERR(adc);
> +	}
> +	adc->common = dev_get_drvdata(dev->parent);
> +
> +	/* Populate data structure depending on compatibility */
> +	of_id = of_match_node(stm32_dfsdm_adc_match, np);
> +	if (!of_id->data) {
> +		dev_err(&pdev->dev, "Data associated to device is missing\n");
> +		return -EINVAL;
> +	}
> +
> +	devdata = (const struct stm32_dfsdm_adc_devdata *)of_id->data;
> +	adc->mode = devdata->mode;
> +
> +	iio->name = np->name;
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = devdata->info;
> +	iio->modes = INDIO_DIRECT_MODE;
> +
> +	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;
> +	}
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
I'd say 'for now' or something like that.  I initially thought you meant
they were handled later in this function.

For this first patch, just drop requesting them at all.
> +	 * In a second step IRQ domain should be used for filter 0 when feature
> +	 * like Watchdog, clock absence detection,... will be integrated.
> +	 */
> +	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 = stm32_dfsdm_adc_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = iio_device_register(iio);
> +	if (ret) {
> +		dev_err(dev, "failed to register iio device\n");
> +		return ret;
> +	}
> +
> +	if (adc->mode == DFSDM_AUDIO) {
> +		struct stm32_adfsdm_pdata dai_data = {
> +			.ops = &stm32_dfsdm_audio_ops,
> +			.adc = adc,
> +		};
> +
> +		adc->audio_pdev = platform_device_register_data(
> +						dev, STM32_ADFSDM_DRV_NAME,
> +						PLATFORM_DEVID_AUTO,
> +						&dai_data, sizeof(dai_data));
> +
> +		if (IS_ERR(adc->audio_pdev))
> +			return PTR_ERR(adc->audio_pdev);
> +	}
> +
> +	return 0;
> +}
> +
> +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);
As before, if nothing else here, you can just use a devm_iio_device_register
call and drop 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..195245d
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
> @@ -0,0 +1,273 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
> + *
> + * License terms: GPL V2.0.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "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
> +
> +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 -  stm32 dfsdm  private data
> + * @pdev:		platform device
> + * @stm32_dfsdm:	common data exported for all instances
> + * @regmap:		register map of the device;
> + * @clkout_div:		SPI clkout divider value.
> + * @n_active_ch:	atomic active channel counter.
> + */
> +struct dfsdm_priv {
> +	struct platform_device *pdev;
> +
> +	struct stm32_dfsdm dfsdm;
> +	struct regmap *regmap;
> +
> +	unsigned int clkout_div;
> +	atomic_t n_active_ch;
> +};
> +
> +/**
> + * 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);
> +	int ret;
> +	int div = priv->clkout_div;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		/* TODO: enable clocks */
> +
> +		/* Output the SPI CLKOUT (if clkout_div == 0 clok if OFF) */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(div));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Global enable of DFSDM interface */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(1));
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +}
> +
> +/**
> + * 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(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Stop SPI CLKOUT */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* TODO: disable clocks */
> +	}
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	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;
> +
> +	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);
> +
> +	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_kzalloc(&pdev->dev, sizeof(*dfsdm->fl_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->fl_list)
> +		return -ENOMEM;
> +
> +	dfsdm->num_fls = dev_data->num_filters;
> +	dfsdm->ch_list = devm_kzalloc(&pdev->dev, sizeof(*dfsdm->ch_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->ch_list)
> +		return -ENOMEM;
> +	dfsdm->num_chs = dev_data->num_channels;
> +		dev_err(&pdev->dev, "%s: dfsdm->num_ch: %d\n",
> +			__func__, dfsdm->num_chs);
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	priv->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
> +					    &stm32h7_dfsdm_regmap_cfg);
> +	if (IS_ERR(priv->regmap)) {
> +		ret = PTR_ERR(priv->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	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);
> +
> +	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..38ab15e
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm.h
> @@ -0,0 +1,141 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License terms: GPL V2.0.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + */
> +#ifndef MDF_STM32_DFSDM__H
> +#define MDF_STM32_DFSDM__H
> +
> +#include <linux/bitfield.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)
Prefix these to make them a little more specific.
STM32_DFSDM perhaps?
> +#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)
> +
> +/*
> + * 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)
> +
> +/**
> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
> + * TODO: complete structure.
> + * @id:		filetr ID,
> + */
> +struct stm32_dfsdm_filter {
> +	unsigned int id;
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
> + * TODO: complete structure.
> + * @id:		filetr ID,
filter?
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;
> +};
> +
> +/**
> + * 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 stm32_dfsdm_filter *fl_list;
> +	int num_fls;
> +	struct stm32_dfsdm_channel *ch_list;
> +	int num_chs;
> +};
> +
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
> +
> +#endif
> 

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

* Re: [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
@ 2017-02-19 14:46     ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:46 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 13/02/17 16:38, 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.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
I think this fits together rather nicely.  As before, various comments that
are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
relevant ones.

So as far as I'm concerned: Looking forward to the full version!
(as long as Mark and others are happy of course)

I definitely want to ultimately see buffered and dma support on the
ADC driver side of things as well but that can come later.

Jonathan
> ---
>  drivers/iio/adc/Kconfig            |  13 +
>  drivers/iio/adc/Makefile           |   1 +
>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>  5 files changed, 911 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 d4366ac..ab917b6 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -452,6 +452,19 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 dfsdm adc"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select REGMAP
> +	select REGMAP_MMIO
> +	select IIO_HW_CONSUMER
> +	help
> +	  Select this option to enable the  driver for STMicroelectronics
> +	  STM32 digital filter for sigma delta converter (ADC).
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,483 @@
> +/*
> + * This file is part of STM32 DFSDM ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.
> + * See the GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include <sound/stm32-adfsdm.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +enum stm32_dfsdm_mode {
> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
> +};
> +
> +struct stm32_dfsdm_adc {
> +	struct stm32_dfsdm *common;
> +
> +	unsigned int fl_id;
> +	unsigned int oversamp;
> +	unsigned int clk_freq;
> +
> +	enum stm32_dfsdm_mode mode;
> +	struct platform_device *audio_pdev;
> +
> +	void (*overrun_cb)(void *context);
> +	void *cb_context;
> +
> +	/* Hardware consumer structure for Front End iio */
IIO
> +	struct iio_hw_consumer *hwc;
> +};
> +
> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
> +
> +struct stm32_dfsdm_adc_devdata {
> +	enum stm32_dfsdm_mode mode;
> +	const struct iio_info *info;
> +};
> +
> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
> +				unsigned int oversamp)
> +{
> +	/*
> +	 * TODO
> +	 * This function tries to compute filter oversampling and integrator
> +	 * oversampling, base on oversampling ratio requested by user.
> +	 */
> +
> +	return 0;
> +};
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan, int *res)
> +{
> +	/* TODO: Perform conversion instead of sending fake value */
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
:( Definitely an RFC
> +
> +	*res = chan->channel + 0xFFFF00;
> +	return 0;
> +}
> +
> +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);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
> +		if (!ret)
> +			adc->oversamp = val;
If no reason to carry on,(i.e. nothing to unwind) return directly from within
the switch statement.
> +		break;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		if (adc->mode == DFSDM_AUDIO)
> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
> +		else
> +			ret = -EINVAL;
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +	}
> +
> +	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;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (adc->hwc) {
> +			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;
> +			}
Not an error if hwc not available?
> +		}
> +		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;
> +		}
> +
> +		if (adc->hwc)
> +			iio_hw_consumer_disable(adc->hwc);
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		*val = adc->oversamp;
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
Hohum. These two are the same, why two copies?  Mind you for the audio
you can't write or read anything so you could drop the callbacks.
> +
> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
> +	.mode = DFSDM_ADC,
> +	.info = &stm32_dfsdm_info_adc,
> +};
> +
> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
> +	.mode = DFSDM_AUDIO,
> +	.info = &stm32_dfsdm_info_audio,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	/* TODO */
> +	return IRQ_HANDLED;
> +}
> +
> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
> +				   unsigned int freq)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s:\n", __func__);
> +
> +	adc->clk_freq = freq;
> +};
> +
> +	/* Set expected audio sampling rate */
> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
> +				   struct stm32_dfsdm_hw_param *params)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
> +
> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
> +};
> +
> +	/* Called when ASoC starts an audio stream setup. */
> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +
> +	return 0;
> +};
> +
> +	/* Shuts down the audio stream. */
Odd indenting.
> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +};
> +
> +	/*
> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
> +	 * transfers.
physical - please run a spell checker over the comments.
> +	 */
> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +
> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
> +};
> +
> +/* Register callback to treat underrun and overrun issues */
> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
> +					 void (*overrun_cb)(void *context),
> +					 void *context)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +	adc->overrun_cb = overrun_cb;
> +	adc->cb_context = context;
> +};
> +
> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
> +	.set_sysclk = stm32_dfsdm_set_sysclk,
> +	.set_hwparam = stm32_dfsdm_set_hwparam,
> +	.audio_startup = stm32_dfsdm_audio_startup,
> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
> +	.get_dma_source = stm32_dfsdm_get_dma_source
> +};
Hmm. I'm wondering if it might make sense to farm the audio stuff off
to a separate file.  Potentially we'll have systems that are built with
no audio support at all, so would be odd to have this stuff still provided.

What do you think?  Also provides an obvious clean bit to be Mark's problem
(even if it's in the IIO directory ;)
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int chan_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channels", chan_idx,
> +					 &ch->channel);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channels' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-names", chan_idx,
> +					    &ch->datasheet_name);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channel-names' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = chan_idx;
> +	if (adc->mode == DFSDM_ADC) {
> +		/*
> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
> +		 */
> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
Very nice.  I was just thinking you should do this before I got here.
Channels with no properties but still with an existence from the point of
view of consumers using the buffered interface.  Potentially you 'could'
provide some of the info as read only but why bother...
> +	}
> +
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +
> +	return 0;
> +}
> +
> +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->common->num_chs) {
> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
> +		return num_ch < 0 ? num_ch : -EINVAL;
> +	}
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	if (adc->mode == DFSDM_ADC) {
> +		/*
> +		 * Bind to sd modulator iio device for ADC only.
> +		 * For Audio the PDM microphone will be handled by ASoC
> +		 */
> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
> +		if (IS_ERR(adc->hwc)) {
> +			dev_err(&indio_dev->dev, "no backend found\n");
Deferred probing a possibility? I'm not quite sure and you know what is going
on here better than me ;)
> +			return PTR_ERR(adc->hwc);
> +		}
> +	}
> +
> +	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:
> +	if (adc->hwc)
> +		iio_hw_consumer_free(adc->hwc);
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc",
> +	  .data = &stm32_dfsdm_devdata_adc},
> +	{ .compatible = "st,stm32-dfsdm-pdm",
> +	  .data = &stm32_dfsdm_devdata_audio},
> +	{}
> +};
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	const struct of_device_id *of_id;
> +	struct device_node *np = dev->of_node;
> +	const struct stm32_dfsdm_adc_devdata *devdata;
> +	struct iio_dev *iio;
> +	int ret, irq;
> +
> +	dev_dbg(dev, "%s:\n", __func__);
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: failed to allocate iio", __func__);
> +		return PTR_ERR(iio);
Returns NULL on failure rather than anything more useful.  I should look at
that one day, but the thought of a flag day to change all the drivers always
puts me off.  Mind you there aren't many ways it can fail and they all
correspond to -ENOMEM anyway so might never be worth the effort.

> +	}
> +
> +	adc = iio_priv(iio);
> +	if (IS_ERR(adc)) {
This one can't happen.  It's part of the same alloc as the struct iio_dev
above so it's both or neither.
> +		dev_err(dev, "%s: failed to allocate adc", __func__);
> +		return PTR_ERR(adc);
> +	}
> +	adc->common = dev_get_drvdata(dev->parent);
> +
> +	/* Populate data structure depending on compatibility */
> +	of_id = of_match_node(stm32_dfsdm_adc_match, np);
> +	if (!of_id->data) {
> +		dev_err(&pdev->dev, "Data associated to device is missing\n");
> +		return -EINVAL;
> +	}
> +
> +	devdata = (const struct stm32_dfsdm_adc_devdata *)of_id->data;
> +	adc->mode = devdata->mode;
> +
> +	iio->name = np->name;
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = devdata->info;
> +	iio->modes = INDIO_DIRECT_MODE;
> +
> +	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;
> +	}
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
I'd say 'for now' or something like that.  I initially thought you meant
they were handled later in this function.

For this first patch, just drop requesting them at all.
> +	 * In a second step IRQ domain should be used for filter 0 when feature
> +	 * like Watchdog, clock absence detection,... will be integrated.
> +	 */
> +	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 = stm32_dfsdm_adc_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = iio_device_register(iio);
> +	if (ret) {
> +		dev_err(dev, "failed to register iio device\n");
> +		return ret;
> +	}
> +
> +	if (adc->mode == DFSDM_AUDIO) {
> +		struct stm32_adfsdm_pdata dai_data = {
> +			.ops = &stm32_dfsdm_audio_ops,
> +			.adc = adc,
> +		};
> +
> +		adc->audio_pdev = platform_device_register_data(
> +						dev, STM32_ADFSDM_DRV_NAME,
> +						PLATFORM_DEVID_AUTO,
> +						&dai_data, sizeof(dai_data));
> +
> +		if (IS_ERR(adc->audio_pdev))
> +			return PTR_ERR(adc->audio_pdev);
> +	}
> +
> +	return 0;
> +}
> +
> +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);
As before, if nothing else here, you can just use a devm_iio_device_register
call and drop 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..195245d
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
> @@ -0,0 +1,273 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
> + *
> + * License terms: GPL V2.0.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "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
> +
> +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 -  stm32 dfsdm  private data
> + * @pdev:		platform device
> + * @stm32_dfsdm:	common data exported for all instances
> + * @regmap:		register map of the device;
> + * @clkout_div:		SPI clkout divider value.
> + * @n_active_ch:	atomic active channel counter.
> + */
> +struct dfsdm_priv {
> +	struct platform_device *pdev;
> +
> +	struct stm32_dfsdm dfsdm;
> +	struct regmap *regmap;
> +
> +	unsigned int clkout_div;
> +	atomic_t n_active_ch;
> +};
> +
> +/**
> + * 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);
> +	int ret;
> +	int div = priv->clkout_div;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		/* TODO: enable clocks */
> +
> +		/* Output the SPI CLKOUT (if clkout_div == 0 clok if OFF) */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(div));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Global enable of DFSDM interface */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(1));
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +}
> +
> +/**
> + * 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(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Stop SPI CLKOUT */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* TODO: disable clocks */
> +	}
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	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;
> +
> +	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);
> +
> +	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_kzalloc(&pdev->dev, sizeof(*dfsdm->fl_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->fl_list)
> +		return -ENOMEM;
> +
> +	dfsdm->num_fls = dev_data->num_filters;
> +	dfsdm->ch_list = devm_kzalloc(&pdev->dev, sizeof(*dfsdm->ch_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->ch_list)
> +		return -ENOMEM;
> +	dfsdm->num_chs = dev_data->num_channels;
> +		dev_err(&pdev->dev, "%s: dfsdm->num_ch: %d\n",
> +			__func__, dfsdm->num_chs);
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	priv->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
> +					    &stm32h7_dfsdm_regmap_cfg);
> +	if (IS_ERR(priv->regmap)) {
> +		ret = PTR_ERR(priv->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	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);
> +
> +	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..38ab15e
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm.h
> @@ -0,0 +1,141 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License terms: GPL V2.0.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + */
> +#ifndef MDF_STM32_DFSDM__H
> +#define MDF_STM32_DFSDM__H
> +
> +#include <linux/bitfield.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)
Prefix these to make them a little more specific.
STM32_DFSDM perhaps?
> +#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)
> +
> +/*
> + * 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)
> +
> +/**
> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
> + * TODO: complete structure.
> + * @id:		filetr ID,
> + */
> +struct stm32_dfsdm_filter {
> +	unsigned int id;
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
> + * TODO: complete structure.
> + * @id:		filetr ID,
filter?
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;
> +};
> +
> +/**
> + * 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 stm32_dfsdm_filter *fl_list;
> +	int num_fls;
> +	struct stm32_dfsdm_channel *ch_list;
> +	int num_chs;
> +};
> +
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
> +
> +#endif
> 


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

* [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
@ 2017-02-19 14:46     ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/02/17 16:38, 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.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
I think this fits together rather nicely.  As before, various comments that
are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
relevant ones.

So as far as I'm concerned: Looking forward to the full version!
(as long as Mark and others are happy of course)

I definitely want to ultimately see buffered and dma support on the
ADC driver side of things as well but that can come later.

Jonathan
> ---
>  drivers/iio/adc/Kconfig            |  13 +
>  drivers/iio/adc/Makefile           |   1 +
>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>  5 files changed, 911 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 d4366ac..ab917b6 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -452,6 +452,19 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 dfsdm adc"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select REGMAP
> +	select REGMAP_MMIO
> +	select IIO_HW_CONSUMER
> +	help
> +	  Select this option to enable the  driver for STMicroelectronics
> +	  STM32 digital filter for sigma delta converter (ADC).
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,483 @@
> +/*
> + * This file is part of STM32 DFSDM ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.
> + * See the GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/hw_consumer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#include <sound/stm32-adfsdm.h>
> +
> +#include "stm32-dfsdm.h"
> +
> +enum stm32_dfsdm_mode {
> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
> +};
> +
> +struct stm32_dfsdm_adc {
> +	struct stm32_dfsdm *common;
> +
> +	unsigned int fl_id;
> +	unsigned int oversamp;
> +	unsigned int clk_freq;
> +
> +	enum stm32_dfsdm_mode mode;
> +	struct platform_device *audio_pdev;
> +
> +	void (*overrun_cb)(void *context);
> +	void *cb_context;
> +
> +	/* Hardware consumer structure for Front End iio */
IIO
> +	struct iio_hw_consumer *hwc;
> +};
> +
> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
> +
> +struct stm32_dfsdm_adc_devdata {
> +	enum stm32_dfsdm_mode mode;
> +	const struct iio_info *info;
> +};
> +
> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
> +				unsigned int oversamp)
> +{
> +	/*
> +	 * TODO
> +	 * This function tries to compute filter oversampling and integrator
> +	 * oversampling, base on oversampling ratio requested by user.
> +	 */
> +
> +	return 0;
> +};
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan, int *res)
> +{
> +	/* TODO: Perform conversion instead of sending fake value */
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
:( Definitely an RFC
> +
> +	*res = chan->channel + 0xFFFF00;
> +	return 0;
> +}
> +
> +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);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
> +		if (!ret)
> +			adc->oversamp = val;
If no reason to carry on,(i.e. nothing to unwind) return directly from within
the switch statement.
> +		break;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		if (adc->mode == DFSDM_AUDIO)
> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
> +		else
> +			ret = -EINVAL;
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +	}
> +
> +	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;
> +
> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (adc->hwc) {
> +			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;
> +			}
Not an error if hwc not available?
> +		}
> +		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;
> +		}
> +
> +		if (adc->hwc)
> +			iio_hw_consumer_disable(adc->hwc);
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> +		*val = adc->oversamp;
> +
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
Hohum. These two are the same, why two copies?  Mind you for the audio
you can't write or read anything so you could drop the callbacks.
> +
> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
> +	.mode = DFSDM_ADC,
> +	.info = &stm32_dfsdm_info_adc,
> +};
> +
> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
> +	.mode = DFSDM_AUDIO,
> +	.info = &stm32_dfsdm_info_audio,
> +};
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	/* TODO */
> +	return IRQ_HANDLED;
> +}
> +
> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
> +				   unsigned int freq)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s:\n", __func__);
> +
> +	adc->clk_freq = freq;
> +};
> +
> +	/* Set expected audio sampling rate */
> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
> +				   struct stm32_dfsdm_hw_param *params)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
> +
> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
> +};
> +
> +	/* Called when ASoC starts an audio stream setup. */
> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +
> +	return 0;
> +};
> +
> +	/* Shuts down the audio stream. */
Odd indenting.
> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +};
> +
> +	/*
> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
> +	 * transfers.
physical - please run a spell checker over the comments.
> +	 */
> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +
> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
> +};
> +
> +/* Register callback to treat underrun and overrun issues */
> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
> +					 void (*overrun_cb)(void *context),
> +					 void *context)
> +{
> +	struct iio_dev *iio = iio_priv_to_dev(adc);
> +
> +	dev_dbg(&iio->dev, "%s\n", __func__);
> +	adc->overrun_cb = overrun_cb;
> +	adc->cb_context = context;
> +};
> +
> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
> +	.set_sysclk = stm32_dfsdm_set_sysclk,
> +	.set_hwparam = stm32_dfsdm_set_hwparam,
> +	.audio_startup = stm32_dfsdm_audio_startup,
> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
> +	.get_dma_source = stm32_dfsdm_get_dma_source
> +};
Hmm. I'm wondering if it might make sense to farm the audio stuff off
to a separate file.  Potentially we'll have systems that are built with
no audio support at all, so would be odd to have this stuff still provided.

What do you think?  Also provides an obvious clean bit to be Mark's problem
(even if it's in the IIO directory ;)
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int chan_idx)
> +{
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channels", chan_idx,
> +					 &ch->channel);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channels' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-names", chan_idx,
> +					    &ch->datasheet_name);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channel-names' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = chan_idx;
> +	if (adc->mode == DFSDM_ADC) {
> +		/*
> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
> +		 */
> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
Very nice.  I was just thinking you should do this before I got here.
Channels with no properties but still with an existence from the point of
view of consumers using the buffered interface.  Potentially you 'could'
provide some of the info as read only but why bother...
> +	}
> +
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = 24;
> +	ch->scan_type.storagebits = 32;
> +
> +	return 0;
> +}
> +
> +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->common->num_chs) {
> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
> +		return num_ch < 0 ? num_ch : -EINVAL;
> +	}
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	if (adc->mode == DFSDM_ADC) {
> +		/*
> +		 * Bind to sd modulator iio device for ADC only.
> +		 * For Audio the PDM microphone will be handled by ASoC
> +		 */
> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
> +		if (IS_ERR(adc->hwc)) {
> +			dev_err(&indio_dev->dev, "no backend found\n");
Deferred probing a possibility? I'm not quite sure and you know what is going
on here better than me ;)
> +			return PTR_ERR(adc->hwc);
> +		}
> +	}
> +
> +	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:
> +	if (adc->hwc)
> +		iio_hw_consumer_free(adc->hwc);
> +	return ret;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc",
> +	  .data = &stm32_dfsdm_devdata_adc},
> +	{ .compatible = "st,stm32-dfsdm-pdm",
> +	  .data = &stm32_dfsdm_devdata_audio},
> +	{}
> +};
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	const struct of_device_id *of_id;
> +	struct device_node *np = dev->of_node;
> +	const struct stm32_dfsdm_adc_devdata *devdata;
> +	struct iio_dev *iio;
> +	int ret, irq;
> +
> +	dev_dbg(dev, "%s:\n", __func__);
> +
> +	iio = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(iio)) {
> +		dev_err(dev, "%s: failed to allocate iio", __func__);
> +		return PTR_ERR(iio);
Returns NULL on failure rather than anything more useful.  I should look at
that one day, but the thought of a flag day to change all the drivers always
puts me off.  Mind you there aren't many ways it can fail and they all
correspond to -ENOMEM anyway so might never be worth the effort.

> +	}
> +
> +	adc = iio_priv(iio);
> +	if (IS_ERR(adc)) {
This one can't happen.  It's part of the same alloc as the struct iio_dev
above so it's both or neither.
> +		dev_err(dev, "%s: failed to allocate adc", __func__);
> +		return PTR_ERR(adc);
> +	}
> +	adc->common = dev_get_drvdata(dev->parent);
> +
> +	/* Populate data structure depending on compatibility */
> +	of_id = of_match_node(stm32_dfsdm_adc_match, np);
> +	if (!of_id->data) {
> +		dev_err(&pdev->dev, "Data associated to device is missing\n");
> +		return -EINVAL;
> +	}
> +
> +	devdata = (const struct stm32_dfsdm_adc_devdata *)of_id->data;
> +	adc->mode = devdata->mode;
> +
> +	iio->name = np->name;
> +	iio->dev.parent = dev;
> +	iio->dev.of_node = np;
> +	iio->info = devdata->info;
> +	iio->modes = INDIO_DIRECT_MODE;
> +
> +	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;
> +	}
> +
> +	/*
> +	 * In a first step IRQs generated for channels are not treated.
> +	 * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
I'd say 'for now' or something like that.  I initially thought you meant
they were handled later in this function.

For this first patch, just drop requesting them at all.
> +	 * In a second step IRQ domain should be used for filter 0 when feature
> +	 * like Watchdog, clock absence detection,... will be integrated.
> +	 */
> +	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 = stm32_dfsdm_adc_chan_init(iio);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = iio_device_register(iio);
> +	if (ret) {
> +		dev_err(dev, "failed to register iio device\n");
> +		return ret;
> +	}
> +
> +	if (adc->mode == DFSDM_AUDIO) {
> +		struct stm32_adfsdm_pdata dai_data = {
> +			.ops = &stm32_dfsdm_audio_ops,
> +			.adc = adc,
> +		};
> +
> +		adc->audio_pdev = platform_device_register_data(
> +						dev, STM32_ADFSDM_DRV_NAME,
> +						PLATFORM_DEVID_AUTO,
> +						&dai_data, sizeof(dai_data));
> +
> +		if (IS_ERR(adc->audio_pdev))
> +			return PTR_ERR(adc->audio_pdev);
> +	}
> +
> +	return 0;
> +}
> +
> +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);
As before, if nothing else here, you can just use a devm_iio_device_register
call and drop 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..195245d
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
> @@ -0,0 +1,273 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
> + *
> + * License terms: GPL V2.0.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "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
> +
> +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 -  stm32 dfsdm  private data
> + * @pdev:		platform device
> + * @stm32_dfsdm:	common data exported for all instances
> + * @regmap:		register map of the device;
> + * @clkout_div:		SPI clkout divider value.
> + * @n_active_ch:	atomic active channel counter.
> + */
> +struct dfsdm_priv {
> +	struct platform_device *pdev;
> +
> +	struct stm32_dfsdm dfsdm;
> +	struct regmap *regmap;
> +
> +	unsigned int clkout_div;
> +	atomic_t n_active_ch;
> +};
> +
> +/**
> + * 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);
> +	int ret;
> +	int div = priv->clkout_div;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		/* TODO: enable clocks */
> +
> +		/* Output the SPI CLKOUT (if clkout_div == 0 clok if OFF) */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(div));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Global enable of DFSDM interface */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(1));
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +}
> +
> +/**
> + * 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(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_DFSDMEN_MASK,
> +					 DFSDM_CHCFGR1_DFSDMEN(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* Stop SPI CLKOUT */
> +		ret = regmap_update_bits(priv->regmap, DFSDM_CHCFGR1(0),
> +					 DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +					 DFSDM_CHCFGR1_CKOUTDIV(0));
> +		if (ret < 0)
> +			return ret;
> +
> +		/* TODO: disable clocks */
> +	}
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	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;
> +
> +	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);
> +
> +	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_kzalloc(&pdev->dev, sizeof(*dfsdm->fl_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->fl_list)
> +		return -ENOMEM;
> +
> +	dfsdm->num_fls = dev_data->num_filters;
> +	dfsdm->ch_list = devm_kzalloc(&pdev->dev, sizeof(*dfsdm->ch_list),
> +				      GFP_KERNEL);
> +	if (!dfsdm->ch_list)
> +		return -ENOMEM;
> +	dfsdm->num_chs = dev_data->num_channels;
> +		dev_err(&pdev->dev, "%s: dfsdm->num_ch: %d\n",
> +			__func__, dfsdm->num_chs);
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	priv->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
> +					    &stm32h7_dfsdm_regmap_cfg);
> +	if (IS_ERR(priv->regmap)) {
> +		ret = PTR_ERR(priv->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	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);
> +
> +	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..38ab15e
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm.h
> @@ -0,0 +1,141 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License terms: GPL V2.0.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + */
> +#ifndef MDF_STM32_DFSDM__H
> +#define MDF_STM32_DFSDM__H
> +
> +#include <linux/bitfield.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)
Prefix these to make them a little more specific.
STM32_DFSDM perhaps?
> +#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)
> +
> +/*
> + * 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)
> +
> +/**
> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
> + * TODO: complete structure.
> + * @id:		filetr ID,
> + */
> +struct stm32_dfsdm_filter {
> +	unsigned int id;
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
> + * TODO: complete structure.
> + * @id:		filetr ID,
filter?
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;
> +};
> +
> +/**
> + * 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 stm32_dfsdm_filter *fl_list;
> +	int num_fls;
> +	struct stm32_dfsdm_channel *ch_list;
> +	int num_chs;
> +};
> +
> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
> +
> +#endif
> 

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-14 17:45       ` Mark Brown
  (?)
@ 2017-02-19 14:56           ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:56 UTC (permalink / raw)
  To: Mark Brown, Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre Torgue, olivier moysan

On 14/02/17 17:45, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
> 
> This looks basically fine as a system specific driver but as some of the
> comments in here say there's bits of it could perhaps be genericised but
> I'm not sure we need to do that right now.  I'm not sure the abstraction
> is exactly comfortable but having another bit of hardware doing a bridge
> to IIO might be the best way to figure out something better.
Agreed.  To an extent we are fishing around in the dark at the moment.
Lets wait until we have a few more cases of similar hardware before trying
too much generalization.  This is acting as a good exploration of what
is needed.

Ideally Lars might upstream some of the other bits he has in his tree
to do with DSP processing on ADC streams and that might provide us with
more clues on generality (at least at the lowest layers)
(Apparently he's busy - though he always makes that excuse :)
Joking aside, the exploration Analog and in particular Lars does around
pushing the limits of how things interact is always useful!)

> 
>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>> +	.period_bytes_max = 4 * PAGE_SIZE,
>> +	.buffer_bytes_max = 16 * PAGE_SIZE
> 
> What's the physical minimum period limit?  The comment makes this sound
> like it's just made up.
> 
>> +	unsigned int shift = 24 -priv->max_scaling;
>> +	
> 
> Missing space after -.
> 
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	return 0;
>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>> +					  SNDRV_PCM_HW_PARAM_RATE,
>> +					  &priv->rates_const);
> 
> Looks like debug changes got left in?
> 
>> +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);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +	if (dir == SND_SOC_CLOCK_IN) {
>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>> +		priv->dmic_clk = freq;
>> +	}
>> +
>> +	/* Determine supported rate which depends on SPI/manchester clock */
>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
> 
> Since the DAI is unidirectional it doesn't matter but obviously if it
> weren't then the fact that getting the supported rates involves setting
> the hwparams means this could become disruptive.  If we're going to
> genericise this to be a more general IIO/ASoC bridge that could matter.
> 
>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>> +{
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +
>> +	return 0;
>> +}
> 
> Remove empty functions, though in this case I think you want to add
> something to disconnect the XRUN callback just in order to be sure it
> can't be mistakenly called.
> 

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-19 14:56           ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:56 UTC (permalink / raw)
  To: Mark Brown, Arnaud Pouliquen
  Cc: Rob Herring, Mark Rutland, 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

On 14/02/17 17:45, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
> 
> This looks basically fine as a system specific driver but as some of the
> comments in here say there's bits of it could perhaps be genericised but
> I'm not sure we need to do that right now.  I'm not sure the abstraction
> is exactly comfortable but having another bit of hardware doing a bridge
> to IIO might be the best way to figure out something better.
Agreed.  To an extent we are fishing around in the dark at the moment.
Lets wait until we have a few more cases of similar hardware before trying
too much generalization.  This is acting as a good exploration of what
is needed.

Ideally Lars might upstream some of the other bits he has in his tree
to do with DSP processing on ADC streams and that might provide us with
more clues on generality (at least at the lowest layers)
(Apparently he's busy - though he always makes that excuse :)
Joking aside, the exploration Analog and in particular Lars does around
pushing the limits of how things interact is always useful!)

> 
>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>> +	.period_bytes_max = 4 * PAGE_SIZE,
>> +	.buffer_bytes_max = 16 * PAGE_SIZE
> 
> What's the physical minimum period limit?  The comment makes this sound
> like it's just made up.
> 
>> +	unsigned int shift = 24 -priv->max_scaling;
>> +	
> 
> Missing space after -.
> 
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	return 0;
>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>> +					  SNDRV_PCM_HW_PARAM_RATE,
>> +					  &priv->rates_const);
> 
> Looks like debug changes got left in?
> 
>> +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);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +	if (dir == SND_SOC_CLOCK_IN) {
>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>> +		priv->dmic_clk = freq;
>> +	}
>> +
>> +	/* Determine supported rate which depends on SPI/manchester clock */
>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
> 
> Since the DAI is unidirectional it doesn't matter but obviously if it
> weren't then the fact that getting the supported rates involves setting
> the hwparams means this could become disruptive.  If we're going to
> genericise this to be a more general IIO/ASoC bridge that could matter.
> 
>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>> +{
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +
>> +	return 0;
>> +}
> 
> Remove empty functions, though in this case I think you want to add
> something to disconnect the XRUN callback just in order to be sure it
> can't be mistakenly called.
> 


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

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-19 14:56           ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 14:56 UTC (permalink / raw)
  To: linux-arm-kernel

On 14/02/17 17:45, Mark Brown wrote:
> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
> 
> This looks basically fine as a system specific driver but as some of the
> comments in here say there's bits of it could perhaps be genericised but
> I'm not sure we need to do that right now.  I'm not sure the abstraction
> is exactly comfortable but having another bit of hardware doing a bridge
> to IIO might be the best way to figure out something better.
Agreed.  To an extent we are fishing around in the dark at the moment.
Lets wait until we have a few more cases of similar hardware before trying
too much generalization.  This is acting as a good exploration of what
is needed.

Ideally Lars might upstream some of the other bits he has in his tree
to do with DSP processing on ADC streams and that might provide us with
more clues on generality (at least at the lowest layers)
(Apparently he's busy - though he always makes that excuse :)
Joking aside, the exploration Analog and in particular Lars does around
pushing the limits of how things interact is always useful!)

> 
>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>> +	.period_bytes_max = 4 * PAGE_SIZE,
>> +	.buffer_bytes_max = 16 * PAGE_SIZE
> 
> What's the physical minimum period limit?  The comment makes this sound
> like it's just made up.
> 
>> +	unsigned int shift = 24 -priv->max_scaling;
>> +	
> 
> Missing space after -.
> 
>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>> +	return 0;
>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>> +					  SNDRV_PCM_HW_PARAM_RATE,
>> +					  &priv->rates_const);
> 
> Looks like debug changes got left in?
> 
>> +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);
>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>> +
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +	if (dir == SND_SOC_CLOCK_IN) {
>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>> +		priv->dmic_clk = freq;
>> +	}
>> +
>> +	/* Determine supported rate which depends on SPI/manchester clock */
>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
> 
> Since the DAI is unidirectional it doesn't matter but obviously if it
> weren't then the fact that getting the supported rates involves setting
> the hwparams means this could become disruptive.  If we're going to
> genericise this to be a more general IIO/ASoC bridge that could matter.
> 
>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>> +{
>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>> +
>> +	return 0;
>> +}
> 
> Remove empty functions, though in this case I think you want to add
> something to disconnect the XRUN callback just in order to be sure it
> can't be mistakenly called.
> 

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
  2017-02-13 16:38   ` Arnaud Pouliquen
  (?)
@ 2017-02-19 15:00     ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 15:00 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 13/02/17 16:38, Arnaud Pouliquen wrote:
> Add bindings that describe Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators and/or PDM microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>  1 file changed, 125 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..83937cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,125 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
> +interface external sigma delta modulators to stm32 micro controlers.
> +it is mainly targeted for:
> +- Sigma delta modulators ( motor control, metering...)
> +- PDM microphones ( audio digital microphone)
> +
> +It featured with up to 8 serial digital interface (SPI or Manchester) and
> +up to 4 filters.
> +
> +Each instance of the sub-drivers uses one filter instance.
> +
> +Contents of a stm32 dfsdmc root node:
> +-------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32-dfsdm-adc".
> +- 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.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +
> +Optional properties:
> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance.
> +- 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 channels Name.
> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- io-channels: from common iio binding. use to pipe external sigma delta
> +		modulator or internal ADC output to dfsdm channel.
> +
> +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. Default value is 0.
> +			- "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. default value is 1.
> +			  - "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.
> +		   to used for multi microphone synchronization.
> +
> +Example of a sigma delta adc purpose:
> +	ads1202: simple_sd_adc@0 {
> +		compatible = "sd-modulator";
This suggests to me that we ought to have a primary compatible of the actual part
number.  Down the line I suspect we will want to distinguish between the different
ADCs and it's kind of easier if we do it from the start.  May not be obvious
later which the 'default sd-modulator' is.
> +		#io-channel-cells = <1>;
> +		status = "okay";
> +	};
> +	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 = "in0";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +			io-channels = <&ads1202 0>;
> +	};
> +
> +Example of a pdm microphone purpose:
Is there nothing about the particular pdm microphone that is
worth giving it an explicit representation in the device
tree?  I've no idea having never used one!
> +	dfsdm: dfsdm@40017000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm";
> +
> +		dfsdm_pdm0: dfsdm-pdm@0 {
> +			compatible = "st,stm32-dfsdm-pdm";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			interrupts = <110>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,adc-channels = <0>;
> +			st,adc-channel-names = "pdm1";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +		};
> +	}
> 

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-19 15:00     ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 15:00 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 13/02/17 16:38, Arnaud Pouliquen wrote:
> Add bindings that describe Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators and/or PDM microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>  1 file changed, 125 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..83937cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,125 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
> +interface external sigma delta modulators to stm32 micro controlers.
> +it is mainly targeted for:
> +- Sigma delta modulators ( motor control, metering...)
> +- PDM microphones ( audio digital microphone)
> +
> +It featured with up to 8 serial digital interface (SPI or Manchester) and
> +up to 4 filters.
> +
> +Each instance of the sub-drivers uses one filter instance.
> +
> +Contents of a stm32 dfsdmc root node:
> +-------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32-dfsdm-adc".
> +- 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.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +
> +Optional properties:
> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance.
> +- 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 channels Name.
> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- io-channels: from common iio binding. use to pipe external sigma delta
> +		modulator or internal ADC output to dfsdm channel.
> +
> +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. Default value is 0.
> +			- "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. default value is 1.
> +			  - "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.
> +		   to used for multi microphone synchronization.
> +
> +Example of a sigma delta adc purpose:
> +	ads1202: simple_sd_adc@0 {
> +		compatible = "sd-modulator";
This suggests to me that we ought to have a primary compatible of the actual part
number.  Down the line I suspect we will want to distinguish between the different
ADCs and it's kind of easier if we do it from the start.  May not be obvious
later which the 'default sd-modulator' is.
> +		#io-channel-cells = <1>;
> +		status = "okay";
> +	};
> +	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 = "in0";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +			io-channels = <&ads1202 0>;
> +	};
> +
> +Example of a pdm microphone purpose:
Is there nothing about the particular pdm microphone that is
worth giving it an explicit representation in the device
tree?  I've no idea having never used one!
> +	dfsdm: dfsdm@40017000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm";
> +
> +		dfsdm_pdm0: dfsdm-pdm@0 {
> +			compatible = "st,stm32-dfsdm-pdm";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			interrupts = <110>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,adc-channels = <0>;
> +			st,adc-channel-names = "pdm1";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +		};
> +	}
> 


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

* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-19 15:00     ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-02-19 15:00 UTC (permalink / raw)
  To: linux-arm-kernel

On 13/02/17 16:38, Arnaud Pouliquen wrote:
> Add bindings that describe Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators and/or PDM microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>  1 file changed, 125 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..83937cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,125 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
> +interface external sigma delta modulators to stm32 micro controlers.
> +it is mainly targeted for:
> +- Sigma delta modulators ( motor control, metering...)
> +- PDM microphones ( audio digital microphone)
> +
> +It featured with up to 8 serial digital interface (SPI or Manchester) and
> +up to 4 filters.
> +
> +Each instance of the sub-drivers uses one filter instance.
> +
> +Contents of a stm32 dfsdmc root node:
> +-------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32-dfsdm-adc".
> +- 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.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +
> +Optional properties:
> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance.
> +- 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 channels Name.
> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- io-channels: from common iio binding. use to pipe external sigma delta
> +		modulator or internal ADC output to dfsdm channel.
> +
> +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. Default value is 0.
> +			- "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. default value is 1.
> +			  - "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.
> +		   to used for multi microphone synchronization.
> +
> +Example of a sigma delta adc purpose:
> +	ads1202: simple_sd_adc at 0 {
> +		compatible = "sd-modulator";
This suggests to me that we ought to have a primary compatible of the actual part
number.  Down the line I suspect we will want to distinguish between the different
ADCs and it's kind of easier if we do it from the start.  May not be obvious
later which the 'default sd-modulator' is.
> +		#io-channel-cells = <1>;
> +		status = "okay";
> +	};
> +	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 = "in0";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +			io-channels = <&ads1202 0>;
> +	};
> +
> +Example of a pdm microphone purpose:
Is there nothing about the particular pdm microphone that is
worth giving it an explicit representation in the device
tree?  I've no idea having never used one!
> +	dfsdm: dfsdm at 40017000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm";
> +
> +		dfsdm_pdm0: dfsdm-pdm at 0 {
> +			compatible = "st,stm32-dfsdm-pdm";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			interrupts = <110>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,adc-channels = <0>;
> +			st,adc-channel-names = "pdm1";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +		};
> +	}
> 

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

* Re: [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
  2017-02-13 16:38   ` Arnaud Pouliquen
  (?)
@ 2017-02-22 15:17     ` Rob Herring
  -1 siblings, 0 replies; 107+ messages in thread
From: Rob Herring @ 2017-02-22 15:17 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 Mon, Feb 13, 2017 at 05:38:24PM +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>
> ---
>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> new file mode 100644
> index 0000000..2b3968a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> @@ -0,0 +1,13 @@
> +Device-Tree bindings for simple sigma delta adc

What makes it "simple"?

> +
> +Required properties:
> +- compatible: should be "sd-modulator".
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +
> +Example node:
> +
> +	ads1202: simple_sd_adc@0 {

Is ads1202 the actual chip? Then it should be in the compatible list.

unit address without a reg prop is an error. The node name should be 
"adc".

> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +		status = "okay";

Drop status from examples.

> +	};
> -- 
> 1.9.1
> 

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

* Re: [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
@ 2017-02-22 15:17     ` Rob Herring
  0 siblings, 0 replies; 107+ messages in thread
From: Rob Herring @ 2017-02-22 15:17 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 Mon, Feb 13, 2017 at 05:38:24PM +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>
> ---
>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> new file mode 100644
> index 0000000..2b3968a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> @@ -0,0 +1,13 @@
> +Device-Tree bindings for simple sigma delta adc

What makes it "simple"?

> +
> +Required properties:
> +- compatible: should be "sd-modulator".
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +
> +Example node:
> +
> +	ads1202: simple_sd_adc@0 {

Is ads1202 the actual chip? Then it should be in the compatible list.

unit address without a reg prop is an error. The node name should be 
"adc".

> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +		status = "okay";

Drop status from examples.

> +	};
> -- 
> 1.9.1
> 

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

* [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
@ 2017-02-22 15:17     ` Rob Herring
  0 siblings, 0 replies; 107+ messages in thread
From: Rob Herring @ 2017-02-22 15:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 13, 2017 at 05:38:24PM +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>
> ---
>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> new file mode 100644
> index 0000000..2b3968a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
> @@ -0,0 +1,13 @@
> +Device-Tree bindings for simple sigma delta adc

What makes it "simple"?

> +
> +Required properties:
> +- compatible: should be "sd-modulator".
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +
> +Example node:
> +
> +	ads1202: simple_sd_adc at 0 {

Is ads1202 the actual chip? Then it should be in the compatible list.

unit address without a reg prop is an error. The node name should be 
"adc".

> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +		status = "okay";

Drop status from examples.

> +	};
> -- 
> 1.9.1
> 

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
  2017-02-13 16:38   ` Arnaud Pouliquen
  (?)
@ 2017-02-22 16:42       ` Rob Herring
  -1 siblings, 0 replies; 107+ messages in thread
From: Rob Herring @ 2017-02-22 16:42 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 Mon, Feb 13, 2017 at 05:38:28PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describe Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators and/or PDM microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>  1 file changed, 125 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..83937cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,125 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
> +interface external sigma delta modulators to stm32 micro controlers.
> +it is mainly targeted for:
> +- Sigma delta modulators ( motor control, metering...)
> +- PDM microphones ( audio digital microphone)
> +
> +It featured with up to 8 serial digital interface (SPI or Manchester) and
> +up to 4 filters.
> +
> +Each instance of the sub-drivers uses one filter instance.
> +
> +Contents of a stm32 dfsdmc root node:
> +-------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32-dfsdm-adc".
> +- 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.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +
> +Optional properties:
> +- st,clkout-freq: needed 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, clkout frequency will not be
> +		  accurate.

We already have a standard property to specify the SPI freq.

> +
> +Contents of a stm32 DFSDM child nodes:
> +----------------------------------------
> +
> +Required properties:
> +- compatible: Must be:
> +	"st,stm32-dfsdm-adc" for sigma delta ADCs
> +	"st,stm32-dfsdm-pdm" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance.
> +- 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 channels Name.

Why do you need names?

> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- io-channels: from common iio binding. use to pipe external sigma delta
> +		modulator or internal ADC output to dfsdm channel.
> +
> +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. Default value is 0.

How is the default 0 when the values are strings?

> +			- "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. default value is 1.

Ditto.

Perhaps you should be using the clock binding and assigned-clocks 
property.

> +			  - "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.
> +		   to used for multi microphone synchronization.

Is there an instance 1?  If not, drop the 0. If you need to expand to 
multiple instances, you can have multiple values.

> +
> +Example of a sigma delta adc purpose:
> +	ads1202: simple_sd_adc@0 {
> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +		status = "okay";
> +	};
> +	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>;

Does reg provide the same thing?

> +			st,adc-channel-names = "in0";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +			io-channels = <&ads1202 0>;
> +	};
> +
> +Example of a pdm microphone purpose:
> +	dfsdm: dfsdm@40017000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm";
> +
> +		dfsdm_pdm0: dfsdm-pdm@0 {
> +			compatible = "st,stm32-dfsdm-pdm";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			interrupts = <110>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,adc-channels = <0>;
> +			st,adc-channel-names = "pdm1";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +		};
> +	}
> -- 
> 1.9.1
> 

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-22 16:42       ` Rob Herring
  0 siblings, 0 replies; 107+ messages in thread
From: Rob Herring @ 2017-02-22 16:42 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 Mon, Feb 13, 2017 at 05:38:28PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describe Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators and/or PDM microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>  1 file changed, 125 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..83937cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,125 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
> +interface external sigma delta modulators to stm32 micro controlers.
> +it is mainly targeted for:
> +- Sigma delta modulators ( motor control, metering...)
> +- PDM microphones ( audio digital microphone)
> +
> +It featured with up to 8 serial digital interface (SPI or Manchester) and
> +up to 4 filters.
> +
> +Each instance of the sub-drivers uses one filter instance.
> +
> +Contents of a stm32 dfsdmc root node:
> +-------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32-dfsdm-adc".
> +- 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.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +
> +Optional properties:
> +- st,clkout-freq: needed 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, clkout frequency will not be
> +		  accurate.

We already have a standard property to specify the SPI freq.

> +
> +Contents of a stm32 DFSDM child nodes:
> +----------------------------------------
> +
> +Required properties:
> +- compatible: Must be:
> +	"st,stm32-dfsdm-adc" for sigma delta ADCs
> +	"st,stm32-dfsdm-pdm" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance.
> +- 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 channels Name.

Why do you need names?

> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- io-channels: from common iio binding. use to pipe external sigma delta
> +		modulator or internal ADC output to dfsdm channel.
> +
> +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. Default value is 0.

How is the default 0 when the values are strings?

> +			- "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. default value is 1.

Ditto.

Perhaps you should be using the clock binding and assigned-clocks 
property.

> +			  - "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.
> +		   to used for multi microphone synchronization.

Is there an instance 1?  If not, drop the 0. If you need to expand to 
multiple instances, you can have multiple values.

> +
> +Example of a sigma delta adc purpose:
> +	ads1202: simple_sd_adc@0 {
> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +		status = "okay";
> +	};
> +	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>;

Does reg provide the same thing?

> +			st,adc-channel-names = "in0";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +			io-channels = <&ads1202 0>;
> +	};
> +
> +Example of a pdm microphone purpose:
> +	dfsdm: dfsdm@40017000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm";
> +
> +		dfsdm_pdm0: dfsdm-pdm@0 {
> +			compatible = "st,stm32-dfsdm-pdm";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			interrupts = <110>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,adc-channels = <0>;
> +			st,adc-channel-names = "pdm1";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +		};
> +	}
> -- 
> 1.9.1
> 

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

* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-22 16:42       ` Rob Herring
  0 siblings, 0 replies; 107+ messages in thread
From: Rob Herring @ 2017-02-22 16:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Feb 13, 2017 at 05:38:28PM +0100, Arnaud Pouliquen wrote:
> Add bindings that describe Digital Filter for Sigma Delta
> Modulators. DFSDM allows to connect sigma delta
> modulators and/or PDM microphones.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>  1 file changed, 125 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..83937cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
> @@ -0,0 +1,125 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
> +interface external sigma delta modulators to stm32 micro controlers.
> +it is mainly targeted for:
> +- Sigma delta modulators ( motor control, metering...)
> +- PDM microphones ( audio digital microphone)
> +
> +It featured with up to 8 serial digital interface (SPI or Manchester) and
> +up to 4 filters.
> +
> +Each instance of the sub-drivers uses one filter instance.
> +
> +Contents of a stm32 dfsdmc root node:
> +-------------------------------------
> +Required properties:
> +- compatible: Should be "st,stm32-dfsdm-adc".
> +- 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.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +
> +Optional properties:
> +- st,clkout-freq: needed 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, clkout frequency will not be
> +		  accurate.

We already have a standard property to specify the SPI freq.

> +
> +Contents of a stm32 DFSDM child nodes:
> +----------------------------------------
> +
> +Required properties:
> +- compatible: Must be:
> +	"st,stm32-dfsdm-adc" for sigma delta ADCs
> +	"st,stm32-dfsdm-pdm" for audio digital microphone.
> +- reg: Specifies the DFSDM filter instance.
> +- 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 channels Name.

Why do you need names?

> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
> +
> +Required properties for "st,stm32-dfsdm-adc" compatibility:
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- io-channels: from common iio binding. use to pipe external sigma delta
> +		modulator or internal ADC output to dfsdm channel.
> +
> +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. Default value is 0.

How is the default 0 when the values are strings?

> +			- "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. default value is 1.

Ditto.

Perhaps you should be using the clock binding and assigned-clocks 
property.

> +			  - "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.
> +		   to used for multi microphone synchronization.

Is there an instance 1?  If not, drop the 0. If you need to expand to 
multiple instances, you can have multiple values.

> +
> +Example of a sigma delta adc purpose:
> +	ads1202: simple_sd_adc at 0 {
> +		compatible = "sd-modulator";
> +		#io-channel-cells = <1>;
> +		status = "okay";
> +	};
> +	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>;

Does reg provide the same thing?

> +			st,adc-channel-names = "in0";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +			io-channels = <&ads1202 0>;
> +	};
> +
> +Example of a pdm microphone purpose:
> +	dfsdm: dfsdm at 40017000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm";
> +
> +		dfsdm_pdm0: dfsdm-pdm at 0 {
> +			compatible = "st,stm32-dfsdm-pdm";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			interrupts = <110>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,adc-channels = <0>;
> +			st,adc-channel-names = "pdm1";
> +			st,adc-channel-types = "SPI_R";
> +			st,adc-channel-clk-src = "CLKOUT";
> +		};
> +	}
> -- 
> 1.9.1
> 

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
  2017-02-16 20:14                 ` Mark Brown
  (?)
@ 2017-02-27  9:05                   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27  9:05 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Olivier MOYSAN, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Rob Herring, linux-arm-kernel,
	Peter Meerwald-Stadler, Hartmut Knaack, Jonathan Cameron,
	Alexandre TORGUE

Hello Mark,

Sorry for the late answer, i was OoO...

On 02/16/2017 09:14 PM, Mark Brown wrote:
> On Wed, Feb 15, 2017 at 04:46:44PM +0100, Arnaud Pouliquen wrote:
>> On 02/15/2017 03:53 PM, Mark Brown wrote:
>>> On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen
>>> wrote:
> 
>>> No, copy() is an alternative to doing DMA rather than
>>> something that works with DMA.  If you want to have some sort
>>> of post processing operation it shoudn't be called copy().
>>> It's probably worth considering just making a new format and
>>> letting userspace convert things here.
> 
>> You mean using a plug-in alsa to do it? this seems not possible
>> with tiny-alsa... Without a plug-in, driver will not be
>> compatible with standard audio application.
> 
> Right.  But if you're using tinyalsa you've got some open coded
> system specific stuff anyway!
> 
>> Do you think this would be reasonable if i implement something
>> similar in my driver, without using the soc generic DMA engine?
> 
> Probably.  It might even be possible to fit it elegently into the 
> generic dmaengine code, just don't say it's a copy() operation
> since that's a specific thing in ALSA.
> 
Ok I will have a look in generic dmaengine and try to propose
something in this way in the V2.

Regards,
Arnaud

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

* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-27  9:05                   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27  9:05 UTC (permalink / raw)
  To: Mark Brown
  Cc: Rob Herring, 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

Hello Mark,

Sorry for the late answer, i was OoO...

On 02/16/2017 09:14 PM, Mark Brown wrote:
> On Wed, Feb 15, 2017 at 04:46:44PM +0100, Arnaud Pouliquen wrote:
>> On 02/15/2017 03:53 PM, Mark Brown wrote:
>>> On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen
>>> wrote:
> 
>>> No, copy() is an alternative to doing DMA rather than
>>> something that works with DMA.  If you want to have some sort
>>> of post processing operation it shoudn't be called copy().
>>> It's probably worth considering just making a new format and
>>> letting userspace convert things here.
> 
>> You mean using a plug-in alsa to do it? this seems not possible
>> with tiny-alsa... Without a plug-in, driver will not be
>> compatible with standard audio application.
> 
> Right.  But if you're using tinyalsa you've got some open coded
> system specific stuff anyway!
> 
>> Do you think this would be reasonable if i implement something
>> similar in my driver, without using the soc generic DMA engine?
> 
> Probably.  It might even be possible to fit it elegently into the 
> generic dmaengine code, just don't say it's a copy() operation
> since that's a specific thing in ALSA.
> 
Ok I will have a look in generic dmaengine and try to propose
something in this way in the V2.

Regards,
Arnaud

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

* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
@ 2017-02-27  9:05                   ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27  9:05 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

Sorry for the late answer, i was OoO...

On 02/16/2017 09:14 PM, Mark Brown wrote:
> On Wed, Feb 15, 2017 at 04:46:44PM +0100, Arnaud Pouliquen wrote:
>> On 02/15/2017 03:53 PM, Mark Brown wrote:
>>> On Wed, Feb 15, 2017 at 02:59:18PM +0100, Arnaud Pouliquen
>>> wrote:
> 
>>> No, copy() is an alternative to doing DMA rather than
>>> something that works with DMA.  If you want to have some sort
>>> of post processing operation it shoudn't be called copy().
>>> It's probably worth considering just making a new format and
>>> letting userspace convert things here.
> 
>> You mean using a plug-in alsa to do it? this seems not possible
>> with tiny-alsa... Without a plug-in, driver will not be
>> compatible with standard audio application.
> 
> Right.  But if you're using tinyalsa you've got some open coded
> system specific stuff anyway!
> 
>> Do you think this would be reasonable if i implement something
>> similar in my driver, without using the soc generic DMA engine?
> 
> Probably.  It might even be possible to fit it elegently into the 
> generic dmaengine code, just don't say it's a copy() operation
> since that's a specific thing in ALSA.
> 
Ok I will have a look in generic dmaengine and try to propose
something in this way in the V2.

Regards,
Arnaud

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

* Re: [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
  2017-02-19 14:46     ` Jonathan Cameron
  (?)
@ 2017-02-27 10:09       ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:09 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,

Late answer... sorry for this.

I will integrate your remark in V2.
Please find my answers in-line

Regards
Arnaud

On 02/19/2017 03:46 PM, Jonathan Cameron wrote:
> On 13/02/17 16:38, 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.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> I think this fits together rather nicely.  As before, various comments that
> are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
> relevant ones.
> 
> So as far as I'm concerned: Looking forward to the full version!
> (as long as Mark and others are happy of course)
> 
> I definitely want to ultimately see buffered and dma support on the
> ADC driver side of things as well but that can come later.
Ok, I suppose that DMA in cyclic mode should also be required for some
other IIO driver...
> 
> Jonathan
>> ---
>>  drivers/iio/adc/Kconfig            |  13 +
>>  drivers/iio/adc/Makefile           |   1 +
>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>>  5 files changed, 911 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 d4366ac..ab917b6 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -452,6 +452,19 @@ config STM32_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-adc.
>>  
>> +config STM32_DFSDM_ADC
>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	select IIO_HW_CONSUMER
>> +	help
>> +	  Select this option to enable the  driver for STMicroelectronics
>> +	  STM32 digital filter for sigma delta converter (ADC).
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>>  obj-$(CONFIG_STX104) += stx104.o
>>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>> @@ -0,0 +1,483 @@
>> +/*
>> + * This file is part of STM32 DFSDM ADC driver
>> + *
>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include <sound/stm32-adfsdm.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +enum stm32_dfsdm_mode {
>> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
>> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
>> +};
>> +
>> +struct stm32_dfsdm_adc {
>> +	struct stm32_dfsdm *common;
>> +
>> +	unsigned int fl_id;
>> +	unsigned int oversamp;
>> +	unsigned int clk_freq;
>> +
>> +	enum stm32_dfsdm_mode mode;
>> +	struct platform_device *audio_pdev;
>> +
>> +	void (*overrun_cb)(void *context);
>> +	void *cb_context;
>> +
>> +	/* Hardware consumer structure for Front End iio */
> IIO
>> +	struct iio_hw_consumer *hwc;
>> +};
>> +
>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
>> +
>> +struct stm32_dfsdm_adc_devdata {
>> +	enum stm32_dfsdm_mode mode;
>> +	const struct iio_info *info;
>> +};
>> +
>> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
>> +				unsigned int oversamp)
>> +{
>> +	/*
>> +	 * TODO
>> +	 * This function tries to compute filter oversampling and integrator
>> +	 * oversampling, base on oversampling ratio requested by user.
>> +	 */
>> +
>> +	return 0;
>> +};
>> +
>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>> +				   const struct iio_chan_spec *chan, int *res)
>> +{
>> +	/* TODO: Perform conversion instead of sending fake value */
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> :( Definitely an RFC
>> +
>> +	*res = chan->channel + 0xFFFF00;
>> +	return 0;
>> +}
>> +
>> +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);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
>> +		if (!ret)
>> +			adc->oversamp = val;
> If no reason to carry on,(i.e. nothing to unwind) return directly from within
> the switch statement.
>> +		break;
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		if (adc->mode == DFSDM_AUDIO)
>> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
>> +		else
>> +			ret = -EINVAL;
>> +		break;
>> +
>> +	default:
>> +		ret = -EINVAL;
>> +	}
>> +
>> +	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;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		if (adc->hwc) {
>> +			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;
>> +			}
> Not an error if hwc not available?
ASoC framework requests to handle a codec driver. So in case of PDM
usecase, no IIO SD modulator device is used, instead an ASoC generic
dmic-codec device is probed. In other words the link between the DFSDM
and the external SD-modulator has to be done in ASOC for PDMs and in
IIO for ADCs.

>> +		}
>> +		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;
>> +		}
>> +
>> +		if (adc->hwc)
>> +			iio_hw_consumer_disable(adc->hwc);
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		*val = adc->oversamp;
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
>> +	.read_raw = stm32_dfsdm_read_raw,
>> +	.write_raw = stm32_dfsdm_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
> Hohum. These two are the same, why two copies?  Mind you for the audio
> you can't write or read anything so you could drop the callbacks.
yes to rework.
>> +
>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
>> +	.mode = DFSDM_ADC,
>> +	.info = &stm32_dfsdm_info_adc,
>> +};
>> +
>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
>> +	.mode = DFSDM_AUDIO,
>> +	.info = &stm32_dfsdm_info_audio,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	/* TODO */
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
>> +				   unsigned int freq)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s:\n", __func__);
>> +
>> +	adc->clk_freq = freq;
>> +};
>> +
>> +	/* Set expected audio sampling rate */
>> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
>> +				   struct stm32_dfsdm_hw_param *params)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
>> +
>> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
>> +};
>> +
>> +	/* Called when ASoC starts an audio stream setup. */
>> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +
>> +	return 0;
>> +};
>> +
>> +	/* Shuts down the audio stream. */
> Odd indenting.
>> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +};
>> +
>> +	/*
>> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
>> +	 * transfers.
> physical - please run a spell checker over the comments.
>> +	 */
>> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +
>> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
>> +};
>> +
>> +/* Register callback to treat underrun and overrun issues */
>> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
>> +					 void (*overrun_cb)(void *context),
>> +					 void *context)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +	adc->overrun_cb = overrun_cb;
>> +	adc->cb_context = context;
>> +};
>> +
>> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
>> +	.set_sysclk = stm32_dfsdm_set_sysclk,
>> +	.set_hwparam = stm32_dfsdm_set_hwparam,
>> +	.audio_startup = stm32_dfsdm_audio_startup,
>> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
>> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
>> +	.get_dma_source = stm32_dfsdm_get_dma_source
>> +};
> Hmm. I'm wondering if it might make sense to farm the audio stuff off
> to a separate file.  Potentially we'll have systems that are built with
> no audio support at all, so would be odd to have this stuff still provided.
Ok, this make sense if the API become generic.
> 
> What do you think?  Also provides an obvious clean bit to be Mark's problem
> (even if it's in the IIO directory ;)
I can't answer instead of Mark,( and perhaps i misunderstood...) but
regarding HDMI story (drivers shared between ASoC and DRM), not sure
that Mark accepts to have an ASoC drivers in IIO directory. So need a
part in ASoC that is customer of IIO driver...

>> +
>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>> +					 struct iio_chan_spec *chan,
>> +					 int chan_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>> +					 "st,adc-channels", chan_idx,
>> +					 &ch->channel);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			" error parsing 'st,adc-channels' for idx %d\n",
>> +			chan_idx);
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-names", chan_idx,
>> +					    &ch->datasheet_name);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			" error parsing 'st,adc-channel-names' for idx %d\n",
>> +			chan_idx);
>> +		return ret;
>> +	}
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = chan_idx;
>> +	if (adc->mode == DFSDM_ADC) {
>> +		/*
>> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
>> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
>> +		 */
>> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
>> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
> Very nice.  I was just thinking you should do this before I got here.
> Channels with no properties but still with an existence from the point of
> view of consumers using the buffered interface.  Potentially you 'could'
> provide some of the info as read only but why bother...
>> +	}
>> +
>> +	ch->scan_type.sign = 'u';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +
>> +	return 0;
>> +}
>> +
>> +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->common->num_chs) {
>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>> +		return num_ch < 0 ? num_ch : -EINVAL;
>> +	}
>> +
>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>> +				GFP_KERNEL);
>> +	if (!channels)
>> +		return -ENOMEM;
>> +
>> +	if (adc->mode == DFSDM_ADC) {
>> +		/*
>> +		 * Bind to sd modulator iio device for ADC only.
>> +		 * For Audio the PDM microphone will be handled by ASoC
>> +		 */
>> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>> +		if (IS_ERR(adc->hwc)) {
>> +			dev_err(&indio_dev->dev, "no backend found\n");
> Deferred probing a possibility? I'm not quite sure and you know what is going
> on here better than me ;)
Reading your comment i have a doubt... i will cross check
Idea here is that there is a customer-provider relationchip with the
sd-modulator driver. So need that sd-modulator driver is probed first.

>> +			return PTR_ERR(adc->hwc);
>> +		}
>> +	}
>> +
>> +	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:
>> +	if (adc->hwc)
>> +		iio_hw_consumer_free(adc->hwc);
>> +	return ret;
>> +}
>> +
[...]

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

* Re: [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
@ 2017-02-27 10:09       ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:09 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,

Late answer... sorry for this.

I will integrate your remark in V2.
Please find my answers in-line

Regards
Arnaud

On 02/19/2017 03:46 PM, Jonathan Cameron wrote:
> On 13/02/17 16:38, 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.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> I think this fits together rather nicely.  As before, various comments that
> are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
> relevant ones.
> 
> So as far as I'm concerned: Looking forward to the full version!
> (as long as Mark and others are happy of course)
> 
> I definitely want to ultimately see buffered and dma support on the
> ADC driver side of things as well but that can come later.
Ok, I suppose that DMA in cyclic mode should also be required for some
other IIO driver...
> 
> Jonathan
>> ---
>>  drivers/iio/adc/Kconfig            |  13 +
>>  drivers/iio/adc/Makefile           |   1 +
>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>>  5 files changed, 911 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 d4366ac..ab917b6 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -452,6 +452,19 @@ config STM32_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-adc.
>>  
>> +config STM32_DFSDM_ADC
>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	select IIO_HW_CONSUMER
>> +	help
>> +	  Select this option to enable the  driver for STMicroelectronics
>> +	  STM32 digital filter for sigma delta converter (ADC).
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>>  obj-$(CONFIG_STX104) += stx104.o
>>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>> @@ -0,0 +1,483 @@
>> +/*
>> + * This file is part of STM32 DFSDM ADC driver
>> + *
>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include <sound/stm32-adfsdm.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +enum stm32_dfsdm_mode {
>> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
>> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
>> +};
>> +
>> +struct stm32_dfsdm_adc {
>> +	struct stm32_dfsdm *common;
>> +
>> +	unsigned int fl_id;
>> +	unsigned int oversamp;
>> +	unsigned int clk_freq;
>> +
>> +	enum stm32_dfsdm_mode mode;
>> +	struct platform_device *audio_pdev;
>> +
>> +	void (*overrun_cb)(void *context);
>> +	void *cb_context;
>> +
>> +	/* Hardware consumer structure for Front End iio */
> IIO
>> +	struct iio_hw_consumer *hwc;
>> +};
>> +
>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
>> +
>> +struct stm32_dfsdm_adc_devdata {
>> +	enum stm32_dfsdm_mode mode;
>> +	const struct iio_info *info;
>> +};
>> +
>> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
>> +				unsigned int oversamp)
>> +{
>> +	/*
>> +	 * TODO
>> +	 * This function tries to compute filter oversampling and integrator
>> +	 * oversampling, base on oversampling ratio requested by user.
>> +	 */
>> +
>> +	return 0;
>> +};
>> +
>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>> +				   const struct iio_chan_spec *chan, int *res)
>> +{
>> +	/* TODO: Perform conversion instead of sending fake value */
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> :( Definitely an RFC
>> +
>> +	*res = chan->channel + 0xFFFF00;
>> +	return 0;
>> +}
>> +
>> +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);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
>> +		if (!ret)
>> +			adc->oversamp = val;
> If no reason to carry on,(i.e. nothing to unwind) return directly from within
> the switch statement.
>> +		break;
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		if (adc->mode == DFSDM_AUDIO)
>> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
>> +		else
>> +			ret = -EINVAL;
>> +		break;
>> +
>> +	default:
>> +		ret = -EINVAL;
>> +	}
>> +
>> +	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;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		if (adc->hwc) {
>> +			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;
>> +			}
> Not an error if hwc not available?
ASoC framework requests to handle a codec driver. So in case of PDM
usecase, no IIO SD modulator device is used, instead an ASoC generic
dmic-codec device is probed. In other words the link between the DFSDM
and the external SD-modulator has to be done in ASOC for PDMs and in
IIO for ADCs.

>> +		}
>> +		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;
>> +		}
>> +
>> +		if (adc->hwc)
>> +			iio_hw_consumer_disable(adc->hwc);
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		*val = adc->oversamp;
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
>> +	.read_raw = stm32_dfsdm_read_raw,
>> +	.write_raw = stm32_dfsdm_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
> Hohum. These two are the same, why two copies?  Mind you for the audio
> you can't write or read anything so you could drop the callbacks.
yes to rework.
>> +
>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
>> +	.mode = DFSDM_ADC,
>> +	.info = &stm32_dfsdm_info_adc,
>> +};
>> +
>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
>> +	.mode = DFSDM_AUDIO,
>> +	.info = &stm32_dfsdm_info_audio,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	/* TODO */
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
>> +				   unsigned int freq)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s:\n", __func__);
>> +
>> +	adc->clk_freq = freq;
>> +};
>> +
>> +	/* Set expected audio sampling rate */
>> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
>> +				   struct stm32_dfsdm_hw_param *params)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
>> +
>> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
>> +};
>> +
>> +	/* Called when ASoC starts an audio stream setup. */
>> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +
>> +	return 0;
>> +};
>> +
>> +	/* Shuts down the audio stream. */
> Odd indenting.
>> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +};
>> +
>> +	/*
>> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
>> +	 * transfers.
> physical - please run a spell checker over the comments.
>> +	 */
>> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +
>> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
>> +};
>> +
>> +/* Register callback to treat underrun and overrun issues */
>> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
>> +					 void (*overrun_cb)(void *context),
>> +					 void *context)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +	adc->overrun_cb = overrun_cb;
>> +	adc->cb_context = context;
>> +};
>> +
>> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
>> +	.set_sysclk = stm32_dfsdm_set_sysclk,
>> +	.set_hwparam = stm32_dfsdm_set_hwparam,
>> +	.audio_startup = stm32_dfsdm_audio_startup,
>> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
>> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
>> +	.get_dma_source = stm32_dfsdm_get_dma_source
>> +};
> Hmm. I'm wondering if it might make sense to farm the audio stuff off
> to a separate file.  Potentially we'll have systems that are built with
> no audio support at all, so would be odd to have this stuff still provided.
Ok, this make sense if the API become generic.
> 
> What do you think?  Also provides an obvious clean bit to be Mark's problem
> (even if it's in the IIO directory ;)
I can't answer instead of Mark,( and perhaps i misunderstood...) but
regarding HDMI story (drivers shared between ASoC and DRM), not sure
that Mark accepts to have an ASoC drivers in IIO directory. So need a
part in ASoC that is customer of IIO driver...

>> +
>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>> +					 struct iio_chan_spec *chan,
>> +					 int chan_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>> +					 "st,adc-channels", chan_idx,
>> +					 &ch->channel);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			" error parsing 'st,adc-channels' for idx %d\n",
>> +			chan_idx);
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-names", chan_idx,
>> +					    &ch->datasheet_name);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			" error parsing 'st,adc-channel-names' for idx %d\n",
>> +			chan_idx);
>> +		return ret;
>> +	}
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = chan_idx;
>> +	if (adc->mode == DFSDM_ADC) {
>> +		/*
>> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
>> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
>> +		 */
>> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
>> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
> Very nice.  I was just thinking you should do this before I got here.
> Channels with no properties but still with an existence from the point of
> view of consumers using the buffered interface.  Potentially you 'could'
> provide some of the info as read only but why bother...
>> +	}
>> +
>> +	ch->scan_type.sign = 'u';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +
>> +	return 0;
>> +}
>> +
>> +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->common->num_chs) {
>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>> +		return num_ch < 0 ? num_ch : -EINVAL;
>> +	}
>> +
>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>> +				GFP_KERNEL);
>> +	if (!channels)
>> +		return -ENOMEM;
>> +
>> +	if (adc->mode == DFSDM_ADC) {
>> +		/*
>> +		 * Bind to sd modulator iio device for ADC only.
>> +		 * For Audio the PDM microphone will be handled by ASoC
>> +		 */
>> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>> +		if (IS_ERR(adc->hwc)) {
>> +			dev_err(&indio_dev->dev, "no backend found\n");
> Deferred probing a possibility? I'm not quite sure and you know what is going
> on here better than me ;)
Reading your comment i have a doubt... i will cross check
Idea here is that there is a customer-provider relationchip with the
sd-modulator driver. So need that sd-modulator driver is probed first.

>> +			return PTR_ERR(adc->hwc);
>> +		}
>> +	}
>> +
>> +	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:
>> +	if (adc->hwc)
>> +		iio_hw_consumer_free(adc->hwc);
>> +	return ret;
>> +}
>> +
[...]

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

* [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
@ 2017-02-27 10:09       ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:09 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Jonathan,

Late answer... sorry for this.

I will integrate your remark in V2.
Please find my answers in-line

Regards
Arnaud

On 02/19/2017 03:46 PM, Jonathan Cameron wrote:
> On 13/02/17 16:38, 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.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> I think this fits together rather nicely.  As before, various comments that
> are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
> relevant ones.
> 
> So as far as I'm concerned: Looking forward to the full version!
> (as long as Mark and others are happy of course)
> 
> I definitely want to ultimately see buffered and dma support on the
> ADC driver side of things as well but that can come later.
Ok, I suppose that DMA in cyclic mode should also be required for some
other IIO driver...
> 
> Jonathan
>> ---
>>  drivers/iio/adc/Kconfig            |  13 +
>>  drivers/iio/adc/Makefile           |   1 +
>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>>  5 files changed, 911 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 d4366ac..ab917b6 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -452,6 +452,19 @@ config STM32_ADC
>>  	  This driver can also be built as a module.  If so, the module
>>  	  will be called stm32-adc.
>>  
>> +config STM32_DFSDM_ADC
>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	select IIO_HW_CONSUMER
>> +	help
>> +	  Select this option to enable the  driver for STMicroelectronics
>> +	  STM32 digital filter for sigma delta converter (ADC).
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>>  obj-$(CONFIG_STX104) += stx104.o
>>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>> @@ -0,0 +1,483 @@
>> +/*
>> + * This file is part of STM32 DFSDM ADC driver
>> + *
>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
>> + *
>> + * License type: GPLv2
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but
>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>> + * See the GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/hw_consumer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#include <sound/stm32-adfsdm.h>
>> +
>> +#include "stm32-dfsdm.h"
>> +
>> +enum stm32_dfsdm_mode {
>> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
>> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
>> +};
>> +
>> +struct stm32_dfsdm_adc {
>> +	struct stm32_dfsdm *common;
>> +
>> +	unsigned int fl_id;
>> +	unsigned int oversamp;
>> +	unsigned int clk_freq;
>> +
>> +	enum stm32_dfsdm_mode mode;
>> +	struct platform_device *audio_pdev;
>> +
>> +	void (*overrun_cb)(void *context);
>> +	void *cb_context;
>> +
>> +	/* Hardware consumer structure for Front End iio */
> IIO
>> +	struct iio_hw_consumer *hwc;
>> +};
>> +
>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
>> +
>> +struct stm32_dfsdm_adc_devdata {
>> +	enum stm32_dfsdm_mode mode;
>> +	const struct iio_info *info;
>> +};
>> +
>> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
>> +				unsigned int oversamp)
>> +{
>> +	/*
>> +	 * TODO
>> +	 * This function tries to compute filter oversampling and integrator
>> +	 * oversampling, base on oversampling ratio requested by user.
>> +	 */
>> +
>> +	return 0;
>> +};
>> +
>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>> +				   const struct iio_chan_spec *chan, int *res)
>> +{
>> +	/* TODO: Perform conversion instead of sending fake value */
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
> :( Definitely an RFC
>> +
>> +	*res = chan->channel + 0xFFFF00;
>> +	return 0;
>> +}
>> +
>> +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);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
>> +		if (!ret)
>> +			adc->oversamp = val;
> If no reason to carry on,(i.e. nothing to unwind) return directly from within
> the switch statement.
>> +		break;
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		if (adc->mode == DFSDM_AUDIO)
>> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
>> +		else
>> +			ret = -EINVAL;
>> +		break;
>> +
>> +	default:
>> +		ret = -EINVAL;
>> +	}
>> +
>> +	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;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		if (adc->hwc) {
>> +			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;
>> +			}
> Not an error if hwc not available?
ASoC framework requests to handle a codec driver. So in case of PDM
usecase, no IIO SD modulator device is used, instead an ASoC generic
dmic-codec device is probed. In other words the link between the DFSDM
and the external SD-modulator has to be done in ASOC for PDMs and in
IIO for ADCs.

>> +		}
>> +		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;
>> +		}
>> +
>> +		if (adc->hwc)
>> +			iio_hw_consumer_disable(adc->hwc);
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +		*val = adc->oversamp;
>> +
>> +		return IIO_VAL_INT;
>> +
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
>> +	.read_raw = stm32_dfsdm_read_raw,
>> +	.write_raw = stm32_dfsdm_write_raw,
>> +	.driver_module = THIS_MODULE,
>> +};
> Hohum. These two are the same, why two copies?  Mind you for the audio
> you can't write or read anything so you could drop the callbacks.
yes to rework.
>> +
>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
>> +	.mode = DFSDM_ADC,
>> +	.info = &stm32_dfsdm_info_adc,
>> +};
>> +
>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
>> +	.mode = DFSDM_AUDIO,
>> +	.info = &stm32_dfsdm_info_audio,
>> +};
>> +
>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>> +{
>> +	/* TODO */
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
>> +				   unsigned int freq)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s:\n", __func__);
>> +
>> +	adc->clk_freq = freq;
>> +};
>> +
>> +	/* Set expected audio sampling rate */
>> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
>> +				   struct stm32_dfsdm_hw_param *params)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
>> +
>> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
>> +};
>> +
>> +	/* Called when ASoC starts an audio stream setup. */
>> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +
>> +	return 0;
>> +};
>> +
>> +	/* Shuts down the audio stream. */
> Odd indenting.
>> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +};
>> +
>> +	/*
>> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
>> +	 * transfers.
> physical - please run a spell checker over the comments.
>> +	 */
>> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +
>> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
>> +};
>> +
>> +/* Register callback to treat underrun and overrun issues */
>> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
>> +					 void (*overrun_cb)(void *context),
>> +					 void *context)
>> +{
>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>> +
>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>> +	adc->overrun_cb = overrun_cb;
>> +	adc->cb_context = context;
>> +};
>> +
>> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
>> +	.set_sysclk = stm32_dfsdm_set_sysclk,
>> +	.set_hwparam = stm32_dfsdm_set_hwparam,
>> +	.audio_startup = stm32_dfsdm_audio_startup,
>> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
>> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
>> +	.get_dma_source = stm32_dfsdm_get_dma_source
>> +};
> Hmm. I'm wondering if it might make sense to farm the audio stuff off
> to a separate file.  Potentially we'll have systems that are built with
> no audio support at all, so would be odd to have this stuff still provided.
Ok, this make sense if the API become generic.
> 
> What do you think?  Also provides an obvious clean bit to be Mark's problem
> (even if it's in the IIO directory ;)
I can't answer instead of Mark,( and perhaps i misunderstood...) but
regarding HDMI story (drivers shared between ASoC and DRM), not sure
that Mark accepts to have an ASoC drivers in IIO directory. So need a
part in ASoC that is customer of IIO driver...

>> +
>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>> +					 struct iio_chan_spec *chan,
>> +					 int chan_idx)
>> +{
>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>> +					 "st,adc-channels", chan_idx,
>> +					 &ch->channel);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			" error parsing 'st,adc-channels' for idx %d\n",
>> +			chan_idx);
>> +		return ret;
>> +	}
>> +
>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>> +					    "st,adc-channel-names", chan_idx,
>> +					    &ch->datasheet_name);
>> +	if (ret < 0) {
>> +		dev_err(&indio_dev->dev,
>> +			" error parsing 'st,adc-channel-names' for idx %d\n",
>> +			chan_idx);
>> +		return ret;
>> +	}
>> +
>> +	ch->type = IIO_VOLTAGE;
>> +	ch->indexed = 1;
>> +	ch->scan_index = chan_idx;
>> +	if (adc->mode == DFSDM_ADC) {
>> +		/*
>> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
>> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
>> +		 */
>> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
>> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
> Very nice.  I was just thinking you should do this before I got here.
> Channels with no properties but still with an existence from the point of
> view of consumers using the buffered interface.  Potentially you 'could'
> provide some of the info as read only but why bother...
>> +	}
>> +
>> +	ch->scan_type.sign = 'u';
>> +	ch->scan_type.realbits = 24;
>> +	ch->scan_type.storagebits = 32;
>> +
>> +	return 0;
>> +}
>> +
>> +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->common->num_chs) {
>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>> +		return num_ch < 0 ? num_ch : -EINVAL;
>> +	}
>> +
>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>> +				GFP_KERNEL);
>> +	if (!channels)
>> +		return -ENOMEM;
>> +
>> +	if (adc->mode == DFSDM_ADC) {
>> +		/*
>> +		 * Bind to sd modulator iio device for ADC only.
>> +		 * For Audio the PDM microphone will be handled by ASoC
>> +		 */
>> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>> +		if (IS_ERR(adc->hwc)) {
>> +			dev_err(&indio_dev->dev, "no backend found\n");
> Deferred probing a possibility? I'm not quite sure and you know what is going
> on here better than me ;)
Reading your comment i have a doubt... i will cross check
Idea here is that there is a customer-provider relationchip with the
sd-modulator driver. So need that sd-modulator driver is probed first.

>> +			return PTR_ERR(adc->hwc);
>> +		}
>> +	}
>> +
>> +	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:
>> +	if (adc->hwc)
>> +		iio_hw_consumer_free(adc->hwc);
>> +	return ret;
>> +}
>> +
[...]

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-19 14:56           ` Jonathan Cameron
  (?)
@ 2017-02-27 10:31             ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:31 UTC (permalink / raw)
  To: Jonathan Cameron, Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Olivier MOYSAN, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Rob Herring, Peter Meerwald-Stadler,
	Hartmut Knaack, linux-arm-kernel, Alexandre TORGUE



On 02/19/2017 03:56 PM, Jonathan Cameron wrote:
> On 14/02/17 17:45, Mark Brown wrote:
>> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
>>
>> This looks basically fine as a system specific driver but as some of the
>> comments in here say there's bits of it could perhaps be genericised but
>> I'm not sure we need to do that right now.  I'm not sure the abstraction
>> is exactly comfortable but having another bit of hardware doing a bridge
>> to IIO might be the best way to figure out something better.
> Agreed.  To an extent we are fishing around in the dark at the moment.
> Lets wait until we have a few more cases of similar hardware before trying
> too much generalization.  This is acting as a good exploration of what
> is needed.

So for now i keep like this the API between ASOC and IIO, means not
generic API, and DMA handled in ASOC?

Then when some other hardwares come with same kind of requirements, we
will re-discuss a more generic way to do it...

> 
> Ideally Lars might upstream some of the other bits he has in his tree
> to do with DSP processing on ADC streams and that might provide us with
> more clues on generality (at least at the lowest layers)
> (Apparently he's busy - though he always makes that excuse :)
> Joking aside, the exploration Analog and in particular Lars does around
> pushing the limits of how things interact is always useful!)
> 
>>
>>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>>> +	.period_bytes_max = 4 * PAGE_SIZE,
>>> +	.buffer_bytes_max = 16 * PAGE_SIZE
>>
>> What's the physical minimum period limit?  The comment makes this sound
>> like it's just made up.
>>
>>> +	unsigned int shift = 24 -priv->max_scaling;
>>> +	
>>
>> Missing space after -.
>>
>>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>>> +	return 0;
>>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>>> +					  SNDRV_PCM_HW_PARAM_RATE,
>>> +					  &priv->rates_const);
>>
>> Looks like debug changes got left in?
>>
>>> +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);
>>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>>> +
>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>> +	if (dir == SND_SOC_CLOCK_IN) {
>>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>>> +		priv->dmic_clk = freq;
>>> +	}
>>> +
>>> +	/* Determine supported rate which depends on SPI/manchester clock */
>>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
>>
>> Since the DAI is unidirectional it doesn't matter but obviously if it
>> weren't then the fact that getting the supported rates involves setting
>> the hwparams means this could become disruptive.  If we're going to
>> genericise this to be a more general IIO/ASoC bridge that could matter.
>>
>>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>>> +{
>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>> +
>>> +	return 0;
>>> +}
>>
>> Remove empty functions, though in this case I think you want to add
>> something to disconnect the XRUN callback just in order to be sure it
>> can't be mistakenly called.
>>
> 

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-27 10:31             ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:31 UTC (permalink / raw)
  To: Jonathan Cameron, Mark Brown
  Cc: Rob Herring, Mark Rutland, 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



On 02/19/2017 03:56 PM, Jonathan Cameron wrote:
> On 14/02/17 17:45, Mark Brown wrote:
>> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
>>
>> This looks basically fine as a system specific driver but as some of the
>> comments in here say there's bits of it could perhaps be genericised but
>> I'm not sure we need to do that right now.  I'm not sure the abstraction
>> is exactly comfortable but having another bit of hardware doing a bridge
>> to IIO might be the best way to figure out something better.
> Agreed.  To an extent we are fishing around in the dark at the moment.
> Lets wait until we have a few more cases of similar hardware before trying
> too much generalization.  This is acting as a good exploration of what
> is needed.

So for now i keep like this the API between ASOC and IIO, means not
generic API, and DMA handled in ASOC?

Then when some other hardwares come with same kind of requirements, we
will re-discuss a more generic way to do it...

> 
> Ideally Lars might upstream some of the other bits he has in his tree
> to do with DSP processing on ADC streams and that might provide us with
> more clues on generality (at least at the lowest layers)
> (Apparently he's busy - though he always makes that excuse :)
> Joking aside, the exploration Analog and in particular Lars does around
> pushing the limits of how things interact is always useful!)
> 
>>
>>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>>> +	.period_bytes_max = 4 * PAGE_SIZE,
>>> +	.buffer_bytes_max = 16 * PAGE_SIZE
>>
>> What's the physical minimum period limit?  The comment makes this sound
>> like it's just made up.
>>
>>> +	unsigned int shift = 24 -priv->max_scaling;
>>> +	
>>
>> Missing space after -.
>>
>>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>>> +	return 0;
>>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>>> +					  SNDRV_PCM_HW_PARAM_RATE,
>>> +					  &priv->rates_const);
>>
>> Looks like debug changes got left in?
>>
>>> +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);
>>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>>> +
>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>> +	if (dir == SND_SOC_CLOCK_IN) {
>>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>>> +		priv->dmic_clk = freq;
>>> +	}
>>> +
>>> +	/* Determine supported rate which depends on SPI/manchester clock */
>>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
>>
>> Since the DAI is unidirectional it doesn't matter but obviously if it
>> weren't then the fact that getting the supported rates involves setting
>> the hwparams means this could become disruptive.  If we're going to
>> genericise this to be a more general IIO/ASoC bridge that could matter.
>>
>>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>>> +{
>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>> +
>>> +	return 0;
>>> +}
>>
>> Remove empty functions, though in this case I think you want to add
>> something to disconnect the XRUN callback just in order to be sure it
>> can't be mistakenly called.
>>
> 

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

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-02-27 10:31             ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:31 UTC (permalink / raw)
  To: linux-arm-kernel



On 02/19/2017 03:56 PM, Jonathan Cameron wrote:
> On 14/02/17 17:45, Mark Brown wrote:
>> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
>>
>> This looks basically fine as a system specific driver but as some of the
>> comments in here say there's bits of it could perhaps be genericised but
>> I'm not sure we need to do that right now.  I'm not sure the abstraction
>> is exactly comfortable but having another bit of hardware doing a bridge
>> to IIO might be the best way to figure out something better.
> Agreed.  To an extent we are fishing around in the dark at the moment.
> Lets wait until we have a few more cases of similar hardware before trying
> too much generalization.  This is acting as a good exploration of what
> is needed.

So for now i keep like this the API between ASOC and IIO, means not
generic API, and DMA handled in ASOC?

Then when some other hardwares come with same kind of requirements, we
will re-discuss a more generic way to do it...

> 
> Ideally Lars might upstream some of the other bits he has in his tree
> to do with DSP processing on ADC streams and that might provide us with
> more clues on generality (at least at the lowest layers)
> (Apparently he's busy - though he always makes that excuse :)
> Joking aside, the exploration Analog and in particular Lars does around
> pushing the limits of how things interact is always useful!)
> 
>>
>>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>>> +	.period_bytes_max = 4 * PAGE_SIZE,
>>> +	.buffer_bytes_max = 16 * PAGE_SIZE
>>
>> What's the physical minimum period limit?  The comment makes this sound
>> like it's just made up.
>>
>>> +	unsigned int shift = 24 -priv->max_scaling;
>>> +	
>>
>> Missing space after -.
>>
>>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>>> +	return 0;
>>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>>> +					  SNDRV_PCM_HW_PARAM_RATE,
>>> +					  &priv->rates_const);
>>
>> Looks like debug changes got left in?
>>
>>> +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);
>>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>>> +
>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>> +	if (dir == SND_SOC_CLOCK_IN) {
>>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>>> +		priv->dmic_clk = freq;
>>> +	}
>>> +
>>> +	/* Determine supported rate which depends on SPI/manchester clock */
>>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
>>
>> Since the DAI is unidirectional it doesn't matter but obviously if it
>> weren't then the fact that getting the supported rates involves setting
>> the hwparams means this could become disruptive.  If we're going to
>> genericise this to be a more general IIO/ASoC bridge that could matter.
>>
>>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>>> +{
>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>> +
>>> +	return 0;
>>> +}
>>
>> Remove empty functions, though in this case I think you want to add
>> something to disconnect the XRUN callback just in order to be sure it
>> can't be mistakenly called.
>>
> 

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
  2017-02-19 15:00     ` Jonathan Cameron
  (?)
@ 2017-02-27 10:47       ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:47 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 02/19/2017 04:00 PM, Jonathan Cameron wrote:
> On 13/02/17 16:38, Arnaud Pouliquen wrote:
>> Add bindings that describe Digital Filter for Sigma Delta
>> Modulators. DFSDM allows to connect sigma delta
>> modulators and/or PDM microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>  1 file changed, 125 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..83937cb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>> @@ -0,0 +1,125 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>> +interface external sigma delta modulators to stm32 micro controlers.
>> +it is mainly targeted for:
>> +- Sigma delta modulators ( motor control, metering...)
>> +- PDM microphones ( audio digital microphone)
>> +
>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>> +up to 4 filters.
>> +
>> +Each instance of the sub-drivers uses one filter instance.
>> +
>> +Contents of a stm32 dfsdmc root node:
>> +-------------------------------------
>> +Required properties:
>> +- compatible: Should be "st,stm32-dfsdm-adc".
>> +- 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.
>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>> +- clock-names: Must be "adc".
>> +
>> +Optional properties:
>> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
>> +- reg: Specifies the DFSDM filter instance.
>> +- 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 channels Name.
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
>> +
>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- io-channels: from common iio binding. use to pipe external sigma delta
>> +		modulator or internal ADC output to dfsdm channel.
>> +
>> +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. Default value is 0.
>> +			- "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. default value is 1.
>> +			  - "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.
>> +		   to used for multi microphone synchronization.
>> +
>> +Example of a sigma delta adc purpose:
>> +	ads1202: simple_sd_adc@0 {
>> +		compatible = "sd-modulator";
> This suggests to me that we ought to have a primary compatible of the actual part
> number.  Down the line I suspect we will want to distinguish between the different
> ADCs and it's kind of easier if we do it from the start.  May not be obvious
> later which the 'default sd-modulator' is.
Not sure to understand your point... you mean use ADC name instead, for
compatibility as suggested by Rob?

>> +		#io-channel-cells = <1>;
>> +		status = "okay";
>> +	};
>> +	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 = "in0";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +			io-channels = <&ads1202 0>;
>> +	};
>> +
>> +Example of a pdm microphone purpose:
> Is there nothing about the particular pdm microphone that is
> worth giving it an explicit representation in the device
> tree?  I've no idea having never used one!
>> +	dfsdm: dfsdm@40017000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm";
>> +
>> +		dfsdm_pdm0: dfsdm-pdm@0 {
>> +			compatible = "st,stm32-dfsdm-pdm";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			interrupts = <110>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,adc-channels = <0>;
>> +			st,adc-channel-names = "pdm1";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +		};
>> +	}
>>
> 
Here is a way to use it in device tree.
- dmic_0 is an ASOC generic device that handles the PDM microphone
- dai-link allows to bind the DFSDM with the SD-modulator in ALSA card
declaration.

	dmic0: dmic_0@0 {
		compatible = "dmic-codec";
		#sound-dai-cells = <0>;

		status = "okay";
	};

	sound_dfsdm_pdm {
		compatible = "simple-audio-card";
		simple-audio-card,name = "dfsdm_pdm";
		status = "okay";

		dfsdm0_mic0: simple-audio-card,dai-link@0 {
			format = "pdm";
			bitclock-master = <&dmic0_codec>;
			cpu {
				system-clock-frequency= <2480000>;
				sound-dai = <&dfsdm_pdm0>;
			};
			dmic0_codec: codec {
				sound-dai = <&dmic0>;
			};
		};
	};
};

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-27 10:47       ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:47 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 02/19/2017 04:00 PM, Jonathan Cameron wrote:
> On 13/02/17 16:38, Arnaud Pouliquen wrote:
>> Add bindings that describe Digital Filter for Sigma Delta
>> Modulators. DFSDM allows to connect sigma delta
>> modulators and/or PDM microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>  1 file changed, 125 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..83937cb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>> @@ -0,0 +1,125 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>> +interface external sigma delta modulators to stm32 micro controlers.
>> +it is mainly targeted for:
>> +- Sigma delta modulators ( motor control, metering...)
>> +- PDM microphones ( audio digital microphone)
>> +
>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>> +up to 4 filters.
>> +
>> +Each instance of the sub-drivers uses one filter instance.
>> +
>> +Contents of a stm32 dfsdmc root node:
>> +-------------------------------------
>> +Required properties:
>> +- compatible: Should be "st,stm32-dfsdm-adc".
>> +- 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.
>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>> +- clock-names: Must be "adc".
>> +
>> +Optional properties:
>> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
>> +- reg: Specifies the DFSDM filter instance.
>> +- 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 channels Name.
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
>> +
>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- io-channels: from common iio binding. use to pipe external sigma delta
>> +		modulator or internal ADC output to dfsdm channel.
>> +
>> +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. Default value is 0.
>> +			- "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. default value is 1.
>> +			  - "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.
>> +		   to used for multi microphone synchronization.
>> +
>> +Example of a sigma delta adc purpose:
>> +	ads1202: simple_sd_adc@0 {
>> +		compatible = "sd-modulator";
> This suggests to me that we ought to have a primary compatible of the actual part
> number.  Down the line I suspect we will want to distinguish between the different
> ADCs and it's kind of easier if we do it from the start.  May not be obvious
> later which the 'default sd-modulator' is.
Not sure to understand your point... you mean use ADC name instead, for
compatibility as suggested by Rob?

>> +		#io-channel-cells = <1>;
>> +		status = "okay";
>> +	};
>> +	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 = "in0";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +			io-channels = <&ads1202 0>;
>> +	};
>> +
>> +Example of a pdm microphone purpose:
> Is there nothing about the particular pdm microphone that is
> worth giving it an explicit representation in the device
> tree?  I've no idea having never used one!
>> +	dfsdm: dfsdm@40017000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm";
>> +
>> +		dfsdm_pdm0: dfsdm-pdm@0 {
>> +			compatible = "st,stm32-dfsdm-pdm";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			interrupts = <110>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,adc-channels = <0>;
>> +			st,adc-channel-names = "pdm1";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +		};
>> +	}
>>
> 
Here is a way to use it in device tree.
- dmic_0 is an ASOC generic device that handles the PDM microphone
- dai-link allows to bind the DFSDM with the SD-modulator in ALSA card
declaration.

	dmic0: dmic_0@0 {
		compatible = "dmic-codec";
		#sound-dai-cells = <0>;

		status = "okay";
	};

	sound_dfsdm_pdm {
		compatible = "simple-audio-card";
		simple-audio-card,name = "dfsdm_pdm";
		status = "okay";

		dfsdm0_mic0: simple-audio-card,dai-link@0 {
			format = "pdm";
			bitclock-master = <&dmic0_codec>;
			cpu {
				system-clock-frequency= <2480000>;
				sound-dai = <&dfsdm_pdm0>;
			};
			dmic0_codec: codec {
				sound-dai = <&dmic0>;
			};
		};
	};
};


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

* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-27 10:47       ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 10:47 UTC (permalink / raw)
  To: linux-arm-kernel



On 02/19/2017 04:00 PM, Jonathan Cameron wrote:
> On 13/02/17 16:38, Arnaud Pouliquen wrote:
>> Add bindings that describe Digital Filter for Sigma Delta
>> Modulators. DFSDM allows to connect sigma delta
>> modulators and/or PDM microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>  1 file changed, 125 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..83937cb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>> @@ -0,0 +1,125 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>> +interface external sigma delta modulators to stm32 micro controlers.
>> +it is mainly targeted for:
>> +- Sigma delta modulators ( motor control, metering...)
>> +- PDM microphones ( audio digital microphone)
>> +
>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>> +up to 4 filters.
>> +
>> +Each instance of the sub-drivers uses one filter instance.
>> +
>> +Contents of a stm32 dfsdmc root node:
>> +-------------------------------------
>> +Required properties:
>> +- compatible: Should be "st,stm32-dfsdm-adc".
>> +- 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.
>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>> +- clock-names: Must be "adc".
>> +
>> +Optional properties:
>> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
>> +- reg: Specifies the DFSDM filter instance.
>> +- 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 channels Name.
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
>> +
>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- io-channels: from common iio binding. use to pipe external sigma delta
>> +		modulator or internal ADC output to dfsdm channel.
>> +
>> +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. Default value is 0.
>> +			- "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. default value is 1.
>> +			  - "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.
>> +		   to used for multi microphone synchronization.
>> +
>> +Example of a sigma delta adc purpose:
>> +	ads1202: simple_sd_adc at 0 {
>> +		compatible = "sd-modulator";
> This suggests to me that we ought to have a primary compatible of the actual part
> number.  Down the line I suspect we will want to distinguish between the different
> ADCs and it's kind of easier if we do it from the start.  May not be obvious
> later which the 'default sd-modulator' is.
Not sure to understand your point... you mean use ADC name instead, for
compatibility as suggested by Rob?

>> +		#io-channel-cells = <1>;
>> +		status = "okay";
>> +	};
>> +	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 = "in0";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +			io-channels = <&ads1202 0>;
>> +	};
>> +
>> +Example of a pdm microphone purpose:
> Is there nothing about the particular pdm microphone that is
> worth giving it an explicit representation in the device
> tree?  I've no idea having never used one!
>> +	dfsdm: dfsdm at 40017000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm";
>> +
>> +		dfsdm_pdm0: dfsdm-pdm at 0 {
>> +			compatible = "st,stm32-dfsdm-pdm";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			interrupts = <110>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,adc-channels = <0>;
>> +			st,adc-channel-names = "pdm1";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +		};
>> +	}
>>
> 
Here is a way to use it in device tree.
- dmic_0 is an ASOC generic device that handles the PDM microphone
- dai-link allows to bind the DFSDM with the SD-modulator in ALSA card
declaration.

	dmic0: dmic_0 at 0 {
		compatible = "dmic-codec";
		#sound-dai-cells = <0>;

		status = "okay";
	};

	sound_dfsdm_pdm {
		compatible = "simple-audio-card";
		simple-audio-card,name = "dfsdm_pdm";
		status = "okay";

		dfsdm0_mic0: simple-audio-card,dai-link at 0 {
			format = "pdm";
			bitclock-master = <&dmic0_codec>;
			cpu {
				system-clock-frequency= <2480000>;
				sound-dai = <&dfsdm_pdm0>;
			};
			dmic0_codec: codec {
				sound-dai = <&dmic0>;
			};
		};
	};
};

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

* Re: [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
  2017-02-22 15:17     ` Rob Herring
  (?)
@ 2017-02-27 11:15       ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 11:15 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,

Please find my answers in-line

Regards,

Arnaud

On 02/22/2017 04:17 PM, Rob Herring wrote:
> On Mon, Feb 13, 2017 at 05:38:24PM +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>
>> ---
>>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>>  1 file changed, 13 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>> new file mode 100644
>> index 0000000..2b3968a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>> @@ -0,0 +1,13 @@
>> +Device-Tree bindings for simple sigma delta adc
> 
> What makes it "simple"?
"simple" sigma delta modulator are external chip that just converts
analog signal in sigma delta modulation. No configuration needs.
"Simple" is use to differentiate SD ADC that offers interfaces to
activate some internal processing ( gain, offset compensation...)

> 
>> +
>> +Required properties:
>> +- compatible: should be "sd-modulator".
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +
>> +Example node:
>> +
>> +	ads1202: simple_sd_adc@0 {
> 
> Is ads1202 the actual chip? Then it should be in the compatible list.
I tried to define a generic device to support several SD modulator
chips, inspired by ALSA "dmic-codec" device.
I have none exhaustive list of chip that can be handled by the driver,
but i found several devices that could match...
That why i did not use chip name in compatible list.
What should be the best way to do it?

> 
> unit address without a reg prop is an error. The node name should be 
> "adc".
> 
>> +		compatible = "sd-modulator";
>> +		#io-channel-cells = <1>;
>> +		status = "okay";
> 
> Drop status from examples.
> 
>> +	};
>> -- 
>> 1.9.1
>>

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

* Re: [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
@ 2017-02-27 11:15       ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 11:15 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,

Please find my answers in-line

Regards,

Arnaud

On 02/22/2017 04:17 PM, Rob Herring wrote:
> On Mon, Feb 13, 2017 at 05:38:24PM +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>
>> ---
>>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>>  1 file changed, 13 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>> new file mode 100644
>> index 0000000..2b3968a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>> @@ -0,0 +1,13 @@
>> +Device-Tree bindings for simple sigma delta adc
> 
> What makes it "simple"?
"simple" sigma delta modulator are external chip that just converts
analog signal in sigma delta modulation. No configuration needs.
"Simple" is use to differentiate SD ADC that offers interfaces to
activate some internal processing ( gain, offset compensation...)

> 
>> +
>> +Required properties:
>> +- compatible: should be "sd-modulator".
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +
>> +Example node:
>> +
>> +	ads1202: simple_sd_adc@0 {
> 
> Is ads1202 the actual chip? Then it should be in the compatible list.
I tried to define a generic device to support several SD modulator
chips, inspired by ALSA "dmic-codec" device.
I have none exhaustive list of chip that can be handled by the driver,
but i found several devices that could match...
That why i did not use chip name in compatible list.
What should be the best way to do it?

> 
> unit address without a reg prop is an error. The node name should be 
> "adc".
> 
>> +		compatible = "sd-modulator";
>> +		#io-channel-cells = <1>;
>> +		status = "okay";
> 
> Drop status from examples.
> 
>> +	};
>> -- 
>> 1.9.1
>>

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

* [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
@ 2017-02-27 11:15       ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 11:15 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Rob,

Please find my answers in-line

Regards,

Arnaud

On 02/22/2017 04:17 PM, Rob Herring wrote:
> On Mon, Feb 13, 2017 at 05:38:24PM +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>
>> ---
>>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>>  1 file changed, 13 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>> new file mode 100644
>> index 0000000..2b3968a
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>> @@ -0,0 +1,13 @@
>> +Device-Tree bindings for simple sigma delta adc
> 
> What makes it "simple"?
"simple" sigma delta modulator are external chip that just converts
analog signal in sigma delta modulation. No configuration needs.
"Simple" is use to differentiate SD ADC that offers interfaces to
activate some internal processing ( gain, offset compensation...)

> 
>> +
>> +Required properties:
>> +- compatible: should be "sd-modulator".
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +
>> +Example node:
>> +
>> +	ads1202: simple_sd_adc at 0 {
> 
> Is ads1202 the actual chip? Then it should be in the compatible list.
I tried to define a generic device to support several SD modulator
chips, inspired by ALSA "dmic-codec" device.
I have none exhaustive list of chip that can be handled by the driver,
but i found several devices that could match...
That why i did not use chip name in compatible list.
What should be the best way to do it?

> 
> unit address without a reg prop is an error. The node name should be 
> "adc".
> 
>> +		compatible = "sd-modulator";
>> +		#io-channel-cells = <1>;
>> +		status = "okay";
> 
> Drop status from examples.
> 
>> +	};
>> -- 
>> 1.9.1
>>

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
  2017-02-22 16:42       ` Rob Herring
  (?)
@ 2017-02-27 14:07         ` Arnaud Pouliquen
  -1 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 14:07 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



On 02/22/2017 05:42 PM, Rob Herring wrote:
> On Mon, Feb 13, 2017 at 05:38:28PM +0100, Arnaud Pouliquen wrote:
>> Add bindings that describe Digital Filter for Sigma Delta
>> Modulators. DFSDM allows to connect sigma delta
>> modulators and/or PDM microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>  1 file changed, 125 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..83937cb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>> @@ -0,0 +1,125 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>> +interface external sigma delta modulators to stm32 micro controlers.
>> +it is mainly targeted for:
>> +- Sigma delta modulators ( motor control, metering...)
>> +- PDM microphones ( audio digital microphone)
>> +
>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>> +up to 4 filters.
>> +
>> +Each instance of the sub-drivers uses one filter instance.
>> +
>> +Contents of a stm32 dfsdmc root node:
>> +-------------------------------------
>> +Required properties:
>> +- compatible: Should be "st,stm32-dfsdm-adc".
>> +- 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.
>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>> +- clock-names: Must be "adc".
>> +
>> +Optional properties:
>> +- st,clkout-freq: needed 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, clkout frequency will not be
>> +		  accurate.
> 
> We already have a standard property to specify the SPI freq.
I found spi-max-frequency. In this case it is a fixed frequency, not a
max... anyway if preferred i will use it.

> 
>> +
>> +Contents of a stm32 DFSDM child nodes:
>> +----------------------------------------
>> +
>> +Required properties:
>> +- compatible: Must be:
>> +	"st,stm32-dfsdm-adc" for sigma delta ADCs
>> +	"st,stm32-dfsdm-pdm" for audio digital microphone.
>> +- reg: Specifies the DFSDM filter instance.
>> +- 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 channels Name.
> 
> Why do you need names?
Name is used to expose an explicit IIO interface to userland (sysfs). So
this field allows to expose an explicit name corresponding to hardware
connected to the DFSDM (pdm microphone, adc for motor control,internal
ADC, ...).
if you prefer I can clean it and use a non explicit hard-coded name (as
stm32-adc.c)

> 
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
>> +
>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- io-channels: from common iio binding. use to pipe external sigma delta
>> +		modulator or internal ADC output to dfsdm channel.
>> +
>> +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. Default value is 0.
> 
> How is the default 0 when the values are strings?
oops, miss cleaning from first version...
> 
>> +			- "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. default value is 1.
> 
> Ditto.
> 
> Perhaps you should be using the clock binding and assigned-clocks 
> property.
This corresponds to bus clocks, is it compatible with clock binding?
or perhaps the issue is that my naming is not very pertinent...
> 
>> +			  - "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.
>> +		   to used for multi microphone synchronization.
> 
> Is there an instance 1?  If not, drop the 0. If you need to expand to 
> multiple instances, you can have multiple values.
Yes there is an instance 0. We can synchronize all the filter output on
filter instance 0. This is possible only for instance 0. , As example
this could be useful for audio stereo recording.
> 
>> +
>> +Example of a sigma delta adc purpose:
>> +	ads1202: simple_sd_adc@0 {
>> +		compatible = "sd-modulator";
>> +		#io-channel-cells = <1>;
>> +		status = "okay";
>> +	};
>> +	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>;
> 
> Does reg provide the same thing?
reg represents the instance of the filter ( 4 instances)
Channels ( 8 instances) are linked to the input interface.
we can connect one or several channels to one filter.
> 
>> +			st,adc-channel-names = "in0";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +			io-channels = <&ads1202 0>;
>> +	};
>> +
>> +Example of a pdm microphone purpose:
>> +	dfsdm: dfsdm@40017000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm";
>> +
>> +		dfsdm_pdm0: dfsdm-pdm@0 {
>> +			compatible = "st,stm32-dfsdm-pdm";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			interrupts = <110>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,adc-channels = <0>;
>> +			st,adc-channel-names = "pdm1";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +		};
>> +	}
>> -- 
>> 1.9.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-27 14:07         ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 14:07 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



On 02/22/2017 05:42 PM, Rob Herring wrote:
> On Mon, Feb 13, 2017 at 05:38:28PM +0100, Arnaud Pouliquen wrote:
>> Add bindings that describe Digital Filter for Sigma Delta
>> Modulators. DFSDM allows to connect sigma delta
>> modulators and/or PDM microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>  1 file changed, 125 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..83937cb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>> @@ -0,0 +1,125 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>> +interface external sigma delta modulators to stm32 micro controlers.
>> +it is mainly targeted for:
>> +- Sigma delta modulators ( motor control, metering...)
>> +- PDM microphones ( audio digital microphone)
>> +
>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>> +up to 4 filters.
>> +
>> +Each instance of the sub-drivers uses one filter instance.
>> +
>> +Contents of a stm32 dfsdmc root node:
>> +-------------------------------------
>> +Required properties:
>> +- compatible: Should be "st,stm32-dfsdm-adc".
>> +- 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.
>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>> +- clock-names: Must be "adc".
>> +
>> +Optional properties:
>> +- st,clkout-freq: needed 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, clkout frequency will not be
>> +		  accurate.
> 
> We already have a standard property to specify the SPI freq.
I found spi-max-frequency. In this case it is a fixed frequency, not a
max... anyway if preferred i will use it.

> 
>> +
>> +Contents of a stm32 DFSDM child nodes:
>> +----------------------------------------
>> +
>> +Required properties:
>> +- compatible: Must be:
>> +	"st,stm32-dfsdm-adc" for sigma delta ADCs
>> +	"st,stm32-dfsdm-pdm" for audio digital microphone.
>> +- reg: Specifies the DFSDM filter instance.
>> +- 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 channels Name.
> 
> Why do you need names?
Name is used to expose an explicit IIO interface to userland (sysfs). So
this field allows to expose an explicit name corresponding to hardware
connected to the DFSDM (pdm microphone, adc for motor control,internal
ADC, ...).
if you prefer I can clean it and use a non explicit hard-coded name (as
stm32-adc.c)

> 
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
>> +
>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- io-channels: from common iio binding. use to pipe external sigma delta
>> +		modulator or internal ADC output to dfsdm channel.
>> +
>> +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. Default value is 0.
> 
> How is the default 0 when the values are strings?
oops, miss cleaning from first version...
> 
>> +			- "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. default value is 1.
> 
> Ditto.
> 
> Perhaps you should be using the clock binding and assigned-clocks 
> property.
This corresponds to bus clocks, is it compatible with clock binding?
or perhaps the issue is that my naming is not very pertinent...
> 
>> +			  - "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.
>> +		   to used for multi microphone synchronization.
> 
> Is there an instance 1?  If not, drop the 0. If you need to expand to 
> multiple instances, you can have multiple values.
Yes there is an instance 0. We can synchronize all the filter output on
filter instance 0. This is possible only for instance 0. , As example
this could be useful for audio stereo recording.
> 
>> +
>> +Example of a sigma delta adc purpose:
>> +	ads1202: simple_sd_adc@0 {
>> +		compatible = "sd-modulator";
>> +		#io-channel-cells = <1>;
>> +		status = "okay";
>> +	};
>> +	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>;
> 
> Does reg provide the same thing?
reg represents the instance of the filter ( 4 instances)
Channels ( 8 instances) are linked to the input interface.
we can connect one or several channels to one filter.
> 
>> +			st,adc-channel-names = "in0";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +			io-channels = <&ads1202 0>;
>> +	};
>> +
>> +Example of a pdm microphone purpose:
>> +	dfsdm: dfsdm@40017000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm";
>> +
>> +		dfsdm_pdm0: dfsdm-pdm@0 {
>> +			compatible = "st,stm32-dfsdm-pdm";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			interrupts = <110>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,adc-channels = <0>;
>> +			st,adc-channel-names = "pdm1";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +		};
>> +	}
>> -- 
>> 1.9.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-02-27 14:07         ` Arnaud Pouliquen
  0 siblings, 0 replies; 107+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 14:07 UTC (permalink / raw)
  To: linux-arm-kernel



On 02/22/2017 05:42 PM, Rob Herring wrote:
> On Mon, Feb 13, 2017 at 05:38:28PM +0100, Arnaud Pouliquen wrote:
>> Add bindings that describe Digital Filter for Sigma Delta
>> Modulators. DFSDM allows to connect sigma delta
>> modulators and/or PDM microphones.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>  1 file changed, 125 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..83937cb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>> @@ -0,0 +1,125 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>> +interface external sigma delta modulators to stm32 micro controlers.
>> +it is mainly targeted for:
>> +- Sigma delta modulators ( motor control, metering...)
>> +- PDM microphones ( audio digital microphone)
>> +
>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>> +up to 4 filters.
>> +
>> +Each instance of the sub-drivers uses one filter instance.
>> +
>> +Contents of a stm32 dfsdmc root node:
>> +-------------------------------------
>> +Required properties:
>> +- compatible: Should be "st,stm32-dfsdm-adc".
>> +- 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.
>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>> +- clock-names: Must be "adc".
>> +
>> +Optional properties:
>> +- st,clkout-freq: needed 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, clkout frequency will not be
>> +		  accurate.
> 
> We already have a standard property to specify the SPI freq.
I found spi-max-frequency. In this case it is a fixed frequency, not a
max... anyway if preferred i will use it.

> 
>> +
>> +Contents of a stm32 DFSDM child nodes:
>> +----------------------------------------
>> +
>> +Required properties:
>> +- compatible: Must be:
>> +	"st,stm32-dfsdm-adc" for sigma delta ADCs
>> +	"st,stm32-dfsdm-pdm" for audio digital microphone.
>> +- reg: Specifies the DFSDM filter instance.
>> +- 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 channels Name.
> 
> Why do you need names?
Name is used to expose an explicit IIO interface to userland (sysfs). So
this field allows to expose an explicit name corresponding to hardware
connected to the DFSDM (pdm microphone, adc for motor control,internal
ADC, ...).
if you prefer I can clean it and use a non explicit hard-coded name (as
stm32-adc.c)

> 
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
>> +
>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- io-channels: from common iio binding. use to pipe external sigma delta
>> +		modulator or internal ADC output to dfsdm channel.
>> +
>> +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. Default value is 0.
> 
> How is the default 0 when the values are strings?
oops, miss cleaning from first version...
> 
>> +			- "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. default value is 1.
> 
> Ditto.
> 
> Perhaps you should be using the clock binding and assigned-clocks 
> property.
This corresponds to bus clocks, is it compatible with clock binding?
or perhaps the issue is that my naming is not very pertinent...
> 
>> +			  - "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.
>> +		   to used for multi microphone synchronization.
> 
> Is there an instance 1?  If not, drop the 0. If you need to expand to 
> multiple instances, you can have multiple values.
Yes there is an instance 0. We can synchronize all the filter output on
filter instance 0. This is possible only for instance 0. , As example
this could be useful for audio stereo recording.
> 
>> +
>> +Example of a sigma delta adc purpose:
>> +	ads1202: simple_sd_adc at 0 {
>> +		compatible = "sd-modulator";
>> +		#io-channel-cells = <1>;
>> +		status = "okay";
>> +	};
>> +	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>;
> 
> Does reg provide the same thing?
reg represents the instance of the filter ( 4 instances)
Channels ( 8 instances) are linked to the input interface.
we can connect one or several channels to one filter.
> 
>> +			st,adc-channel-names = "in0";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +			io-channels = <&ads1202 0>;
>> +	};
>> +
>> +Example of a pdm microphone purpose:
>> +	dfsdm: dfsdm at 40017000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm";
>> +
>> +		dfsdm_pdm0: dfsdm-pdm at 0 {
>> +			compatible = "st,stm32-dfsdm-pdm";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			interrupts = <110>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,adc-channels = <0>;
>> +			st,adc-channel-names = "pdm1";
>> +			st,adc-channel-types = "SPI_R";
>> +			st,adc-channel-clk-src = "CLKOUT";
>> +		};
>> +	}
>> -- 
>> 1.9.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
  2017-02-27 10:09       ` Arnaud Pouliquen
  (?)
@ 2017-03-05 10:55           ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 10:55 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 27/02/17 10:09, Arnaud Pouliquen wrote:
> Hello Jonathan,
> 
> Late answer... sorry for this.
> 
> I will integrate your remark in V2.
> Please find my answers in-line
> 
> Regards
> Arnaud
> 
> On 02/19/2017 03:46 PM, Jonathan Cameron wrote:
>> On 13/02/17 16:38, 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.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
>> I think this fits together rather nicely.  As before, various comments that
>> are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
>> relevant ones.
>>
>> So as far as I'm concerned: Looking forward to the full version!
>> (as long as Mark and others are happy of course)
>>
>> I definitely want to ultimately see buffered and dma support on the
>> ADC driver side of things as well but that can come later.
> Ok, I suppose that DMA in cyclic mode should also be required for some
> other IIO driver...
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/Kconfig            |  13 +
>>>  drivers/iio/adc/Makefile           |   1 +
>>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>>>  5 files changed, 911 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 d4366ac..ab917b6 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -452,6 +452,19 @@ config STM32_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-adc.
>>>  
>>> +config STM32_DFSDM_ADC
>>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select REGMAP
>>> +	select REGMAP_MMIO
>>> +	select IIO_HW_CONSUMER
>>> +	help
>>> +	  Select this option to enable the  driver for STMicroelectronics
>>> +	  STM32 digital filter for sigma delta converter (ADC).
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>>>  obj-$(CONFIG_STX104) += stx104.o
>>>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>>>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>>> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>>> @@ -0,0 +1,483 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM ADC driver
>>> + *
>>> + * Copyright (C) 2016, 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/io.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include <sound/stm32-adfsdm.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +enum stm32_dfsdm_mode {
>>> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
>>> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
>>> +};
>>> +
>>> +struct stm32_dfsdm_adc {
>>> +	struct stm32_dfsdm *common;
>>> +
>>> +	unsigned int fl_id;
>>> +	unsigned int oversamp;
>>> +	unsigned int clk_freq;
>>> +
>>> +	enum stm32_dfsdm_mode mode;
>>> +	struct platform_device *audio_pdev;
>>> +
>>> +	void (*overrun_cb)(void *context);
>>> +	void *cb_context;
>>> +
>>> +	/* Hardware consumer structure for Front End iio */
>> IIO
>>> +	struct iio_hw_consumer *hwc;
>>> +};
>>> +
>>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
>>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
>>> +
>>> +struct stm32_dfsdm_adc_devdata {
>>> +	enum stm32_dfsdm_mode mode;
>>> +	const struct iio_info *info;
>>> +};
>>> +
>>> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
>>> +				unsigned int oversamp)
>>> +{
>>> +	/*
>>> +	 * TODO
>>> +	 * This function tries to compute filter oversampling and integrator
>>> +	 * oversampling, base on oversampling ratio requested by user.
>>> +	 */
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>>> +				   const struct iio_chan_spec *chan, int *res)
>>> +{
>>> +	/* TODO: Perform conversion instead of sending fake value */
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> :( Definitely an RFC
>>> +
>>> +	*res = chan->channel + 0xFFFF00;
>>> +	return 0;
>>> +}
>>> +
>>> +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);
>>> +	int ret;
>>> +
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
>>> +		if (!ret)
>>> +			adc->oversamp = val;
>> If no reason to carry on,(i.e. nothing to unwind) return directly from within
>> the switch statement.
>>> +		break;
>>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>>> +		if (adc->mode == DFSDM_AUDIO)
>>> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
>>> +		else
>>> +			ret = -EINVAL;
>>> +		break;
>>> +
>>> +	default:
>>> +		ret = -EINVAL;
>>> +	}
>>> +
>>> +	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;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_RAW:
>>> +		if (adc->hwc) {
>>> +			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;
>>> +			}
>> Not an error if hwc not available?
> ASoC framework requests to handle a codec driver. So in case of PDM
> usecase, no IIO SD modulator device is used, instead an ASoC generic
> dmic-codec device is probed. In other words the link between the DFSDM
> and the external SD-modulator has to be done in ASOC for PDMs and in
> IIO for ADCs.
Fair enough.  Please add a comment in the code to that effect so others
in future know what is going on (and for when I inevitably forget!)
> 
>>> +		}
>>> +		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;
>>> +		}
>>> +
>>> +		if (adc->hwc)
>>> +			iio_hw_consumer_disable(adc->hwc);
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		*val = adc->oversamp;
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>>> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
>>> +	.read_raw = stm32_dfsdm_read_raw,
>>> +	.write_raw = stm32_dfsdm_write_raw,
>>> +	.driver_module = THIS_MODULE,
>>> +};
>> Hohum. These two are the same, why two copies?  Mind you for the audio
>> you can't write or read anything so you could drop the callbacks.
> yes to rework.
>>> +
>>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
>>> +	.mode = DFSDM_ADC,
>>> +	.info = &stm32_dfsdm_info_adc,
>>> +};
>>> +
>>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
>>> +	.mode = DFSDM_AUDIO,
>>> +	.info = &stm32_dfsdm_info_audio,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	/* TODO */
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
>>> +				   unsigned int freq)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s:\n", __func__);
>>> +
>>> +	adc->clk_freq = freq;
>>> +};
>>> +
>>> +	/* Set expected audio sampling rate */
>>> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
>>> +				   struct stm32_dfsdm_hw_param *params)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
>>> +
>>> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
>>> +};
>>> +
>>> +	/* Called when ASoC starts an audio stream setup. */
>>> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +	/* Shuts down the audio stream. */
>> Odd indenting.
>>> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +};
>>> +
>>> +	/*
>>> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
>>> +	 * transfers.
>> physical - please run a spell checker over the comments.
>>> +	 */
>>> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +
>>> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
>>> +};
>>> +
>>> +/* Register callback to treat underrun and overrun issues */
>>> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
>>> +					 void (*overrun_cb)(void *context),
>>> +					 void *context)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +	adc->overrun_cb = overrun_cb;
>>> +	adc->cb_context = context;
>>> +};
>>> +
>>> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
>>> +	.set_sysclk = stm32_dfsdm_set_sysclk,
>>> +	.set_hwparam = stm32_dfsdm_set_hwparam,
>>> +	.audio_startup = stm32_dfsdm_audio_startup,
>>> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
>>> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
>>> +	.get_dma_source = stm32_dfsdm_get_dma_source
>>> +};
>> Hmm. I'm wondering if it might make sense to farm the audio stuff off
>> to a separate file.  Potentially we'll have systems that are built with
>> no audio support at all, so would be odd to have this stuff still provided.
> Ok, this make sense if the API become generic.
Probably worth doing even if we don't get more generic. Which is not to say
I'm not in favour of more generic!
>>
>> What do you think?  Also provides an obvious clean bit to be Mark's problem
>> (even if it's in the IIO directory ;)
> I can't answer instead of Mark,( and perhaps i misunderstood...) but
> regarding HDMI story (drivers shared between ASoC and DRM), not sure
> that Mark accepts to have an ASoC drivers in IIO directory. So need a
> part in ASoC that is customer of IIO driver...
Sure.  That makes sense. Not sure exactly where the divide occurs.

If we end up with some non trivial audio specific code left behind in
the IIO driver (to facilitate the bridge) then I'd still like that
to be in a separate file within IIO with build magic to drop it if
audio support isn't configured.
> 
>>> +
>>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>>> +					 struct iio_chan_spec *chan,
>>> +					 int chan_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
>>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> +					 "st,adc-channels", chan_idx,
>>> +					 &ch->channel);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			" error parsing 'st,adc-channels' for idx %d\n",
>>> +			chan_idx);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-names", chan_idx,
>>> +					    &ch->datasheet_name);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			" error parsing 'st,adc-channel-names' for idx %d\n",
>>> +			chan_idx);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = chan_idx;
>>> +	if (adc->mode == DFSDM_ADC) {
>>> +		/*
>>> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>>> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
>>> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
>>> +		 */
>>> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
>>> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>> Very nice.  I was just thinking you should do this before I got here.
>> Channels with no properties but still with an existence from the point of
>> view of consumers using the buffered interface.  Potentially you 'could'
>> provide some of the info as read only but why bother...
>>> +	}
>>> +
>>> +	ch->scan_type.sign = 'u';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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->common->num_chs) {
>>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>>> +		return num_ch < 0 ? num_ch : -EINVAL;
>>> +	}
>>> +
>>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>>> +				GFP_KERNEL);
>>> +	if (!channels)
>>> +		return -ENOMEM;
>>> +
>>> +	if (adc->mode == DFSDM_ADC) {
>>> +		/*
>>> +		 * Bind to sd modulator iio device for ADC only.
>>> +		 * For Audio the PDM microphone will be handled by ASoC
>>> +		 */
>>> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>>> +		if (IS_ERR(adc->hwc)) {
>>> +			dev_err(&indio_dev->dev, "no backend found\n");
>> Deferred probing a possibility? I'm not quite sure and you know what is going
>> on here better than me ;)
> Reading your comment i have a doubt... i will cross check
> Idea here is that there is a customer-provider relationchip with the
> sd-modulator driver. So need that sd-modulator driver is probed first.
Should be fine, I hadn't thought that through.
> 
>>> +			return PTR_ERR(adc->hwc);
>>> +		}
>>> +	}
>>> +
>>> +	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:
>>> +	if (adc->hwc)
>>> +		iio_hw_consumer_free(adc->hwc);
>>> +	return ret;
>>> +}
>>> +
> [...]
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

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

* Re: [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
@ 2017-03-05 10:55           ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 10:55 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 27/02/17 10:09, Arnaud Pouliquen wrote:
> Hello Jonathan,
> 
> Late answer... sorry for this.
> 
> I will integrate your remark in V2.
> Please find my answers in-line
> 
> Regards
> Arnaud
> 
> On 02/19/2017 03:46 PM, Jonathan Cameron wrote:
>> On 13/02/17 16:38, 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.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> I think this fits together rather nicely.  As before, various comments that
>> are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
>> relevant ones.
>>
>> So as far as I'm concerned: Looking forward to the full version!
>> (as long as Mark and others are happy of course)
>>
>> I definitely want to ultimately see buffered and dma support on the
>> ADC driver side of things as well but that can come later.
> Ok, I suppose that DMA in cyclic mode should also be required for some
> other IIO driver...
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/Kconfig            |  13 +
>>>  drivers/iio/adc/Makefile           |   1 +
>>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>>>  5 files changed, 911 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 d4366ac..ab917b6 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -452,6 +452,19 @@ config STM32_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-adc.
>>>  
>>> +config STM32_DFSDM_ADC
>>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select REGMAP
>>> +	select REGMAP_MMIO
>>> +	select IIO_HW_CONSUMER
>>> +	help
>>> +	  Select this option to enable the  driver for STMicroelectronics
>>> +	  STM32 digital filter for sigma delta converter (ADC).
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>>>  obj-$(CONFIG_STX104) += stx104.o
>>>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>>>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>>> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>>> @@ -0,0 +1,483 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/io.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include <sound/stm32-adfsdm.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +enum stm32_dfsdm_mode {
>>> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
>>> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
>>> +};
>>> +
>>> +struct stm32_dfsdm_adc {
>>> +	struct stm32_dfsdm *common;
>>> +
>>> +	unsigned int fl_id;
>>> +	unsigned int oversamp;
>>> +	unsigned int clk_freq;
>>> +
>>> +	enum stm32_dfsdm_mode mode;
>>> +	struct platform_device *audio_pdev;
>>> +
>>> +	void (*overrun_cb)(void *context);
>>> +	void *cb_context;
>>> +
>>> +	/* Hardware consumer structure for Front End iio */
>> IIO
>>> +	struct iio_hw_consumer *hwc;
>>> +};
>>> +
>>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
>>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
>>> +
>>> +struct stm32_dfsdm_adc_devdata {
>>> +	enum stm32_dfsdm_mode mode;
>>> +	const struct iio_info *info;
>>> +};
>>> +
>>> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
>>> +				unsigned int oversamp)
>>> +{
>>> +	/*
>>> +	 * TODO
>>> +	 * This function tries to compute filter oversampling and integrator
>>> +	 * oversampling, base on oversampling ratio requested by user.
>>> +	 */
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>>> +				   const struct iio_chan_spec *chan, int *res)
>>> +{
>>> +	/* TODO: Perform conversion instead of sending fake value */
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> :( Definitely an RFC
>>> +
>>> +	*res = chan->channel + 0xFFFF00;
>>> +	return 0;
>>> +}
>>> +
>>> +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);
>>> +	int ret;
>>> +
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
>>> +		if (!ret)
>>> +			adc->oversamp = val;
>> If no reason to carry on,(i.e. nothing to unwind) return directly from within
>> the switch statement.
>>> +		break;
>>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>>> +		if (adc->mode == DFSDM_AUDIO)
>>> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
>>> +		else
>>> +			ret = -EINVAL;
>>> +		break;
>>> +
>>> +	default:
>>> +		ret = -EINVAL;
>>> +	}
>>> +
>>> +	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;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_RAW:
>>> +		if (adc->hwc) {
>>> +			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;
>>> +			}
>> Not an error if hwc not available?
> ASoC framework requests to handle a codec driver. So in case of PDM
> usecase, no IIO SD modulator device is used, instead an ASoC generic
> dmic-codec device is probed. In other words the link between the DFSDM
> and the external SD-modulator has to be done in ASOC for PDMs and in
> IIO for ADCs.
Fair enough.  Please add a comment in the code to that effect so others
in future know what is going on (and for when I inevitably forget!)
> 
>>> +		}
>>> +		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;
>>> +		}
>>> +
>>> +		if (adc->hwc)
>>> +			iio_hw_consumer_disable(adc->hwc);
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		*val = adc->oversamp;
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>>> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
>>> +	.read_raw = stm32_dfsdm_read_raw,
>>> +	.write_raw = stm32_dfsdm_write_raw,
>>> +	.driver_module = THIS_MODULE,
>>> +};
>> Hohum. These two are the same, why two copies?  Mind you for the audio
>> you can't write or read anything so you could drop the callbacks.
> yes to rework.
>>> +
>>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
>>> +	.mode = DFSDM_ADC,
>>> +	.info = &stm32_dfsdm_info_adc,
>>> +};
>>> +
>>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
>>> +	.mode = DFSDM_AUDIO,
>>> +	.info = &stm32_dfsdm_info_audio,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	/* TODO */
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
>>> +				   unsigned int freq)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s:\n", __func__);
>>> +
>>> +	adc->clk_freq = freq;
>>> +};
>>> +
>>> +	/* Set expected audio sampling rate */
>>> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
>>> +				   struct stm32_dfsdm_hw_param *params)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
>>> +
>>> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
>>> +};
>>> +
>>> +	/* Called when ASoC starts an audio stream setup. */
>>> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +	/* Shuts down the audio stream. */
>> Odd indenting.
>>> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +};
>>> +
>>> +	/*
>>> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
>>> +	 * transfers.
>> physical - please run a spell checker over the comments.
>>> +	 */
>>> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +
>>> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
>>> +};
>>> +
>>> +/* Register callback to treat underrun and overrun issues */
>>> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
>>> +					 void (*overrun_cb)(void *context),
>>> +					 void *context)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +	adc->overrun_cb = overrun_cb;
>>> +	adc->cb_context = context;
>>> +};
>>> +
>>> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
>>> +	.set_sysclk = stm32_dfsdm_set_sysclk,
>>> +	.set_hwparam = stm32_dfsdm_set_hwparam,
>>> +	.audio_startup = stm32_dfsdm_audio_startup,
>>> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
>>> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
>>> +	.get_dma_source = stm32_dfsdm_get_dma_source
>>> +};
>> Hmm. I'm wondering if it might make sense to farm the audio stuff off
>> to a separate file.  Potentially we'll have systems that are built with
>> no audio support at all, so would be odd to have this stuff still provided.
> Ok, this make sense if the API become generic.
Probably worth doing even if we don't get more generic. Which is not to say
I'm not in favour of more generic!
>>
>> What do you think?  Also provides an obvious clean bit to be Mark's problem
>> (even if it's in the IIO directory ;)
> I can't answer instead of Mark,( and perhaps i misunderstood...) but
> regarding HDMI story (drivers shared between ASoC and DRM), not sure
> that Mark accepts to have an ASoC drivers in IIO directory. So need a
> part in ASoC that is customer of IIO driver...
Sure.  That makes sense. Not sure exactly where the divide occurs.

If we end up with some non trivial audio specific code left behind in
the IIO driver (to facilitate the bridge) then I'd still like that
to be in a separate file within IIO with build magic to drop it if
audio support isn't configured.
> 
>>> +
>>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>>> +					 struct iio_chan_spec *chan,
>>> +					 int chan_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
>>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> +					 "st,adc-channels", chan_idx,
>>> +					 &ch->channel);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			" error parsing 'st,adc-channels' for idx %d\n",
>>> +			chan_idx);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-names", chan_idx,
>>> +					    &ch->datasheet_name);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			" error parsing 'st,adc-channel-names' for idx %d\n",
>>> +			chan_idx);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = chan_idx;
>>> +	if (adc->mode == DFSDM_ADC) {
>>> +		/*
>>> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>>> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
>>> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
>>> +		 */
>>> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
>>> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>> Very nice.  I was just thinking you should do this before I got here.
>> Channels with no properties but still with an existence from the point of
>> view of consumers using the buffered interface.  Potentially you 'could'
>> provide some of the info as read only but why bother...
>>> +	}
>>> +
>>> +	ch->scan_type.sign = 'u';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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->common->num_chs) {
>>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>>> +		return num_ch < 0 ? num_ch : -EINVAL;
>>> +	}
>>> +
>>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>>> +				GFP_KERNEL);
>>> +	if (!channels)
>>> +		return -ENOMEM;
>>> +
>>> +	if (adc->mode == DFSDM_ADC) {
>>> +		/*
>>> +		 * Bind to sd modulator iio device for ADC only.
>>> +		 * For Audio the PDM microphone will be handled by ASoC
>>> +		 */
>>> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>>> +		if (IS_ERR(adc->hwc)) {
>>> +			dev_err(&indio_dev->dev, "no backend found\n");
>> Deferred probing a possibility? I'm not quite sure and you know what is going
>> on here better than me ;)
> Reading your comment i have a doubt... i will cross check
> Idea here is that there is a customer-provider relationchip with the
> sd-modulator driver. So need that sd-modulator driver is probed first.
Should be fine, I hadn't thought that through.
> 
>>> +			return PTR_ERR(adc->hwc);
>>> +		}
>>> +	}
>>> +
>>> +	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:
>>> +	if (adc->hwc)
>>> +		iio_hw_consumer_free(adc->hwc);
>>> +	return ret;
>>> +}
>>> +
> [...]
> --
> 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] 107+ messages in thread

* [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
@ 2017-03-05 10:55           ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 10:55 UTC (permalink / raw)
  To: linux-arm-kernel

On 27/02/17 10:09, Arnaud Pouliquen wrote:
> Hello Jonathan,
> 
> Late answer... sorry for this.
> 
> I will integrate your remark in V2.
> Please find my answers in-line
> 
> Regards
> Arnaud
> 
> On 02/19/2017 03:46 PM, Jonathan Cameron wrote:
>> On 13/02/17 16:38, 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.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> I think this fits together rather nicely.  As before, various comments that
>> are irrelevant to an RFC (I just couldn't stop myself ;) and a few more
>> relevant ones.
>>
>> So as far as I'm concerned: Looking forward to the full version!
>> (as long as Mark and others are happy of course)
>>
>> I definitely want to ultimately see buffered and dma support on the
>> ADC driver side of things as well but that can come later.
> Ok, I suppose that DMA in cyclic mode should also be required for some
> other IIO driver...
>>
>> Jonathan
>>> ---
>>>  drivers/iio/adc/Kconfig            |  13 +
>>>  drivers/iio/adc/Makefile           |   1 +
>>>  drivers/iio/adc/stm32-dfsdm-adc.c  | 483 +++++++++++++++++++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm-core.c | 273 +++++++++++++++++++++
>>>  drivers/iio/adc/stm32-dfsdm.h      | 141 +++++++++++
>>>  5 files changed, 911 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 d4366ac..ab917b6 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -452,6 +452,19 @@ config STM32_ADC
>>>  	  This driver can also be built as a module.  If so, the module
>>>  	  will be called stm32-adc.
>>>  
>>> +config STM32_DFSDM_ADC
>>> +	tristate "STMicroelectronics STM32 dfsdm adc"
>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> +	select REGMAP
>>> +	select REGMAP_MMIO
>>> +	select IIO_HW_CONSUMER
>>> +	help
>>> +	  Select this option to enable the  driver for STMicroelectronics
>>> +	  STM32 digital filter for sigma delta converter (ADC).
>>> +
>>> +	  This driver can also be built as a module.  If so, the module
>>> +	  will be called stm32-adc-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 bd67144..5bcad23 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>>>  obj-$(CONFIG_STX104) += stx104.o
>>>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>>>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>>> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o 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..8f9c3263
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>>> @@ -0,0 +1,483 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/io.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include <sound/stm32-adfsdm.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +enum stm32_dfsdm_mode {
>>> +	DFSDM_ADC, /* ADC mode, access through IIO ABI */
>>> +	DFSDM_AUDIO /* Audio mode, access through ASoC ABI */
>>> +};
>>> +
>>> +struct stm32_dfsdm_adc {
>>> +	struct stm32_dfsdm *common;
>>> +
>>> +	unsigned int fl_id;
>>> +	unsigned int oversamp;
>>> +	unsigned int clk_freq;
>>> +
>>> +	enum stm32_dfsdm_mode mode;
>>> +	struct platform_device *audio_pdev;
>>> +
>>> +	void (*overrun_cb)(void *context);
>>> +	void *cb_context;
>>> +
>>> +	/* Hardware consumer structure for Front End iio */
>> IIO
>>> +	struct iio_hw_consumer *hwc;
>>> +};
>>> +
>>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_adc = DFSDM_ADC;
>>> +static const enum stm32_dfsdm_mode stm32_dfsdm_data_audio = DFSDM_AUDIO;
>>> +
>>> +struct stm32_dfsdm_adc_devdata {
>>> +	enum stm32_dfsdm_mode mode;
>>> +	const struct iio_info *info;
>>> +};
>>> +
>>> +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_adc *adc, bool fast,
>>> +				unsigned int oversamp)
>>> +{
>>> +	/*
>>> +	 * TODO
>>> +	 * This function tries to compute filter oversampling and integrator
>>> +	 * oversampling, base on oversampling ratio requested by user.
>>> +	 */
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>>> +				   const struct iio_chan_spec *chan, int *res)
>>> +{
>>> +	/* TODO: Perform conversion instead of sending fake value */
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>> :( Definitely an RFC
>>> +
>>> +	*res = chan->channel + 0xFFFF00;
>>> +	return 0;
>>> +}
>>> +
>>> +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);
>>> +	int ret;
>>> +
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		ret = stm32_dfsdm_set_osrs(adc, 0, val);
>>> +		if (!ret)
>>> +			adc->oversamp = val;
>> If no reason to carry on,(i.e. nothing to unwind) return directly from within
>> the switch statement.
>>> +		break;
>>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>>> +		if (adc->mode == DFSDM_AUDIO)
>>> +			ret = stm32_dfsdm_set_osrs(adc, 0, val);
>>> +		else
>>> +			ret = -EINVAL;
>>> +		break;
>>> +
>>> +	default:
>>> +		ret = -EINVAL;
>>> +	}
>>> +
>>> +	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;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s\n", __func__);
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_RAW:
>>> +		if (adc->hwc) {
>>> +			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;
>>> +			}
>> Not an error if hwc not available?
> ASoC framework requests to handle a codec driver. So in case of PDM
> usecase, no IIO SD modulator device is used, instead an ASoC generic
> dmic-codec device is probed. In other words the link between the DFSDM
> and the external SD-modulator has to be done in ASOC for PDMs and in
> IIO for ADCs.
Fair enough.  Please add a comment in the code to that effect so others
in future know what is going on (and for when I inevitably forget!)
> 
>>> +		}
>>> +		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;
>>> +		}
>>> +
>>> +		if (adc->hwc)
>>> +			iio_hw_consumer_disable(adc->hwc);
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> +		*val = adc->oversamp;
>>> +
>>> +		return IIO_VAL_INT;
>>> +
>>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>>> +		*val = DIV_ROUND_CLOSEST(adc->clk_freq, 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 const struct iio_info stm32_dfsdm_info_audio = {
>>> +	.read_raw = stm32_dfsdm_read_raw,
>>> +	.write_raw = stm32_dfsdm_write_raw,
>>> +	.driver_module = THIS_MODULE,
>>> +};
>> Hohum. These two are the same, why two copies?  Mind you for the audio
>> you can't write or read anything so you could drop the callbacks.
> yes to rework.
>>> +
>>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_adc = {
>>> +	.mode = DFSDM_ADC,
>>> +	.info = &stm32_dfsdm_info_adc,
>>> +};
>>> +
>>> +const struct stm32_dfsdm_adc_devdata stm32_dfsdm_devdata_audio = {
>>> +	.mode = DFSDM_AUDIO,
>>> +	.info = &stm32_dfsdm_info_audio,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> +	/* TODO */
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void stm32_dfsdm_set_sysclk(struct stm32_dfsdm_adc *adc,
>>> +				   unsigned int freq)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s:\n", __func__);
>>> +
>>> +	adc->clk_freq = freq;
>>> +};
>>> +
>>> +	/* Set expected audio sampling rate */
>>> +static int stm32_dfsdm_set_hwparam(struct stm32_dfsdm_adc *adc,
>>> +				   struct stm32_dfsdm_hw_param *params)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s for rate %d\n", __func__, params->rate);
>>> +
>>> +	return stm32_dfsdm_set_osrs(adc, 0, params->rate);
>>> +};
>>> +
>>> +	/* Called when ASoC starts an audio stream setup. */
>>> +static int stm32_dfsdm_audio_startup(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +
>>> +	return 0;
>>> +};
>>> +
>>> +	/* Shuts down the audio stream. */
>> Odd indenting.
>>> +static void stm32_dfsdm_audio_shutdown(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +};
>>> +
>>> +	/*
>>> +	 * Provides DMA source physicla addr to allow ALsa to handle DMA
>>> +	 * transfers.
>> physical - please run a spell checker over the comments.
>>> +	 */
>>> +static dma_addr_t stm32_dfsdm_get_dma_source(struct stm32_dfsdm_adc *adc)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +
>>> +	return (dma_addr_t)(adc->common->phys_base + DFSDM_RDATAR(adc->fl_id));
>>> +};
>>> +
>>> +/* Register callback to treat underrun and overrun issues */
>>> +static void stm32_dfsdm_register_xrun_cb(struct stm32_dfsdm_adc *adc,
>>> +					 void (*overrun_cb)(void *context),
>>> +					 void *context)
>>> +{
>>> +	struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> +	dev_dbg(&iio->dev, "%s\n", __func__);
>>> +	adc->overrun_cb = overrun_cb;
>>> +	adc->cb_context = context;
>>> +};
>>> +
>>> +const struct stm32_adfsdm_codec_ops stm32_dfsdm_audio_ops = {
>>> +	.set_sysclk = stm32_dfsdm_set_sysclk,
>>> +	.set_hwparam = stm32_dfsdm_set_hwparam,
>>> +	.audio_startup = stm32_dfsdm_audio_startup,
>>> +	.audio_shutdown = stm32_dfsdm_audio_shutdown,
>>> +	.register_xrun_cb = stm32_dfsdm_register_xrun_cb,
>>> +	.get_dma_source = stm32_dfsdm_get_dma_source
>>> +};
>> Hmm. I'm wondering if it might make sense to farm the audio stuff off
>> to a separate file.  Potentially we'll have systems that are built with
>> no audio support at all, so would be odd to have this stuff still provided.
> Ok, this make sense if the API become generic.
Probably worth doing even if we don't get more generic. Which is not to say
I'm not in favour of more generic!
>>
>> What do you think?  Also provides an obvious clean bit to be Mark's problem
>> (even if it's in the IIO directory ;)
> I can't answer instead of Mark,( and perhaps i misunderstood...) but
> regarding HDMI story (drivers shared between ASoC and DRM), not sure
> that Mark accepts to have an ASoC drivers in IIO directory. So need a
> part in ASoC that is customer of IIO driver...
Sure.  That makes sense. Not sure exactly where the divide occurs.

If we end up with some non trivial audio specific code left behind in
the IIO driver (to facilitate the bridge) then I'd still like that
to be in a separate file within IIO with build magic to drop it if
audio support isn't configured.
> 
>>> +
>>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>>> +					 struct iio_chan_spec *chan,
>>> +					 int chan_idx)
>>> +{
>>> +	struct iio_chan_spec *ch = &chan[chan_idx];
>>> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
>>> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> +					 "st,adc-channels", chan_idx,
>>> +					 &ch->channel);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			" error parsing 'st,adc-channels' for idx %d\n",
>>> +			chan_idx);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> +					    "st,adc-channel-names", chan_idx,
>>> +					    &ch->datasheet_name);
>>> +	if (ret < 0) {
>>> +		dev_err(&indio_dev->dev,
>>> +			" error parsing 'st,adc-channel-names' for idx %d\n",
>>> +			chan_idx);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ch->type = IIO_VOLTAGE;
>>> +	ch->indexed = 1;
>>> +	ch->scan_index = chan_idx;
>>> +	if (adc->mode == DFSDM_ADC) {
>>> +		/*
>>> +		 * IIO_CHAN_INFO_RAW: used to compute regular conversion
>>> +		 * IIO_CHAN_INFO_SAMP_FREQ: used to indicate sampling frequency
>>> +		 * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used set oversampling
>>> +		 */
>>> +		ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> +					 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
>>> +					 BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>> Very nice.  I was just thinking you should do this before I got here.
>> Channels with no properties but still with an existence from the point of
>> view of consumers using the buffered interface.  Potentially you 'could'
>> provide some of the info as read only but why bother...
>>> +	}
>>> +
>>> +	ch->scan_type.sign = 'u';
>>> +	ch->scan_type.realbits = 24;
>>> +	ch->scan_type.storagebits = 32;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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->common->num_chs) {
>>> +		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>>> +		return num_ch < 0 ? num_ch : -EINVAL;
>>> +	}
>>> +
>>> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>>> +				GFP_KERNEL);
>>> +	if (!channels)
>>> +		return -ENOMEM;
>>> +
>>> +	if (adc->mode == DFSDM_ADC) {
>>> +		/*
>>> +		 * Bind to sd modulator iio device for ADC only.
>>> +		 * For Audio the PDM microphone will be handled by ASoC
>>> +		 */
>>> +		adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>>> +		if (IS_ERR(adc->hwc)) {
>>> +			dev_err(&indio_dev->dev, "no backend found\n");
>> Deferred probing a possibility? I'm not quite sure and you know what is going
>> on here better than me ;)
> Reading your comment i have a doubt... i will cross check
> Idea here is that there is a customer-provider relationchip with the
> sd-modulator driver. So need that sd-modulator driver is probed first.
Should be fine, I hadn't thought that through.
> 
>>> +			return PTR_ERR(adc->hwc);
>>> +		}
>>> +	}
>>> +
>>> +	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:
>>> +	if (adc->hwc)
>>> +		iio_hw_consumer_free(adc->hwc);
>>> +	return ret;
>>> +}
>>> +
> [...]
> --
> 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] 107+ messages in thread

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
  2017-02-27 10:31             ` Arnaud Pouliquen
  (?)
@ 2017-03-05 10:55                 ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 10:55 UTC (permalink / raw)
  To: Arnaud Pouliquen, Mark Brown
  Cc: Rob Herring, Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	Maxime Coquelin, Alexandre TORGUE, Olivier MOYSAN

On 27/02/17 10:31, Arnaud Pouliquen wrote:
> 
> 
> On 02/19/2017 03:56 PM, Jonathan Cameron wrote:
>> On 14/02/17 17:45, Mark Brown wrote:
>>> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
>>>
>>> This looks basically fine as a system specific driver but as some of the
>>> comments in here say there's bits of it could perhaps be genericised but
>>> I'm not sure we need to do that right now.  I'm not sure the abstraction
>>> is exactly comfortable but having another bit of hardware doing a bridge
>>> to IIO might be the best way to figure out something better.
>> Agreed.  To an extent we are fishing around in the dark at the moment.
>> Lets wait until we have a few more cases of similar hardware before trying
>> too much generalization.  This is acting as a good exploration of what
>> is needed.
> 
> So for now i keep like this the API between ASOC and IIO, means not
> generic API, and DMA handled in ASOC?
> 
> Then when some other hardwares come with same kind of requirements, we
> will re-discuss a more generic way to do it...
Exactly what I was thinking.
> 
>>
>> Ideally Lars might upstream some of the other bits he has in his tree
>> to do with DSP processing on ADC streams and that might provide us with
>> more clues on generality (at least at the lowest layers)
>> (Apparently he's busy - though he always makes that excuse :)
>> Joking aside, the exploration Analog and in particular Lars does around
>> pushing the limits of how things interact is always useful!)
>>
>>>
>>>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>>>> +	.period_bytes_max = 4 * PAGE_SIZE,
>>>> +	.buffer_bytes_max = 16 * PAGE_SIZE
>>>
>>> What's the physical minimum period limit?  The comment makes this sound
>>> like it's just made up.
>>>
>>>> +	unsigned int shift = 24 -priv->max_scaling;
>>>> +	
>>>
>>> Missing space after -.
>>>
>>>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>>>> +	return 0;
>>>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>>>> +					  SNDRV_PCM_HW_PARAM_RATE,
>>>> +					  &priv->rates_const);
>>>
>>> Looks like debug changes got left in?
>>>
>>>> +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);
>>>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>>>> +
>>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>>> +	if (dir == SND_SOC_CLOCK_IN) {
>>>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>>>> +		priv->dmic_clk = freq;
>>>> +	}
>>>> +
>>>> +	/* Determine supported rate which depends on SPI/manchester clock */
>>>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
>>>
>>> Since the DAI is unidirectional it doesn't matter but obviously if it
>>> weren't then the fact that getting the supported rates involves setting
>>> the hwparams means this could become disruptive.  If we're going to
>>> genericise this to be a more general IIO/ASoC bridge that could matter.
>>>
>>>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>>>> +{
>>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>>> +
>>>> +	return 0;
>>>> +}
>>>
>>> Remove empty functions, though in this case I think you want to add
>>> something to disconnect the XRUN callback just in order to be sure it
>>> can't be mistakenly called.
>>>
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-03-05 10:55                 ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 10:55 UTC (permalink / raw)
  To: Arnaud Pouliquen, Mark Brown
  Cc: Rob Herring, Mark Rutland, 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

On 27/02/17 10:31, Arnaud Pouliquen wrote:
> 
> 
> On 02/19/2017 03:56 PM, Jonathan Cameron wrote:
>> On 14/02/17 17:45, Mark Brown wrote:
>>> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
>>>
>>> This looks basically fine as a system specific driver but as some of the
>>> comments in here say there's bits of it could perhaps be genericised but
>>> I'm not sure we need to do that right now.  I'm not sure the abstraction
>>> is exactly comfortable but having another bit of hardware doing a bridge
>>> to IIO might be the best way to figure out something better.
>> Agreed.  To an extent we are fishing around in the dark at the moment.
>> Lets wait until we have a few more cases of similar hardware before trying
>> too much generalization.  This is acting as a good exploration of what
>> is needed.
> 
> So for now i keep like this the API between ASOC and IIO, means not
> generic API, and DMA handled in ASOC?
> 
> Then when some other hardwares come with same kind of requirements, we
> will re-discuss a more generic way to do it...
Exactly what I was thinking.
> 
>>
>> Ideally Lars might upstream some of the other bits he has in his tree
>> to do with DSP processing on ADC streams and that might provide us with
>> more clues on generality (at least at the lowest layers)
>> (Apparently he's busy - though he always makes that excuse :)
>> Joking aside, the exploration Analog and in particular Lars does around
>> pushing the limits of how things interact is always useful!)
>>
>>>
>>>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>>>> +	.period_bytes_max = 4 * PAGE_SIZE,
>>>> +	.buffer_bytes_max = 16 * PAGE_SIZE
>>>
>>> What's the physical minimum period limit?  The comment makes this sound
>>> like it's just made up.
>>>
>>>> +	unsigned int shift = 24 -priv->max_scaling;
>>>> +	
>>>
>>> Missing space after -.
>>>
>>>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>>>> +	return 0;
>>>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>>>> +					  SNDRV_PCM_HW_PARAM_RATE,
>>>> +					  &priv->rates_const);
>>>
>>> Looks like debug changes got left in?
>>>
>>>> +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);
>>>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>>>> +
>>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>>> +	if (dir == SND_SOC_CLOCK_IN) {
>>>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>>>> +		priv->dmic_clk = freq;
>>>> +	}
>>>> +
>>>> +	/* Determine supported rate which depends on SPI/manchester clock */
>>>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
>>>
>>> Since the DAI is unidirectional it doesn't matter but obviously if it
>>> weren't then the fact that getting the supported rates involves setting
>>> the hwparams means this could become disruptive.  If we're going to
>>> genericise this to be a more general IIO/ASoC bridge that could matter.
>>>
>>>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>>>> +{
>>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>>> +
>>>> +	return 0;
>>>> +}
>>>
>>> Remove empty functions, though in this case I think you want to add
>>> something to disconnect the XRUN callback just in order to be sure it
>>> can't be mistakenly called.
>>>
>>
> --
> 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] 107+ messages in thread

* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
@ 2017-03-05 10:55                 ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 10:55 UTC (permalink / raw)
  To: linux-arm-kernel

On 27/02/17 10:31, Arnaud Pouliquen wrote:
> 
> 
> On 02/19/2017 03:56 PM, Jonathan Cameron wrote:
>> On 14/02/17 17:45, Mark Brown wrote:
>>> On Mon, Feb 13, 2017 at 05:38:27PM +0100, Arnaud Pouliquen wrote:
>>>
>>> This looks basically fine as a system specific driver but as some of the
>>> comments in here say there's bits of it could perhaps be genericised but
>>> I'm not sure we need to do that right now.  I'm not sure the abstraction
>>> is exactly comfortable but having another bit of hardware doing a bridge
>>> to IIO might be the best way to figure out something better.
>> Agreed.  To an extent we are fishing around in the dark at the moment.
>> Lets wait until we have a few more cases of similar hardware before trying
>> too much generalization.  This is acting as a good exploration of what
>> is needed.
> 
> So for now i keep like this the API between ASOC and IIO, means not
> generic API, and DMA handled in ASOC?
> 
> Then when some other hardwares come with same kind of requirements, we
> will re-discuss a more generic way to do it...
Exactly what I was thinking.
> 
>>
>> Ideally Lars might upstream some of the other bits he has in his tree
>> to do with DSP processing on ADC streams and that might provide us with
>> more clues on generality (at least at the lowest layers)
>> (Apparently he's busy - though he always makes that excuse :)
>> Joking aside, the exploration Analog and in particular Lars does around
>> pushing the limits of how things interact is always useful!)
>>
>>>
>>>> +	.period_bytes_min = 40, /* 8 khz 5 ms */
>>>> +	.period_bytes_max = 4 * PAGE_SIZE,
>>>> +	.buffer_bytes_max = 16 * PAGE_SIZE
>>>
>>> What's the physical minimum period limit?  The comment makes this sound
>>> like it's just made up.
>>>
>>>> +	unsigned int shift = 24 -priv->max_scaling;
>>>> +	
>>>
>>> Missing space after -.
>>>
>>>> +	dev_dbg(dai->dev, "%s: enter\n", __func__);
>>>> +	return 0;
>>>> +	return snd_pcm_hw_constraint_list(substream->runtime, 0,
>>>> +					  SNDRV_PCM_HW_PARAM_RATE,
>>>> +					  &priv->rates_const);
>>>
>>> Looks like debug changes got left in?
>>>
>>>> +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);
>>>> +	struct stm32_adfsdm_pdata *pdata = priv->pdata;
>>>> +
>>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>>> +	if (dir == SND_SOC_CLOCK_IN) {
>>>> +		pdata->ops->set_sysclk(pdata->adc, freq);
>>>> +		priv->dmic_clk = freq;
>>>> +	}
>>>> +
>>>> +	/* Determine supported rate which depends on SPI/manchester clock */
>>>> +	return stm32_adfsdm_get_supported_rates(dai, &priv->rates_const.mask);
>>>
>>> Since the DAI is unidirectional it doesn't matter but obviously if it
>>> weren't then the fact that getting the supported rates involves setting
>>> the hwparams means this could become disruptive.  If we're going to
>>> genericise this to be a more general IIO/ASoC bridge that could matter.
>>>
>>>> +static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
>>>> +{
>>>> +	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
>>>> +
>>>> +	return 0;
>>>> +}
>>>
>>> Remove empty functions, though in this case I think you want to add
>>> something to disconnect the XRUN callback just in order to be sure it
>>> can't be mistakenly called.
>>>
>>
> --
> 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] 107+ messages in thread

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
  2017-02-27 10:47       ` Arnaud Pouliquen
  (?)
@ 2017-03-05 11:00           ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 11:00 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 27/02/17 10:47, Arnaud Pouliquen wrote:
> 
> 
> On 02/19/2017 04:00 PM, Jonathan Cameron wrote:
>> On 13/02/17 16:38, Arnaud Pouliquen wrote:
>>> Add bindings that describe Digital Filter for Sigma Delta
>>> Modulators. DFSDM allows to connect sigma delta
>>> modulators and/or PDM microphones.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
>>> ---
>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>>  1 file changed, 125 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..83937cb
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>>> @@ -0,0 +1,125 @@
>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>> +
>>> +
>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>>> +interface external sigma delta modulators to stm32 micro controlers.
>>> +it is mainly targeted for:
>>> +- Sigma delta modulators ( motor control, metering...)
>>> +- PDM microphones ( audio digital microphone)
>>> +
>>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>>> +up to 4 filters.
>>> +
>>> +Each instance of the sub-drivers uses one filter instance.
>>> +
>>> +Contents of a stm32 dfsdmc root node:
>>> +-------------------------------------
>>> +Required properties:
>>> +- compatible: Should be "st,stm32-dfsdm-adc".
>>> +- 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.
>>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>>> +- clock-names: Must be "adc".
>>> +
>>> +Optional properties:
>>> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
>>> +- reg: Specifies the DFSDM filter instance.
>>> +- 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 channels Name.
>>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>>> +			0: FastSinC
>>> +			[1-5]: order 1 to 5.
>>> +			For audio purpose it is recommended to use order 3 to 5.
>>> +
>>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +- io-channels: from common iio binding. use to pipe external sigma delta
>>> +		modulator or internal ADC output to dfsdm channel.
>>> +
>>> +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. Default value is 0.
>>> +			- "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. default value is 1.
>>> +			  - "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.
>>> +		   to used for multi microphone synchronization.
>>> +
>>> +Example of a sigma delta adc purpose:
>>> +	ads1202: simple_sd_adc@0 {
>>> +		compatible = "sd-modulator";
>> This suggests to me that we ought to have a primary compatible of the actual part
>> number.  Down the line I suspect we will want to distinguish between the different
>> ADCs and it's kind of easier if we do it from the start.  May not be obvious
>> later which the 'default sd-modulator' is.
> Not sure to understand your point... you mean use ADC name instead, for
> compatibility as suggested by Rob?
Yes, though you can do it as (and I may have the syntax wrong)
compatible = "ads1202", "sd-modulator" perhaps giving a fallback.
Better than having people 'lie' about the connected part in device trees
because they don't want to push the change upstream to add their part to those
supported.

This is similar to what we do for SoC ADCs where they are in theory compatible
across all supported parts, but in reality there are sometimes errata etc.
Allows people to be as specific as possible in their device trees, without
precluding use in the generic form if the part isn't explicitly supported.

Up to Rob though on whether he's happy with that trick here...

> 
>>> +		#io-channel-cells = <1>;
>>> +		status = "okay";
>>> +	};
>>> +	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 = "in0";
>>> +			st,adc-channel-types = "SPI_R";
>>> +			st,adc-channel-clk-src = "CLKOUT";
>>> +			io-channels = <&ads1202 0>;
>>> +	};
>>> +
>>> +Example of a pdm microphone purpose:
>> Is there nothing about the particular pdm microphone that is
>> worth giving it an explicit representation in the device
>> tree?  I've no idea having never used one!
>>> +	dfsdm: dfsdm@40017000 {
>>> +		compatible = "st,stm32h7-dfsdm";
>>> +		reg = <0x40017000 0x400>;
>>> +		clocks = <&timer_clk>;
>>> +		clock-names = "dfsdm";
>>> +
>>> +		dfsdm_pdm0: dfsdm-pdm@0 {
>>> +			compatible = "st,stm32-dfsdm-pdm";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			interrupts = <110>;
>>> +			dmas = <&dmamux1 102 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,adc-channels = <0>;
>>> +			st,adc-channel-names = "pdm1";
>>> +			st,adc-channel-types = "SPI_R";
>>> +			st,adc-channel-clk-src = "CLKOUT";
>>> +		};
>>> +	}
>>>
>>
> Here is a way to use it in device tree.
> - dmic_0 is an ASOC generic device that handles the PDM microphone
> - dai-link allows to bind the DFSDM with the SD-modulator in ALSA card
> declaration.
> 
> 	dmic0: dmic_0@0 {
> 		compatible = "dmic-codec";
> 		#sound-dai-cells = <0>;
> 
> 		status = "okay";
> 	};
> 
> 	sound_dfsdm_pdm {
> 		compatible = "simple-audio-card";
> 		simple-audio-card,name = "dfsdm_pdm";
> 		status = "okay";
> 
> 		dfsdm0_mic0: simple-audio-card,dai-link@0 {
> 			format = "pdm";
> 			bitclock-master = <&dmic0_codec>;
> 			cpu {
> 				system-clock-frequency= <2480000>;
> 				sound-dai = <&dfsdm_pdm0>;
> 			};
> 			dmic0_codec: codec {
> 				sound-dai = <&dmic0>;
> 			};
> 		};
> 	};
> };
That's fine.  I was just wondering if they ever had an 'smarts' 
that might make it sensible to provide an explicit description like
we are doing for the ADC part being used.

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

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

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

* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-03-05 11:00           ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 11:00 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 27/02/17 10:47, Arnaud Pouliquen wrote:
> 
> 
> On 02/19/2017 04:00 PM, Jonathan Cameron wrote:
>> On 13/02/17 16:38, Arnaud Pouliquen wrote:
>>> Add bindings that describe Digital Filter for Sigma Delta
>>> Modulators. DFSDM allows to connect sigma delta
>>> modulators and/or PDM microphones.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> ---
>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>>  1 file changed, 125 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..83937cb
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>>> @@ -0,0 +1,125 @@
>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>> +
>>> +
>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>>> +interface external sigma delta modulators to stm32 micro controlers.
>>> +it is mainly targeted for:
>>> +- Sigma delta modulators ( motor control, metering...)
>>> +- PDM microphones ( audio digital microphone)
>>> +
>>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>>> +up to 4 filters.
>>> +
>>> +Each instance of the sub-drivers uses one filter instance.
>>> +
>>> +Contents of a stm32 dfsdmc root node:
>>> +-------------------------------------
>>> +Required properties:
>>> +- compatible: Should be "st,stm32-dfsdm-adc".
>>> +- 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.
>>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>>> +- clock-names: Must be "adc".
>>> +
>>> +Optional properties:
>>> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
>>> +- reg: Specifies the DFSDM filter instance.
>>> +- 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 channels Name.
>>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>>> +			0: FastSinC
>>> +			[1-5]: order 1 to 5.
>>> +			For audio purpose it is recommended to use order 3 to 5.
>>> +
>>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +- io-channels: from common iio binding. use to pipe external sigma delta
>>> +		modulator or internal ADC output to dfsdm channel.
>>> +
>>> +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. Default value is 0.
>>> +			- "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. default value is 1.
>>> +			  - "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.
>>> +		   to used for multi microphone synchronization.
>>> +
>>> +Example of a sigma delta adc purpose:
>>> +	ads1202: simple_sd_adc@0 {
>>> +		compatible = "sd-modulator";
>> This suggests to me that we ought to have a primary compatible of the actual part
>> number.  Down the line I suspect we will want to distinguish between the different
>> ADCs and it's kind of easier if we do it from the start.  May not be obvious
>> later which the 'default sd-modulator' is.
> Not sure to understand your point... you mean use ADC name instead, for
> compatibility as suggested by Rob?
Yes, though you can do it as (and I may have the syntax wrong)
compatible = "ads1202", "sd-modulator" perhaps giving a fallback.
Better than having people 'lie' about the connected part in device trees
because they don't want to push the change upstream to add their part to those
supported.

This is similar to what we do for SoC ADCs where they are in theory compatible
across all supported parts, but in reality there are sometimes errata etc.
Allows people to be as specific as possible in their device trees, without
precluding use in the generic form if the part isn't explicitly supported.

Up to Rob though on whether he's happy with that trick here...

> 
>>> +		#io-channel-cells = <1>;
>>> +		status = "okay";
>>> +	};
>>> +	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 = "in0";
>>> +			st,adc-channel-types = "SPI_R";
>>> +			st,adc-channel-clk-src = "CLKOUT";
>>> +			io-channels = <&ads1202 0>;
>>> +	};
>>> +
>>> +Example of a pdm microphone purpose:
>> Is there nothing about the particular pdm microphone that is
>> worth giving it an explicit representation in the device
>> tree?  I've no idea having never used one!
>>> +	dfsdm: dfsdm@40017000 {
>>> +		compatible = "st,stm32h7-dfsdm";
>>> +		reg = <0x40017000 0x400>;
>>> +		clocks = <&timer_clk>;
>>> +		clock-names = "dfsdm";
>>> +
>>> +		dfsdm_pdm0: dfsdm-pdm@0 {
>>> +			compatible = "st,stm32-dfsdm-pdm";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			interrupts = <110>;
>>> +			dmas = <&dmamux1 102 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,adc-channels = <0>;
>>> +			st,adc-channel-names = "pdm1";
>>> +			st,adc-channel-types = "SPI_R";
>>> +			st,adc-channel-clk-src = "CLKOUT";
>>> +		};
>>> +	}
>>>
>>
> Here is a way to use it in device tree.
> - dmic_0 is an ASOC generic device that handles the PDM microphone
> - dai-link allows to bind the DFSDM with the SD-modulator in ALSA card
> declaration.
> 
> 	dmic0: dmic_0@0 {
> 		compatible = "dmic-codec";
> 		#sound-dai-cells = <0>;
> 
> 		status = "okay";
> 	};
> 
> 	sound_dfsdm_pdm {
> 		compatible = "simple-audio-card";
> 		simple-audio-card,name = "dfsdm_pdm";
> 		status = "okay";
> 
> 		dfsdm0_mic0: simple-audio-card,dai-link@0 {
> 			format = "pdm";
> 			bitclock-master = <&dmic0_codec>;
> 			cpu {
> 				system-clock-frequency= <2480000>;
> 				sound-dai = <&dfsdm_pdm0>;
> 			};
> 			dmic0_codec: codec {
> 				sound-dai = <&dmic0>;
> 			};
> 		};
> 	};
> };
That's fine.  I was just wondering if they ever had an 'smarts' 
that might make it sensible to provide an explicit description like
we are doing for the ADC part being used.

Sounds like no!
> 
> --
> 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] 107+ messages in thread

* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
@ 2017-03-05 11:00           ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 11:00 UTC (permalink / raw)
  To: linux-arm-kernel

On 27/02/17 10:47, Arnaud Pouliquen wrote:
> 
> 
> On 02/19/2017 04:00 PM, Jonathan Cameron wrote:
>> On 13/02/17 16:38, Arnaud Pouliquen wrote:
>>> Add bindings that describe Digital Filter for Sigma Delta
>>> Modulators. DFSDM allows to connect sigma delta
>>> modulators and/or PDM microphones.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> ---
>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 125 +++++++++++++++++++++
>>>  1 file changed, 125 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..83937cb
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt
>>> @@ -0,0 +1,125 @@
>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>> +
>>> +
>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter dedicted to
>>> +interface external sigma delta modulators to stm32 micro controlers.
>>> +it is mainly targeted for:
>>> +- Sigma delta modulators ( motor control, metering...)
>>> +- PDM microphones ( audio digital microphone)
>>> +
>>> +It featured with up to 8 serial digital interface (SPI or Manchester) and
>>> +up to 4 filters.
>>> +
>>> +Each instance of the sub-drivers uses one filter instance.
>>> +
>>> +Contents of a stm32 dfsdmc root node:
>>> +-------------------------------------
>>> +Required properties:
>>> +- compatible: Should be "st,stm32-dfsdm-adc".
>>> +- 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.
>>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>>> +- clock-names: Must be "adc".
>>> +
>>> +Optional properties:
>>> +- st,clkout-freq: needed 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, 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-pdm" for audio digital microphone.
>>> +- reg: Specifies the DFSDM filter instance.
>>> +- 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 channels Name.
>>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>>> +			0: FastSinC
>>> +			[1-5]: order 1 to 5.
>>> +			For audio purpose it is recommended to use order 3 to 5.
>>> +
>>> +Required properties for "st,stm32-dfsdm-adc" compatibility:
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +- io-channels: from common iio binding. use to pipe external sigma delta
>>> +		modulator or internal ADC output to dfsdm channel.
>>> +
>>> +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. Default value is 0.
>>> +			- "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. default value is 1.
>>> +			  - "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.
>>> +		   to used for multi microphone synchronization.
>>> +
>>> +Example of a sigma delta adc purpose:
>>> +	ads1202: simple_sd_adc at 0 {
>>> +		compatible = "sd-modulator";
>> This suggests to me that we ought to have a primary compatible of the actual part
>> number.  Down the line I suspect we will want to distinguish between the different
>> ADCs and it's kind of easier if we do it from the start.  May not be obvious
>> later which the 'default sd-modulator' is.
> Not sure to understand your point... you mean use ADC name instead, for
> compatibility as suggested by Rob?
Yes, though you can do it as (and I may have the syntax wrong)
compatible = "ads1202", "sd-modulator" perhaps giving a fallback.
Better than having people 'lie' about the connected part in device trees
because they don't want to push the change upstream to add their part to those
supported.

This is similar to what we do for SoC ADCs where they are in theory compatible
across all supported parts, but in reality there are sometimes errata etc.
Allows people to be as specific as possible in their device trees, without
precluding use in the generic form if the part isn't explicitly supported.

Up to Rob though on whether he's happy with that trick here...

> 
>>> +		#io-channel-cells = <1>;
>>> +		status = "okay";
>>> +	};
>>> +	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 = "in0";
>>> +			st,adc-channel-types = "SPI_R";
>>> +			st,adc-channel-clk-src = "CLKOUT";
>>> +			io-channels = <&ads1202 0>;
>>> +	};
>>> +
>>> +Example of a pdm microphone purpose:
>> Is there nothing about the particular pdm microphone that is
>> worth giving it an explicit representation in the device
>> tree?  I've no idea having never used one!
>>> +	dfsdm: dfsdm at 40017000 {
>>> +		compatible = "st,stm32h7-dfsdm";
>>> +		reg = <0x40017000 0x400>;
>>> +		clocks = <&timer_clk>;
>>> +		clock-names = "dfsdm";
>>> +
>>> +		dfsdm_pdm0: dfsdm-pdm at 0 {
>>> +			compatible = "st,stm32-dfsdm-pdm";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			interrupts = <110>;
>>> +			dmas = <&dmamux1 102 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,adc-channels = <0>;
>>> +			st,adc-channel-names = "pdm1";
>>> +			st,adc-channel-types = "SPI_R";
>>> +			st,adc-channel-clk-src = "CLKOUT";
>>> +		};
>>> +	}
>>>
>>
> Here is a way to use it in device tree.
> - dmic_0 is an ASOC generic device that handles the PDM microphone
> - dai-link allows to bind the DFSDM with the SD-modulator in ALSA card
> declaration.
> 
> 	dmic0: dmic_0 at 0 {
> 		compatible = "dmic-codec";
> 		#sound-dai-cells = <0>;
> 
> 		status = "okay";
> 	};
> 
> 	sound_dfsdm_pdm {
> 		compatible = "simple-audio-card";
> 		simple-audio-card,name = "dfsdm_pdm";
> 		status = "okay";
> 
> 		dfsdm0_mic0: simple-audio-card,dai-link at 0 {
> 			format = "pdm";
> 			bitclock-master = <&dmic0_codec>;
> 			cpu {
> 				system-clock-frequency= <2480000>;
> 				sound-dai = <&dfsdm_pdm0>;
> 			};
> 			dmic0_codec: codec {
> 				sound-dai = <&dmic0>;
> 			};
> 		};
> 	};
> };
That's fine.  I was just wondering if they ever had an 'smarts' 
that might make it sensible to provide an explicit description like
we are doing for the ADC part being used.

Sounds like no!
> 
> --
> 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] 107+ messages in thread

* Re: [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
  2017-02-27 11:15       ` Arnaud Pouliquen
  (?)
@ 2017-03-05 11:04         ` Jonathan Cameron
  -1 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 11:04 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Olivier MOYSAN, kernel, Liam Girdwood, linux-iio, Takashi Iwai,
	Maxime Coquelin, Mark Brown, Peter Meerwald-Stadler,
	Hartmut Knaack, linux-arm-kernel, Alexandre TORGUE

On 27/02/17 11:15, Arnaud Pouliquen wrote:
> Hello Rob,
> 
> Please find my answers in-line
> 
> Regards,
> 
> Arnaud
> 
> On 02/22/2017 04:17 PM, Rob Herring wrote:
>> On Mon, Feb 13, 2017 at 05:38:24PM +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>
>>> ---
>>>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>>>  1 file changed, 13 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>> new file mode 100644
>>> index 0000000..2b3968a
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>> @@ -0,0 +1,13 @@
>>> +Device-Tree bindings for simple sigma delta adc
>>
>> What makes it "simple"?
> "simple" sigma delta modulator are external chip that just converts
> analog signal in sigma delta modulation. No configuration needs.
> "Simple" is use to differentiate SD ADC that offers interfaces to
> activate some internal processing ( gain, offset compensation...)
My gut feeling would be to drop the term simple.  If device is more complex
it needs to be explicitly supported, hence will be named whatever!
> 
>>
>>> +
>>> +Required properties:
>>> +- compatible: should be "sd-modulator".
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +
>>> +Example node:
>>> +
>>> +	ads1202: simple_sd_adc@0 {
>>
>> Is ads1202 the actual chip? Then it should be in the compatible list.
> I tried to define a generic device to support several SD modulator
> chips, inspired by ALSA "dmic-codec" device.
> I have none exhaustive list of chip that can be handled by the driver,
> but i found several devices that could match...
> That why i did not use chip name in compatible list.
> What should be the best way to do it?
Rob, would the fallback approach (I brought this up in  another branch of
this discussion) work here,

compatible = "ads1201", "sd-modulator'?

Main advantage is that people won't then lie in their board files just to
avoid changing the driver to introduce their new part.  They'll just
be less specific than would be ideal.
> 
>>
>> unit address without a reg prop is an error. The node name should be 
>> "adc".
>>
>>> +		compatible = "sd-modulator";
>>> +		#io-channel-cells = <1>;
>>> +		status = "okay";
>>
>> Drop status from examples.
>>
>>> +	};
>>> -- 
>>> 1.9.1
>>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
@ 2017-03-05 11:04         ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 11:04 UTC (permalink / raw)
  To: Arnaud Pouliquen, Rob Herring
  Cc: Mark Rutland, 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 27/02/17 11:15, Arnaud Pouliquen wrote:
> Hello Rob,
> 
> Please find my answers in-line
> 
> Regards,
> 
> Arnaud
> 
> On 02/22/2017 04:17 PM, Rob Herring wrote:
>> On Mon, Feb 13, 2017 at 05:38:24PM +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>
>>> ---
>>>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>>>  1 file changed, 13 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>> new file mode 100644
>>> index 0000000..2b3968a
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>> @@ -0,0 +1,13 @@
>>> +Device-Tree bindings for simple sigma delta adc
>>
>> What makes it "simple"?
> "simple" sigma delta modulator are external chip that just converts
> analog signal in sigma delta modulation. No configuration needs.
> "Simple" is use to differentiate SD ADC that offers interfaces to
> activate some internal processing ( gain, offset compensation...)
My gut feeling would be to drop the term simple.  If device is more complex
it needs to be explicitly supported, hence will be named whatever!
> 
>>
>>> +
>>> +Required properties:
>>> +- compatible: should be "sd-modulator".
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +
>>> +Example node:
>>> +
>>> +	ads1202: simple_sd_adc@0 {
>>
>> Is ads1202 the actual chip? Then it should be in the compatible list.
> I tried to define a generic device to support several SD modulator
> chips, inspired by ALSA "dmic-codec" device.
> I have none exhaustive list of chip that can be handled by the driver,
> but i found several devices that could match...
> That why i did not use chip name in compatible list.
> What should be the best way to do it?
Rob, would the fallback approach (I brought this up in  another branch of
this discussion) work here,

compatible = "ads1201", "sd-modulator'?

Main advantage is that people won't then lie in their board files just to
avoid changing the driver to introduce their new part.  They'll just
be less specific than would be ideal.
> 
>>
>> unit address without a reg prop is an error. The node name should be 
>> "adc".
>>
>>> +		compatible = "sd-modulator";
>>> +		#io-channel-cells = <1>;
>>> +		status = "okay";
>>
>> Drop status from examples.
>>
>>> +	};
>>> -- 
>>> 1.9.1
>>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
@ 2017-03-05 11:04         ` Jonathan Cameron
  0 siblings, 0 replies; 107+ messages in thread
From: Jonathan Cameron @ 2017-03-05 11:04 UTC (permalink / raw)
  To: linux-arm-kernel

On 27/02/17 11:15, Arnaud Pouliquen wrote:
> Hello Rob,
> 
> Please find my answers in-line
> 
> Regards,
> 
> Arnaud
> 
> On 02/22/2017 04:17 PM, Rob Herring wrote:
>> On Mon, Feb 13, 2017 at 05:38:24PM +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>
>>> ---
>>>  Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt | 13 +++++++++++++
>>>  1 file changed, 13 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>> new file mode 100644
>>> index 0000000..2b3968a
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/simple_sd_adc.txt
>>> @@ -0,0 +1,13 @@
>>> +Device-Tree bindings for simple sigma delta adc
>>
>> What makes it "simple"?
> "simple" sigma delta modulator are external chip that just converts
> analog signal in sigma delta modulation. No configuration needs.
> "Simple" is use to differentiate SD ADC that offers interfaces to
> activate some internal processing ( gain, offset compensation...)
My gut feeling would be to drop the term simple.  If device is more complex
it needs to be explicitly supported, hence will be named whatever!
> 
>>
>>> +
>>> +Required properties:
>>> +- compatible: should be "sd-modulator".
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +
>>> +Example node:
>>> +
>>> +	ads1202: simple_sd_adc at 0 {
>>
>> Is ads1202 the actual chip? Then it should be in the compatible list.
> I tried to define a generic device to support several SD modulator
> chips, inspired by ALSA "dmic-codec" device.
> I have none exhaustive list of chip that can be handled by the driver,
> but i found several devices that could match...
> That why i did not use chip name in compatible list.
> What should be the best way to do it?
Rob, would the fallback approach (I brought this up in  another branch of
this discussion) work here,

compatible = "ads1201", "sd-modulator'?

Main advantage is that people won't then lie in their board files just to
avoid changing the driver to introduce their new part.  They'll just
be less specific than would be ideal.
> 
>>
>> unit address without a reg prop is an error. The node name should be 
>> "adc".
>>
>>> +		compatible = "sd-modulator";
>>> +		#io-channel-cells = <1>;
>>> +		status = "okay";
>>
>> Drop status from examples.
>>
>>> +	};
>>> -- 
>>> 1.9.1
>>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

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

Thread overview: 107+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-13 16:38 [RFC v2 0/7] Add STM32 DFSDM support Arnaud Pouliquen
2017-02-13 16:38 ` Arnaud Pouliquen
2017-02-13 16:38 ` Arnaud Pouliquen
2017-02-13 16:38 ` [RFC v2 1/7] iio: Add hardware consumer support Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
     [not found]   ` <1487003909-11710-2-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-02-19 14:13     ` Jonathan Cameron
2017-02-19 14:13       ` Jonathan Cameron
2017-02-19 14:13       ` Jonathan Cameron
2017-02-13 16:38 ` [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-22 15:17   ` Rob Herring
2017-02-22 15:17     ` Rob Herring
2017-02-22 15:17     ` Rob Herring
2017-02-27 11:15     ` Arnaud Pouliquen
2017-02-27 11:15       ` Arnaud Pouliquen
2017-02-27 11:15       ` Arnaud Pouliquen
2017-03-05 11:04       ` Jonathan Cameron
2017-03-05 11:04         ` Jonathan Cameron
2017-03-05 11:04         ` Jonathan Cameron
2017-02-13 16:38 ` [RFC v2 3/7] IIO: ADC: add sigma delta modulator support Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
     [not found]   ` <1487003909-11710-4-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-02-19 14:20     ` Jonathan Cameron
2017-02-19 14:20       ` Jonathan Cameron
2017-02-19 14:20       ` Jonathan Cameron
2017-02-13 16:38 ` [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-14 17:16   ` Mark Brown
2017-02-14 17:16     ` Mark Brown
2017-02-14 17:16     ` Mark Brown
2017-02-15 13:59     ` Arnaud Pouliquen
2017-02-15 13:59       ` Arnaud Pouliquen
2017-02-15 13:59       ` Arnaud Pouliquen
     [not found]       ` <40633f7c-a2ac-1658-cc9d-b30eaff8a95a-qxv4g6HH51o@public.gmane.org>
2017-02-15 14:53         ` Mark Brown
2017-02-15 14:53           ` Mark Brown
2017-02-15 14:53           ` Mark Brown
2017-02-15 15:46           ` Arnaud Pouliquen
2017-02-15 15:46             ` Arnaud Pouliquen
2017-02-15 15:46             ` Arnaud Pouliquen
     [not found]             ` <338f8db7-2077-626f-986b-b4e3df40469c-qxv4g6HH51o@public.gmane.org>
2017-02-16 20:14               ` Mark Brown
2017-02-16 20:14                 ` Mark Brown
2017-02-16 20:14                 ` Mark Brown
2017-02-27  9:05                 ` Arnaud Pouliquen
2017-02-27  9:05                   ` Arnaud Pouliquen
2017-02-27  9:05                   ` Arnaud Pouliquen
2017-02-13 16:38 ` [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
     [not found]   ` <1487003909-11710-6-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-02-13 18:13     ` Peter Meerwald-Stadler
2017-02-13 18:13       ` Peter Meerwald-Stadler
     [not found]       ` <alpine.DEB.2.02.1702131906350.25127-jW+XmwGofnusTnJN9+BGXg@public.gmane.org>
2017-02-14 11:09         ` Arnaud Pouliquen
2017-02-14 11:09           ` Arnaud Pouliquen
     [not found]           ` <c381a9a2-5dff-af9a-eeb0-8fd1a74f448e-qxv4g6HH51o@public.gmane.org>
2017-02-14 12:57             ` Peter Meerwald-Stadler
2017-02-14 12:57               ` Peter Meerwald-Stadler
2017-02-14 17:45     ` Mark Brown
2017-02-14 17:45       ` Mark Brown
2017-02-14 17:45       ` Mark Brown
2017-02-15 16:39       ` Arnaud Pouliquen
2017-02-15 16:39         ` Arnaud Pouliquen
2017-02-15 16:39         ` Arnaud Pouliquen
     [not found]         ` <9b875a75-294a-2f59-5830-cc0f6b3b62c7-qxv4g6HH51o@public.gmane.org>
2017-02-15 16:53           ` Mark Brown
2017-02-15 16:53             ` Mark Brown
2017-02-15 16:53             ` Mark Brown
     [not found]       ` <20170214174534.35ytbpax75mxcayg-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
2017-02-19 14:56         ` Jonathan Cameron
2017-02-19 14:56           ` Jonathan Cameron
2017-02-19 14:56           ` Jonathan Cameron
2017-02-27 10:31           ` Arnaud Pouliquen
2017-02-27 10:31             ` Arnaud Pouliquen
2017-02-27 10:31             ` Arnaud Pouliquen
     [not found]             ` <84f330ab-48a2-6e0b-ab95-6aab5b34c241-qxv4g6HH51o@public.gmane.org>
2017-03-05 10:55               ` Jonathan Cameron
2017-03-05 10:55                 ` Jonathan Cameron
2017-03-05 10:55                 ` Jonathan Cameron
2017-02-13 16:38 ` [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-19 15:00   ` Jonathan Cameron
2017-02-19 15:00     ` Jonathan Cameron
2017-02-19 15:00     ` Jonathan Cameron
2017-02-27 10:47     ` Arnaud Pouliquen
2017-02-27 10:47       ` Arnaud Pouliquen
2017-02-27 10:47       ` Arnaud Pouliquen
     [not found]       ` <7fbfc694-3685-ec90-6292-5a5157a8a0d2-qxv4g6HH51o@public.gmane.org>
2017-03-05 11:00         ` Jonathan Cameron
2017-03-05 11:00           ` Jonathan Cameron
2017-03-05 11:00           ` Jonathan Cameron
     [not found]   ` <1487003909-11710-7-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-02-13 18:05     ` Peter Meerwald-Stadler
2017-02-13 18:05       ` Peter Meerwald-Stadler
2017-02-22 16:42     ` Rob Herring
2017-02-22 16:42       ` Rob Herring
2017-02-22 16:42       ` Rob Herring
2017-02-27 14:07       ` Arnaud Pouliquen
2017-02-27 14:07         ` Arnaud Pouliquen
2017-02-27 14:07         ` Arnaud Pouliquen
2017-02-13 16:38 ` [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-13 16:38   ` Arnaud Pouliquen
2017-02-19 14:46   ` Jonathan Cameron
2017-02-19 14:46     ` Jonathan Cameron
2017-02-19 14:46     ` Jonathan Cameron
2017-02-27 10:09     ` Arnaud Pouliquen
2017-02-27 10:09       ` Arnaud Pouliquen
2017-02-27 10:09       ` Arnaud Pouliquen
     [not found]       ` <fe86eca5-5dca-efb3-45d2-46e193f60dc9-qxv4g6HH51o@public.gmane.org>
2017-03-05 10:55         ` Jonathan Cameron
2017-03-05 10:55           ` Jonathan Cameron
2017-03-05 10:55           ` Jonathan Cameron

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.