* [RFC v2 1/7] iio: Add hardware consumer support
2017-02-13 16:38 [RFC v2 0/7] Add STM32 DFSDM support 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-13 16:38 ` [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc Arnaud Pouliquen
` (5 subsequent siblings)
6 siblings, 1 reply; 37+ 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] 37+ messages in thread
* [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
2017-02-13 16:38 [RFC v2 0/7] Add STM32 DFSDM support 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-22 15:17 ` Rob Herring
2017-02-13 16:38 ` [RFC v2 3/7] IIO: ADC: add sigma delta modulator support Arnaud Pouliquen
` (4 subsequent siblings)
6 siblings, 1 reply; 37+ 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] 37+ messages in thread
* Re: [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc
2017-02-13 16:38 ` [RFC v2 2/7] IIO: Add bindings for simple sigma delta adc Arnaud Pouliquen
@ 2017-02-22 15:17 ` Rob Herring
2017-02-27 11:15 ` Arnaud Pouliquen
0 siblings, 1 reply; 37+ 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] 37+ 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
2017-03-05 11:04 ` Jonathan Cameron
0 siblings, 1 reply; 37+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 11:15 UTC (permalink / raw)
To: Rob Herring
Cc: Mark Rutland, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, Lars-Peter Clausen, Olivier MOYSAN,
kernel@stlinux.com, Liam Girdwood, linux-iio@vger.kernel.org,
Takashi Iwai, Maxime Coquelin, Mark Brown,
linux-arm-kernel@lists.infradead.org, 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] 37+ 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
0 siblings, 0 replies; 37+ messages in thread
From: Jonathan Cameron @ 2017-03-05 11:04 UTC (permalink / raw)
To: Arnaud Pouliquen, Rob Herring
Cc: Mark Rutland, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, Lars-Peter Clausen, Olivier MOYSAN,
kernel@stlinux.com, Liam Girdwood, linux-iio@vger.kernel.org,
Takashi Iwai, Maxime Coquelin, Mark Brown, Peter Meerwald-Stadler,
Hartmut Knaack, linux-arm-kernel@lists.infradead.org,
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] 37+ messages in thread
* [RFC v2 3/7] IIO: ADC: add sigma delta modulator support
2017-02-13 16:38 [RFC v2 0/7] Add STM32 DFSDM support Arnaud Pouliquen
2017-02-13 16:38 ` [RFC v2 1/7] iio: Add hardware consumer support Arnaud Pouliquen
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
[not found] ` <1487003909-11710-4-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-02-13 16:38 ` [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support Arnaud Pouliquen
` (3 subsequent siblings)
6 siblings, 1 reply; 37+ 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] 37+ messages in thread
* [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
2017-02-13 16:38 [RFC v2 0/7] Add STM32 DFSDM support Arnaud Pouliquen
` (2 preceding siblings ...)
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-14 17:16 ` Mark Brown
2017-02-13 16:38 ` [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support Arnaud Pouliquen
` (2 subsequent siblings)
6 siblings, 1 reply; 37+ 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] 37+ messages in thread
* Re: [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support
2017-02-13 16:38 ` [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support Arnaud Pouliquen
@ 2017-02-14 17:16 ` Mark Brown
2017-02-15 13:59 ` Arnaud Pouliquen
0 siblings, 1 reply; 37+ 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] 37+ 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
[not found] ` <40633f7c-a2ac-1658-cc9d-b30eaff8a95a-qxv4g6HH51o@public.gmane.org>
0 siblings, 1 reply; 37+ messages in thread
From: Arnaud Pouliquen @ 2017-02-15 13:59 UTC (permalink / raw)
To: Mark Brown
Cc: Mark Rutland, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, Lars-Peter Clausen, Olivier MOYSAN,
kernel@stlinux.com, Liam Girdwood, linux-iio@vger.kernel.org,
Takashi Iwai, Maxime Coquelin, Rob Herring,
linux-arm-kernel@lists.infradead.org, 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] 37+ messages in thread
* [RFC v2 5/7] ASoC: stm32: add DFSDM DAI support
2017-02-13 16:38 [RFC v2 0/7] Add STM32 DFSDM support Arnaud Pouliquen
` (3 preceding siblings ...)
2017-02-13 16:38 ` [RFC v2 4/7] ASoC: dmaengine_pcm: add copy support 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 16:38 ` [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter Arnaud Pouliquen
2017-02-13 16:38 ` [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support Arnaud Pouliquen
6 siblings, 1 reply; 37+ 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, ¶ms);
+ 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] 37+ messages in thread
* [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
2017-02-13 16:38 [RFC v2 0/7] Add STM32 DFSDM support Arnaud Pouliquen
` (4 preceding siblings ...)
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-19 15:00 ` Jonathan Cameron
[not found] ` <1487003909-11710-7-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-02-13 16:38 ` [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support Arnaud Pouliquen
6 siblings, 2 replies; 37+ 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] 37+ messages in thread
* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
2017-02-13 16:38 ` [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter Arnaud Pouliquen
@ 2017-02-19 15:00 ` Jonathan Cameron
2017-02-27 10:47 ` Arnaud Pouliquen
[not found] ` <1487003909-11710-7-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
1 sibling, 1 reply; 37+ 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] 37+ 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
[not found] ` <7fbfc694-3685-ec90-6292-5a5157a8a0d2-qxv4g6HH51o@public.gmane.org>
0 siblings, 1 reply; 37+ 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@vger.kernel.org, alsa-devel@alsa-project.org,
Olivier MOYSAN, kernel@stlinux.com, linux-iio@vger.kernel.org,
Maxime Coquelin, linux-arm-kernel@lists.infradead.org,
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] 37+ messages in thread
[parent not found: <1487003909-11710-7-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>]
* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
[not found] ` <1487003909-11710-7-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
@ 2017-02-13 18:05 ` Peter Meerwald-Stadler
2017-02-22 16:42 ` Rob Herring
1 sibling, 0 replies; 37+ 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] 37+ messages in thread
* Re: [RFC v2 6/7] IIO: add bindings for stm32 DFSDM filter
[not found] ` <1487003909-11710-7-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-02-13 18:05 ` Peter Meerwald-Stadler
@ 2017-02-22 16:42 ` Rob Herring
2017-02-27 14:07 ` Arnaud Pouliquen
1 sibling, 1 reply; 37+ 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] 37+ 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
0 siblings, 0 replies; 37+ messages in thread
From: Arnaud Pouliquen @ 2017-02-27 14:07 UTC (permalink / raw)
To: Rob Herring
Cc: Mark Rutland, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, Lars-Peter Clausen, Olivier MOYSAN,
kernel@stlinux.com, Liam Girdwood, linux-iio@vger.kernel.org,
Takashi Iwai, Maxime Coquelin, Mark Brown,
linux-arm-kernel@lists.infradead.org, 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] 37+ messages in thread
* [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
2017-02-13 16:38 [RFC v2 0/7] Add STM32 DFSDM support Arnaud Pouliquen
` (5 preceding siblings ...)
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-19 14:46 ` Jonathan Cameron
6 siblings, 1 reply; 37+ 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] 37+ messages in thread
* Re: [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support
2017-02-13 16:38 ` [RFC v2 7/7] IIO: ADC: add stm32 DFSDM support Arnaud Pouliquen
@ 2017-02-19 14:46 ` Jonathan Cameron
2017-02-27 10:09 ` Arnaud Pouliquen
0 siblings, 1 reply; 37+ 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] 37+ 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
[not found] ` <fe86eca5-5dca-efb3-45d2-46e193f60dc9-qxv4g6HH51o@public.gmane.org>
0 siblings, 1 reply; 37+ 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@vger.kernel.org, alsa-devel@alsa-project.org,
Olivier MOYSAN, kernel@stlinux.com, linux-iio@vger.kernel.org,
Maxime Coquelin, linux-arm-kernel@lists.infradead.org,
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] 37+ messages in thread