All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/10] iio: dac: support IIO backends on the output direction
@ 2024-03-28 13:22 ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa, Paul Cercueil,
	Alexandru Ardelean

Hi Jonathan,

This is the result of the brief discussion we had in the RFC [1].
Patches 2 to 5 from Paul's series to bring output buffer support to
DMA buffers [2].

Note that the original DMA patch 4 ("iio: buffer-dmaengine: Support
specifying buffer direction") also had changes for the axi-adc as it
used devm_iio_dmaengine_buffer_setup(). However, when that was converted
to the backend framework, we started to use iio_dmaengine_buffer_alloc()
and default is INPUT so no need for any change.

I did gave it a try on beginning to support extending IIO channels
(chan_spec) from backend's. For now, we just need to do it for the
extended info and honestly, IMHO, I don't think the result to be
horrible :). The only thing that I don't like much is the call to
iio_device_set_drvdata() in iio_backend_extend_chan_spec(). Long story
short, I know it's likely to break so maybe we should have a proper
solution right from the beginning. But see the comment I have in there.
I did thought in a solution that should work but you may have a better
idea.

Also note the main reason to come up with the iio_backend_ext_info_set()
and iio_backend_ext_info_get() helpers is not to allow starting having
these from backends. I was even thinking in just passing the callbacks
from the frontend to iio_backend_extend_chan_spec() but I kind of prefer
this way. We may still need at some point to allow frontends to have full
control and have their callbacks in the attributes. But for now, I'm not
even allowing for backends to append a channel ext_info so i would say to
worry about that when such a scenario pops up.

I would also prefer for  iio_backend_ext_info_set() and
iio_backend_ext_info_get() to be static but that would make the code more
complex than it needs to be (we would have to kmemdup() the backends
ext_info and assign the callbacks) so I went this way.

[1]: https://lore.kernel.org/linux-iio/20240216-iio-backend-axi-dds-v1-0-22aed9fb07a1@analog.com/
[2]: https://lore.kernel.org/linux-iio/20230807112113.47157-1-paul@crapouillou.net/

---
Nuno Sa (6):
      iio: buffer: add helper for setting direction
      dt-bindings: iio: dac: add bindings doc for AXI DAC driver
      dt-bindings: iio: dac: add bindings doc for AD9739A
      iio: backend: add new functionality
      iio: dac: add support for AXI DAC IP core
      iio: dac: support the ad9739a RF DAC

Paul Cercueil (4):
      iio: buffer-dma: Rename iio_dma_buffer_data_available()
      iio: buffer-dma: Enable buffer write support
      iio: buffer-dmaengine: Support specifying buffer direction
      iio: buffer-dmaengine: Enable write support

 Documentation/ABI/testing/sysfs-bus-iio-ad9739a    |  17 +
 .../devicetree/bindings/iio/dac/adi,ad9739a.yaml   |  88 +++
 .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   |  62 ++
 MAINTAINERS                                        |  17 +
 drivers/iio/buffer/industrialio-buffer-dma.c       | 100 +++-
 drivers/iio/buffer/industrialio-buffer-dmaengine.c |  28 +-
 drivers/iio/dac/Kconfig                            |  37 ++
 drivers/iio/dac/Makefile                           |   2 +
 drivers/iio/dac/ad9739a.c                          | 445 ++++++++++++++
 drivers/iio/dac/adi-axi-dac.c                      | 644 +++++++++++++++++++++
 drivers/iio/industrialio-backend.c                 | 144 +++++
 drivers/iio/industrialio-buffer.c                  |   7 +
 include/linux/iio/backend.h                        |  49 ++
 include/linux/iio/buffer-dma.h                     |   4 +-
 include/linux/iio/buffer-dmaengine.h               |   6 +-
 include/linux/iio/buffer.h                         |   3 +
 16 files changed, 1624 insertions(+), 29 deletions(-)
---
base-commit: a276b4da56e988157a34b9fef9c46ebfd95f7f09
change-id: 20240326-iio-backend-axi-dac-497a707a5d88
--

Thanks!
- Nuno Sá



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

* [PATCH 00/10] iio: dac: support IIO backends on the output direction
@ 2024-03-28 13:22 ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa, Paul Cercueil,
	Alexandru Ardelean

Hi Jonathan,

This is the result of the brief discussion we had in the RFC [1].
Patches 2 to 5 from Paul's series to bring output buffer support to
DMA buffers [2].

Note that the original DMA patch 4 ("iio: buffer-dmaengine: Support
specifying buffer direction") also had changes for the axi-adc as it
used devm_iio_dmaengine_buffer_setup(). However, when that was converted
to the backend framework, we started to use iio_dmaengine_buffer_alloc()
and default is INPUT so no need for any change.

I did gave it a try on beginning to support extending IIO channels
(chan_spec) from backend's. For now, we just need to do it for the
extended info and honestly, IMHO, I don't think the result to be
horrible :). The only thing that I don't like much is the call to
iio_device_set_drvdata() in iio_backend_extend_chan_spec(). Long story
short, I know it's likely to break so maybe we should have a proper
solution right from the beginning. But see the comment I have in there.
I did thought in a solution that should work but you may have a better
idea.

Also note the main reason to come up with the iio_backend_ext_info_set()
and iio_backend_ext_info_get() helpers is not to allow starting having
these from backends. I was even thinking in just passing the callbacks
from the frontend to iio_backend_extend_chan_spec() but I kind of prefer
this way. We may still need at some point to allow frontends to have full
control and have their callbacks in the attributes. But for now, I'm not
even allowing for backends to append a channel ext_info so i would say to
worry about that when such a scenario pops up.

I would also prefer for  iio_backend_ext_info_set() and
iio_backend_ext_info_get() to be static but that would make the code more
complex than it needs to be (we would have to kmemdup() the backends
ext_info and assign the callbacks) so I went this way.

[1]: https://lore.kernel.org/linux-iio/20240216-iio-backend-axi-dds-v1-0-22aed9fb07a1@analog.com/
[2]: https://lore.kernel.org/linux-iio/20230807112113.47157-1-paul@crapouillou.net/

---
Nuno Sa (6):
      iio: buffer: add helper for setting direction
      dt-bindings: iio: dac: add bindings doc for AXI DAC driver
      dt-bindings: iio: dac: add bindings doc for AD9739A
      iio: backend: add new functionality
      iio: dac: add support for AXI DAC IP core
      iio: dac: support the ad9739a RF DAC

Paul Cercueil (4):
      iio: buffer-dma: Rename iio_dma_buffer_data_available()
      iio: buffer-dma: Enable buffer write support
      iio: buffer-dmaengine: Support specifying buffer direction
      iio: buffer-dmaengine: Enable write support

 Documentation/ABI/testing/sysfs-bus-iio-ad9739a    |  17 +
 .../devicetree/bindings/iio/dac/adi,ad9739a.yaml   |  88 +++
 .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   |  62 ++
 MAINTAINERS                                        |  17 +
 drivers/iio/buffer/industrialio-buffer-dma.c       | 100 +++-
 drivers/iio/buffer/industrialio-buffer-dmaengine.c |  28 +-
 drivers/iio/dac/Kconfig                            |  37 ++
 drivers/iio/dac/Makefile                           |   2 +
 drivers/iio/dac/ad9739a.c                          | 445 ++++++++++++++
 drivers/iio/dac/adi-axi-dac.c                      | 644 +++++++++++++++++++++
 drivers/iio/industrialio-backend.c                 | 144 +++++
 drivers/iio/industrialio-buffer.c                  |   7 +
 include/linux/iio/backend.h                        |  49 ++
 include/linux/iio/buffer-dma.h                     |   4 +-
 include/linux/iio/buffer-dmaengine.h               |   6 +-
 include/linux/iio/buffer.h                         |   3 +
 16 files changed, 1624 insertions(+), 29 deletions(-)
---
base-commit: a276b4da56e988157a34b9fef9c46ebfd95f7f09
change-id: 20240326-iio-backend-axi-dac-497a707a5d88
--

Thanks!
- Nuno Sá


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

* [PATCH 01/10] iio: buffer: add helper for setting direction
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

From: Nuno Sa <nuno.sa@analog.com>

Simple helper for setting the buffer direction when it's allocated using
iio_dmaengine_buffer_alloc().

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/industrialio-buffer.c | 7 +++++++
 include/linux/iio/buffer.h        | 3 +++
 2 files changed, 10 insertions(+)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 1d950a3e153b..4b1ca6ad86ee 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1956,6 +1956,13 @@ void iio_buffer_put(struct iio_buffer *buffer)
 }
 EXPORT_SYMBOL_GPL(iio_buffer_put);
 
+void iio_buffer_set_dir(struct iio_buffer *buffer,
+			enum iio_buffer_direction dir)
+{
+	buffer->direction = dir;
+}
+EXPORT_SYMBOL_GPL(iio_buffer_set_dir);
+
 /**
  * iio_device_attach_buffer - Attach a buffer to a IIO device
  * @indio_dev: The device the buffer should be attached to
diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
index 418b1307d3f2..7e70bb5adc01 100644
--- a/include/linux/iio/buffer.h
+++ b/include/linux/iio/buffer.h
@@ -55,4 +55,7 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
 int iio_device_attach_buffer(struct iio_dev *indio_dev,
 			     struct iio_buffer *buffer);
 
+void iio_buffer_set_dir(struct iio_buffer *buffer,
+			enum iio_buffer_direction dir);
+
 #endif /* _IIO_BUFFER_GENERIC_H_ */

-- 
2.44.0



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

* [PATCH 01/10] iio: buffer: add helper for setting direction
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

Simple helper for setting the buffer direction when it's allocated using
iio_dmaengine_buffer_alloc().

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/industrialio-buffer.c | 7 +++++++
 include/linux/iio/buffer.h        | 3 +++
 2 files changed, 10 insertions(+)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 1d950a3e153b..4b1ca6ad86ee 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1956,6 +1956,13 @@ void iio_buffer_put(struct iio_buffer *buffer)
 }
 EXPORT_SYMBOL_GPL(iio_buffer_put);
 
+void iio_buffer_set_dir(struct iio_buffer *buffer,
+			enum iio_buffer_direction dir)
+{
+	buffer->direction = dir;
+}
+EXPORT_SYMBOL_GPL(iio_buffer_set_dir);
+
 /**
  * iio_device_attach_buffer - Attach a buffer to a IIO device
  * @indio_dev: The device the buffer should be attached to
diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
index 418b1307d3f2..7e70bb5adc01 100644
--- a/include/linux/iio/buffer.h
+++ b/include/linux/iio/buffer.h
@@ -55,4 +55,7 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
 int iio_device_attach_buffer(struct iio_dev *indio_dev,
 			     struct iio_buffer *buffer);
 
+void iio_buffer_set_dir(struct iio_buffer *buffer,
+			enum iio_buffer_direction dir);
+
 #endif /* _IIO_BUFFER_GENERIC_H_ */

-- 
2.44.0


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

* [PATCH 02/10] iio: buffer-dma: Rename iio_dma_buffer_data_available()
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Paul Cercueil, Nuno Sa

From: Paul Cercueil <paul@crapouillou.net>

Change its name to iio_dma_buffer_usage(), as this function can be used
both for the .data_available and the .space_available callbacks.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dma.c       | 11 ++++++-----
 drivers/iio/buffer/industrialio-buffer-dmaengine.c |  2 +-
 include/linux/iio/buffer-dma.h                     |  2 +-
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 5610ba67925e..404f9867bdc5 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -547,13 +547,14 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
 
 /**
- * iio_dma_buffer_data_available() - DMA buffer data_available callback
+ * iio_dma_buffer_usage() - DMA buffer data_available and
+ * space_available callback
  * @buf: Buffer to check for data availability
  *
- * Should be used as the data_available callback for iio_buffer_access_ops
- * struct for DMA buffers.
+ * Should be used as the data_available and space_available callbacks for
+ * iio_buffer_access_ops struct for DMA buffers.
  */
-size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
+size_t iio_dma_buffer_usage(struct iio_buffer *buf)
 {
 	struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf);
 	struct iio_dma_buffer_block *block;
@@ -586,7 +587,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
 
 	return data_available;
 }
-EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available);
+EXPORT_SYMBOL_GPL(iio_dma_buffer_usage);
 
 /**
  * iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index a18c1da292af..ccf6e0b19019 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -117,7 +117,7 @@ static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
 	.request_update = iio_dma_buffer_request_update,
 	.enable = iio_dma_buffer_enable,
 	.disable = iio_dma_buffer_disable,
-	.data_available = iio_dma_buffer_data_available,
+	.data_available = iio_dma_buffer_usage,
 	.release = iio_dmaengine_buffer_release,
 
 	.modes = INDIO_BUFFER_HARDWARE,
diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
index 18d3702fa95d..52a838ec0e57 100644
--- a/include/linux/iio/buffer-dma.h
+++ b/include/linux/iio/buffer-dma.h
@@ -132,7 +132,7 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer,
 	struct iio_dev *indio_dev);
 int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 	char __user *user_buffer);
-size_t iio_dma_buffer_data_available(struct iio_buffer *buffer);
+size_t iio_dma_buffer_usage(struct iio_buffer *buffer);
 int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
 int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length);
 int iio_dma_buffer_request_update(struct iio_buffer *buffer);

-- 
2.44.0



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

* [PATCH 02/10] iio: buffer-dma: Rename iio_dma_buffer_data_available()
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Paul Cercueil, Nuno Sa

From: Paul Cercueil <paul@crapouillou.net>

Change its name to iio_dma_buffer_usage(), as this function can be used
both for the .data_available and the .space_available callbacks.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dma.c       | 11 ++++++-----
 drivers/iio/buffer/industrialio-buffer-dmaengine.c |  2 +-
 include/linux/iio/buffer-dma.h                     |  2 +-
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 5610ba67925e..404f9867bdc5 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -547,13 +547,14 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
 
 /**
- * iio_dma_buffer_data_available() - DMA buffer data_available callback
+ * iio_dma_buffer_usage() - DMA buffer data_available and
+ * space_available callback
  * @buf: Buffer to check for data availability
  *
- * Should be used as the data_available callback for iio_buffer_access_ops
- * struct for DMA buffers.
+ * Should be used as the data_available and space_available callbacks for
+ * iio_buffer_access_ops struct for DMA buffers.
  */
-size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
+size_t iio_dma_buffer_usage(struct iio_buffer *buf)
 {
 	struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf);
 	struct iio_dma_buffer_block *block;
@@ -586,7 +587,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
 
 	return data_available;
 }
-EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available);
+EXPORT_SYMBOL_GPL(iio_dma_buffer_usage);
 
 /**
  * iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index a18c1da292af..ccf6e0b19019 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -117,7 +117,7 @@ static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
 	.request_update = iio_dma_buffer_request_update,
 	.enable = iio_dma_buffer_enable,
 	.disable = iio_dma_buffer_disable,
-	.data_available = iio_dma_buffer_data_available,
+	.data_available = iio_dma_buffer_usage,
 	.release = iio_dmaengine_buffer_release,
 
 	.modes = INDIO_BUFFER_HARDWARE,
diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
index 18d3702fa95d..52a838ec0e57 100644
--- a/include/linux/iio/buffer-dma.h
+++ b/include/linux/iio/buffer-dma.h
@@ -132,7 +132,7 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer,
 	struct iio_dev *indio_dev);
 int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 	char __user *user_buffer);
-size_t iio_dma_buffer_data_available(struct iio_buffer *buffer);
+size_t iio_dma_buffer_usage(struct iio_buffer *buffer);
 int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
 int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length);
 int iio_dma_buffer_request_update(struct iio_buffer *buffer);

-- 
2.44.0


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

* [PATCH 03/10] iio: buffer-dma: Enable buffer write support
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Paul Cercueil, Alexandru Ardelean,
	Nuno Sa

From: Paul Cercueil <paul@crapouillou.net>

Adding write support to the buffer-dma code is easy - the write()
function basically needs to do the exact same thing as the read()
function: dequeue a block, read or write the data, enqueue the block
when entirely processed.

Therefore, the iio_buffer_dma_read() and the new iio_buffer_dma_write()
now both call a function iio_buffer_dma_io(), which will perform this
task.

Note that we preemptively reset block->bytes_used to the buffer's size
in iio_dma_buffer_request_update(), as in the future the
iio_dma_buffer_enqueue() function won't reset it.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dma.c | 89 +++++++++++++++++++++++-----
 include/linux/iio/buffer-dma.h               |  2 +
 2 files changed, 75 insertions(+), 16 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 404f9867bdc5..29cc3083cb7e 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -195,6 +195,18 @@ static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
 		block->state = IIO_BLOCK_STATE_DONE;
 }
 
+static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue)
+{
+	__poll_t flags;
+
+	if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN)
+		flags = EPOLLIN | EPOLLRDNORM;
+	else
+		flags = EPOLLOUT | EPOLLWRNORM;
+
+	wake_up_interruptible_poll(&queue->buffer.pollq, flags);
+}
+
 /**
  * iio_dma_buffer_block_done() - Indicate that a block has been completed
  * @block: The completed block
@@ -212,7 +224,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
 	spin_unlock_irqrestore(&queue->list_lock, flags);
 
 	iio_buffer_block_put_atomic(block);
-	wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
+	iio_dma_buffer_queue_wake(queue);
 }
 EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done);
 
@@ -241,7 +253,7 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
 	}
 	spin_unlock_irqrestore(&queue->list_lock, flags);
 
-	wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
+	iio_dma_buffer_queue_wake(queue);
 }
 EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort);
 
@@ -335,8 +347,24 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
 			queue->fileio.blocks[i] = block;
 		}
 
-		block->state = IIO_BLOCK_STATE_QUEUED;
-		list_add_tail(&block->head, &queue->incoming);
+		/*
+		 * block->bytes_used may have been modified previously, e.g. by
+		 * iio_dma_buffer_block_list_abort(). Reset it here to the
+		 * block's so that iio_dma_buffer_io() will work.
+		 */
+		block->bytes_used = block->size;
+
+		/*
+		 * If it's an input buffer, mark the block as queued, and
+		 * iio_dma_buffer_enable() will submit it. Otherwise mark it as
+		 * done, which means it's ready to be dequeued.
+		 */
+		if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
+			block->state = IIO_BLOCK_STATE_QUEUED;
+			list_add_tail(&block->head, &queue->incoming);
+		} else {
+			block->state = IIO_BLOCK_STATE_DONE;
+		}
 	}
 
 out_unlock:
@@ -488,20 +516,12 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
 	return block;
 }
 
-/**
- * iio_dma_buffer_read() - DMA buffer read callback
- * @buffer: Buffer to read form
- * @n: Number of bytes to read
- * @user_buffer: Userspace buffer to copy the data to
- *
- * Should be used as the read callback for iio_buffer_access_ops
- * struct for DMA buffers.
- */
-int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
-	char __user *user_buffer)
+static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
+			     char __user *user_buffer, bool is_from_user)
 {
 	struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
 	struct iio_dma_buffer_block *block;
+	void *addr;
 	int ret;
 
 	if (n < buffer->bytes_per_datum)
@@ -524,8 +544,13 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 	n = rounddown(n, buffer->bytes_per_datum);
 	if (n > block->bytes_used - queue->fileio.pos)
 		n = block->bytes_used - queue->fileio.pos;
+	addr = block->vaddr + queue->fileio.pos;
 
-	if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) {
+	if (is_from_user)
+		ret = copy_from_user(addr, user_buffer, n);
+	else
+		ret = copy_to_user(user_buffer, addr, n);
+	if (ret) {
 		ret = -EFAULT;
 		goto out_unlock;
 	}
@@ -544,8 +569,40 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 
 	return ret;
 }
+
+/**
+ * iio_dma_buffer_read() - DMA buffer read callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data to
+ *
+ * Should be used as the read callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
+	char __user *user_buffer)
+{
+	return iio_dma_buffer_io(buffer, n, user_buffer, false);
+}
 EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
 
+/**
+ * iio_dma_buffer_write() - DMA buffer write callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data from
+ *
+ * Should be used as the write callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
+			 const char __user *user_buffer)
+{
+	return iio_dma_buffer_io(buffer, n,
+				 (__force __user char *)user_buffer, true);
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_write);
+
 /**
  * iio_dma_buffer_usage() - DMA buffer data_available and
  * space_available callback
diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
index 52a838ec0e57..6e27e47077d5 100644
--- a/include/linux/iio/buffer-dma.h
+++ b/include/linux/iio/buffer-dma.h
@@ -132,6 +132,8 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer,
 	struct iio_dev *indio_dev);
 int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 	char __user *user_buffer);
+int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
+			 const char __user *user_buffer);
 size_t iio_dma_buffer_usage(struct iio_buffer *buffer);
 int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
 int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length);

-- 
2.44.0



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

* [PATCH 03/10] iio: buffer-dma: Enable buffer write support
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Paul Cercueil, Alexandru Ardelean,
	Nuno Sa

From: Paul Cercueil <paul@crapouillou.net>

Adding write support to the buffer-dma code is easy - the write()
function basically needs to do the exact same thing as the read()
function: dequeue a block, read or write the data, enqueue the block
when entirely processed.

Therefore, the iio_buffer_dma_read() and the new iio_buffer_dma_write()
now both call a function iio_buffer_dma_io(), which will perform this
task.

Note that we preemptively reset block->bytes_used to the buffer's size
in iio_dma_buffer_request_update(), as in the future the
iio_dma_buffer_enqueue() function won't reset it.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dma.c | 89 +++++++++++++++++++++++-----
 include/linux/iio/buffer-dma.h               |  2 +
 2 files changed, 75 insertions(+), 16 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 404f9867bdc5..29cc3083cb7e 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -195,6 +195,18 @@ static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
 		block->state = IIO_BLOCK_STATE_DONE;
 }
 
+static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue)
+{
+	__poll_t flags;
+
+	if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN)
+		flags = EPOLLIN | EPOLLRDNORM;
+	else
+		flags = EPOLLOUT | EPOLLWRNORM;
+
+	wake_up_interruptible_poll(&queue->buffer.pollq, flags);
+}
+
 /**
  * iio_dma_buffer_block_done() - Indicate that a block has been completed
  * @block: The completed block
@@ -212,7 +224,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
 	spin_unlock_irqrestore(&queue->list_lock, flags);
 
 	iio_buffer_block_put_atomic(block);
-	wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
+	iio_dma_buffer_queue_wake(queue);
 }
 EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done);
 
@@ -241,7 +253,7 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
 	}
 	spin_unlock_irqrestore(&queue->list_lock, flags);
 
-	wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
+	iio_dma_buffer_queue_wake(queue);
 }
 EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort);
 
@@ -335,8 +347,24 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
 			queue->fileio.blocks[i] = block;
 		}
 
-		block->state = IIO_BLOCK_STATE_QUEUED;
-		list_add_tail(&block->head, &queue->incoming);
+		/*
+		 * block->bytes_used may have been modified previously, e.g. by
+		 * iio_dma_buffer_block_list_abort(). Reset it here to the
+		 * block's so that iio_dma_buffer_io() will work.
+		 */
+		block->bytes_used = block->size;
+
+		/*
+		 * If it's an input buffer, mark the block as queued, and
+		 * iio_dma_buffer_enable() will submit it. Otherwise mark it as
+		 * done, which means it's ready to be dequeued.
+		 */
+		if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
+			block->state = IIO_BLOCK_STATE_QUEUED;
+			list_add_tail(&block->head, &queue->incoming);
+		} else {
+			block->state = IIO_BLOCK_STATE_DONE;
+		}
 	}
 
 out_unlock:
@@ -488,20 +516,12 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
 	return block;
 }
 
-/**
- * iio_dma_buffer_read() - DMA buffer read callback
- * @buffer: Buffer to read form
- * @n: Number of bytes to read
- * @user_buffer: Userspace buffer to copy the data to
- *
- * Should be used as the read callback for iio_buffer_access_ops
- * struct for DMA buffers.
- */
-int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
-	char __user *user_buffer)
+static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
+			     char __user *user_buffer, bool is_from_user)
 {
 	struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
 	struct iio_dma_buffer_block *block;
+	void *addr;
 	int ret;
 
 	if (n < buffer->bytes_per_datum)
@@ -524,8 +544,13 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 	n = rounddown(n, buffer->bytes_per_datum);
 	if (n > block->bytes_used - queue->fileio.pos)
 		n = block->bytes_used - queue->fileio.pos;
+	addr = block->vaddr + queue->fileio.pos;
 
-	if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) {
+	if (is_from_user)
+		ret = copy_from_user(addr, user_buffer, n);
+	else
+		ret = copy_to_user(user_buffer, addr, n);
+	if (ret) {
 		ret = -EFAULT;
 		goto out_unlock;
 	}
@@ -544,8 +569,40 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 
 	return ret;
 }
+
+/**
+ * iio_dma_buffer_read() - DMA buffer read callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data to
+ *
+ * Should be used as the read callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
+	char __user *user_buffer)
+{
+	return iio_dma_buffer_io(buffer, n, user_buffer, false);
+}
 EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
 
+/**
+ * iio_dma_buffer_write() - DMA buffer write callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data from
+ *
+ * Should be used as the write callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
+			 const char __user *user_buffer)
+{
+	return iio_dma_buffer_io(buffer, n,
+				 (__force __user char *)user_buffer, true);
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_write);
+
 /**
  * iio_dma_buffer_usage() - DMA buffer data_available and
  * space_available callback
diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
index 52a838ec0e57..6e27e47077d5 100644
--- a/include/linux/iio/buffer-dma.h
+++ b/include/linux/iio/buffer-dma.h
@@ -132,6 +132,8 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer,
 	struct iio_dev *indio_dev);
 int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
 	char __user *user_buffer);
+int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
+			 const char __user *user_buffer);
 size_t iio_dma_buffer_usage(struct iio_buffer *buffer);
 int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
 int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length);

-- 
2.44.0


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

* [PATCH 04/10] iio: buffer-dmaengine: Support specifying buffer direction
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Paul Cercueil, Alexandru Ardelean,
	Nuno Sa

From: Paul Cercueil <paul@crapouillou.net>

Update the devm_iio_dmaengine_buffer_setup() function to support
specifying the buffer direction.

Update the iio_dmaengine_buffer_submit() function to handle input
buffers as well as output buffers.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dmaengine.c | 24 +++++++++++++++++-----
 include/linux/iio/buffer-dmaengine.h               |  6 +++++-
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index ccf6e0b19019..d9d72f7cba60 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -64,14 +64,25 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
 	struct dmaengine_buffer *dmaengine_buffer =
 		iio_buffer_to_dmaengine_buffer(&queue->buffer);
 	struct dma_async_tx_descriptor *desc;
+	enum dma_transfer_direction dma_dir;
+	size_t max_size;
 	dma_cookie_t cookie;
 
-	block->bytes_used = min(block->size, dmaengine_buffer->max_size);
-	block->bytes_used = round_down(block->bytes_used,
-			dmaengine_buffer->align);
+	max_size = min(block->size, dmaengine_buffer->max_size);
+	max_size = round_down(max_size, dmaengine_buffer->align);
+
+	if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
+		block->bytes_used = max_size;
+		dma_dir = DMA_DEV_TO_MEM;
+	} else {
+		dma_dir = DMA_MEM_TO_DEV;
+	}
+
+	if (!block->bytes_used || block->bytes_used > max_size)
+		return -EINVAL;
 
 	desc = dmaengine_prep_slave_single(dmaengine_buffer->chan,
-		block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM,
+		block->phys_addr, block->bytes_used, dma_dir,
 		DMA_PREP_INTERRUPT);
 	if (!desc)
 		return -ENOMEM;
@@ -277,7 +288,8 @@ static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
  */
 int devm_iio_dmaengine_buffer_setup(struct device *dev,
 				    struct iio_dev *indio_dev,
-				    const char *channel)
+				    const char *channel,
+				    enum iio_buffer_direction dir)
 {
 	struct iio_buffer *buffer;
 
@@ -287,6 +299,8 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev,
 
 	indio_dev->modes |= INDIO_BUFFER_HARDWARE;
 
+	buffer->direction = dir;
+
 	return iio_device_attach_buffer(indio_dev, buffer);
 }
 EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER);
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h
index cbb8ba957fad..f0d750ce2880 100644
--- a/include/linux/iio/buffer-dmaengine.h
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -7,6 +7,8 @@
 #ifndef __IIO_DMAENGINE_H__
 #define __IIO_DMAENGINE_H__
 
+#include <linux/iio/buffer.h>
+
 struct iio_dev;
 struct device;
 
@@ -15,6 +17,8 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
 void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
 int devm_iio_dmaengine_buffer_setup(struct device *dev,
 				    struct iio_dev *indio_dev,
-				    const char *channel);
+				    const char *channel,
+				    enum iio_buffer_direction dir);
+
 
 #endif

-- 
2.44.0



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

* [PATCH 04/10] iio: buffer-dmaengine: Support specifying buffer direction
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Paul Cercueil, Alexandru Ardelean,
	Nuno Sa

From: Paul Cercueil <paul@crapouillou.net>

Update the devm_iio_dmaengine_buffer_setup() function to support
specifying the buffer direction.

Update the iio_dmaengine_buffer_submit() function to handle input
buffers as well as output buffers.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dmaengine.c | 24 +++++++++++++++++-----
 include/linux/iio/buffer-dmaengine.h               |  6 +++++-
 2 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index ccf6e0b19019..d9d72f7cba60 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -64,14 +64,25 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
 	struct dmaengine_buffer *dmaengine_buffer =
 		iio_buffer_to_dmaengine_buffer(&queue->buffer);
 	struct dma_async_tx_descriptor *desc;
+	enum dma_transfer_direction dma_dir;
+	size_t max_size;
 	dma_cookie_t cookie;
 
-	block->bytes_used = min(block->size, dmaengine_buffer->max_size);
-	block->bytes_used = round_down(block->bytes_used,
-			dmaengine_buffer->align);
+	max_size = min(block->size, dmaengine_buffer->max_size);
+	max_size = round_down(max_size, dmaengine_buffer->align);
+
+	if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
+		block->bytes_used = max_size;
+		dma_dir = DMA_DEV_TO_MEM;
+	} else {
+		dma_dir = DMA_MEM_TO_DEV;
+	}
+
+	if (!block->bytes_used || block->bytes_used > max_size)
+		return -EINVAL;
 
 	desc = dmaengine_prep_slave_single(dmaengine_buffer->chan,
-		block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM,
+		block->phys_addr, block->bytes_used, dma_dir,
 		DMA_PREP_INTERRUPT);
 	if (!desc)
 		return -ENOMEM;
@@ -277,7 +288,8 @@ static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
  */
 int devm_iio_dmaengine_buffer_setup(struct device *dev,
 				    struct iio_dev *indio_dev,
-				    const char *channel)
+				    const char *channel,
+				    enum iio_buffer_direction dir)
 {
 	struct iio_buffer *buffer;
 
@@ -287,6 +299,8 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev,
 
 	indio_dev->modes |= INDIO_BUFFER_HARDWARE;
 
+	buffer->direction = dir;
+
 	return iio_device_attach_buffer(indio_dev, buffer);
 }
 EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER);
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h
index cbb8ba957fad..f0d750ce2880 100644
--- a/include/linux/iio/buffer-dmaengine.h
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -7,6 +7,8 @@
 #ifndef __IIO_DMAENGINE_H__
 #define __IIO_DMAENGINE_H__
 
+#include <linux/iio/buffer.h>
+
 struct iio_dev;
 struct device;
 
@@ -15,6 +17,8 @@ struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
 void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
 int devm_iio_dmaengine_buffer_setup(struct device *dev,
 				    struct iio_dev *indio_dev,
-				    const char *channel);
+				    const char *channel,
+				    enum iio_buffer_direction dir);
+
 
 #endif

-- 
2.44.0


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

* [PATCH 05/10] iio: buffer-dmaengine: Enable write support
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Paul Cercueil, Alexandru Ardelean,
	Nuno Sa

From: Paul Cercueil <paul@crapouillou.net>

Use the iio_dma_buffer_write() and iio_dma_buffer_space_available()
functions provided by the buffer-dma core, to enable write support in
the buffer-dmaengine code.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dmaengine.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index d9d72f7cba60..e151af7e8907 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -123,12 +123,14 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
 
 static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
 	.read = iio_dma_buffer_read,
+	.write = iio_dma_buffer_write,
 	.set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
 	.set_length = iio_dma_buffer_set_length,
 	.request_update = iio_dma_buffer_request_update,
 	.enable = iio_dma_buffer_enable,
 	.disable = iio_dma_buffer_disable,
 	.data_available = iio_dma_buffer_usage,
+	.space_available = iio_dma_buffer_usage,
 	.release = iio_dmaengine_buffer_release,
 
 	.modes = INDIO_BUFFER_HARDWARE,

-- 
2.44.0



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

* [PATCH 05/10] iio: buffer-dmaengine: Enable write support
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Paul Cercueil, Alexandru Ardelean,
	Nuno Sa

From: Paul Cercueil <paul@crapouillou.net>

Use the iio_dma_buffer_write() and iio_dma_buffer_space_available()
functions provided by the buffer-dma core, to enable write support in
the buffer-dmaengine code.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/buffer/industrialio-buffer-dmaengine.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index d9d72f7cba60..e151af7e8907 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -123,12 +123,14 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
 
 static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
 	.read = iio_dma_buffer_read,
+	.write = iio_dma_buffer_write,
 	.set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
 	.set_length = iio_dma_buffer_set_length,
 	.request_update = iio_dma_buffer_request_update,
 	.enable = iio_dma_buffer_enable,
 	.disable = iio_dma_buffer_disable,
 	.data_available = iio_dma_buffer_usage,
+	.space_available = iio_dma_buffer_usage,
 	.release = iio_dmaengine_buffer_release,
 
 	.modes = INDIO_BUFFER_HARDWARE,

-- 
2.44.0


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

* [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

From: Nuno Sa <nuno.sa@analog.com>

This adds the bindings documentation for the AXI DAC driver.

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   | 62 ++++++++++++++++++++++
 MAINTAINERS                                        |  7 +++
 2 files changed, 69 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
new file mode 100644
index 000000000000..1018fd274f04
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AXI DAC IP core
+
+maintainers:
+  - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+  Analog Devices Generic AXI DAC IP core for interfacing a DAC device
+  with a high speed serial (JESD204B/C) or source synchronous parallel
+  interface (LVDS/CMOS).
+  Usually, some other interface type (i.e SPI) is used as a control
+  interface for the actual DAC, while this IP core will interface
+  to the data-lines of the DAC and handle the streaming of data into
+  memory via DMA.
+
+  https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+
+properties:
+  compatible:
+    enum:
+      - adi,axi-dac-9.1.b
+
+  reg:
+    maxItems: 1
+
+  dmas:
+    maxItems: 1
+
+  dma-names:
+    items:
+      - const: tx
+
+  clocks:
+    maxItems: 1
+
+  '#io-backend-cells':
+    const: 0
+
+required:
+  - compatible
+  - dmas
+  - reg
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    dac@44a00000 {
+        compatible = "adi,axi-dac-9.1.b";
+        reg = <0x44a00000 0x10000>;
+        dmas = <&tx_dma 0>;
+        dma-names = "tx";
+        #io-backend-cells = <0>;
+        clocks = <&axi_clk>;
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index a7287cf44869..2137eb452376 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1399,6 +1399,13 @@ F:	sound/soc/codecs/adav*
 F:	sound/soc/codecs/sigmadsp.*
 F:	sound/soc/codecs/ssm*
 
+ANALOG DEVICES INC AXI DAC DRIVER
+M:	Nuno Sa <nuno.sa@analog.com>
+L:	linux-iio@vger.kernel.org
+S:	Supported
+W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+
 ANALOG DEVICES INC DMA DRIVERS
 M:	Lars-Peter Clausen <lars@metafoo.de>
 S:	Supported

-- 
2.44.0



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

* [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

This adds the bindings documentation for the AXI DAC driver.

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   | 62 ++++++++++++++++++++++
 MAINTAINERS                                        |  7 +++
 2 files changed, 69 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
new file mode 100644
index 000000000000..1018fd274f04
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AXI DAC IP core
+
+maintainers:
+  - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+  Analog Devices Generic AXI DAC IP core for interfacing a DAC device
+  with a high speed serial (JESD204B/C) or source synchronous parallel
+  interface (LVDS/CMOS).
+  Usually, some other interface type (i.e SPI) is used as a control
+  interface for the actual DAC, while this IP core will interface
+  to the data-lines of the DAC and handle the streaming of data into
+  memory via DMA.
+
+  https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+
+properties:
+  compatible:
+    enum:
+      - adi,axi-dac-9.1.b
+
+  reg:
+    maxItems: 1
+
+  dmas:
+    maxItems: 1
+
+  dma-names:
+    items:
+      - const: tx
+
+  clocks:
+    maxItems: 1
+
+  '#io-backend-cells':
+    const: 0
+
+required:
+  - compatible
+  - dmas
+  - reg
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    dac@44a00000 {
+        compatible = "adi,axi-dac-9.1.b";
+        reg = <0x44a00000 0x10000>;
+        dmas = <&tx_dma 0>;
+        dma-names = "tx";
+        #io-backend-cells = <0>;
+        clocks = <&axi_clk>;
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index a7287cf44869..2137eb452376 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1399,6 +1399,13 @@ F:	sound/soc/codecs/adav*
 F:	sound/soc/codecs/sigmadsp.*
 F:	sound/soc/codecs/ssm*
 
+ANALOG DEVICES INC AXI DAC DRIVER
+M:	Nuno Sa <nuno.sa@analog.com>
+L:	linux-iio@vger.kernel.org
+S:	Supported
+W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+
 ANALOG DEVICES INC DMA DRIVERS
 M:	Lars-Peter Clausen <lars@metafoo.de>
 S:	Supported

-- 
2.44.0


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

* [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

From: Nuno Sa <nuno.sa@analog.com>

This adds the bindings documentation for the 14 bit
RF Digital-to-Analog converter.

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 .../devicetree/bindings/iio/dac/adi,ad9739a.yaml   | 88 ++++++++++++++++++++++
 MAINTAINERS                                        |  8 ++
 2 files changed, 96 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
new file mode 100644
index 000000000000..24bcec763a9b
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD9739A RF DAC
+
+maintainers:
+  - Dragos Bogdan <dragos.bogdan@analog.com>
+  - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+  The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
+  of synthesizing wideband signals from dc up to 3 GHz.
+
+  https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
+
+properties:
+  compatible:
+    enum:
+      - adi,ad9739a
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  vdd_3_3-supply:
+    description: 3.3V Digital input supply.
+
+  vdd-supply:
+    description: 1.8V Digital input supply.
+
+  vdda-supply:
+    description: 3.3V Analog input supply.
+
+  vddc-supply:
+    description: 1.8V Clock input supply.
+
+  io-backends:
+    maxItems: 1
+
+  adi,full-scale-microamp:
+    description: This property represents the DAC full scale current.
+    minimum: 8700
+    maximum: 31700
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - io-backends
+  - vdd_3_3-supply
+  - vdd-supply
+  - vdda-supply
+  - vddc-supply
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        dac@0 {
+            compatible = "adi,ad9739a";
+            reg = <0>;
+
+            clocks = <&dac_clk>;
+
+            io-backends = <&iio_backend>;
+
+            vdd_3_3-supply = <&vdd_3_3>;
+            vdd-supply = <&vdd>;
+            vdda-supply = <&vdd_3_3>;
+            vddc-supply = <&vdd>;
+        };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 2137eb452376..76e872e320d7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1234,6 +1234,14 @@ W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
 F:	drivers/iio/adc/ad7780.c
 
+ANALOG DEVICES INC AD9739a DRIVER
+M:	Nuno Sa <nuno.sa@analog.com>
+M:	Dragos Bogdan <dragos.bogdan@analog.com>
+L:	linux-iio@vger.kernel.org
+S:	Supported
+W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
+
 ANALOG DEVICES INC ADA4250 DRIVER
 M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
 L:	linux-iio@vger.kernel.org

-- 
2.44.0



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

* [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

This adds the bindings documentation for the 14 bit
RF Digital-to-Analog converter.

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 .../devicetree/bindings/iio/dac/adi,ad9739a.yaml   | 88 ++++++++++++++++++++++
 MAINTAINERS                                        |  8 ++
 2 files changed, 96 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
new file mode 100644
index 000000000000..24bcec763a9b
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD9739A RF DAC
+
+maintainers:
+  - Dragos Bogdan <dragos.bogdan@analog.com>
+  - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+  The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
+  of synthesizing wideband signals from dc up to 3 GHz.
+
+  https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
+
+properties:
+  compatible:
+    enum:
+      - adi,ad9739a
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+
+  vdd_3_3-supply:
+    description: 3.3V Digital input supply.
+
+  vdd-supply:
+    description: 1.8V Digital input supply.
+
+  vdda-supply:
+    description: 3.3V Analog input supply.
+
+  vddc-supply:
+    description: 1.8V Clock input supply.
+
+  io-backends:
+    maxItems: 1
+
+  adi,full-scale-microamp:
+    description: This property represents the DAC full scale current.
+    minimum: 8700
+    maximum: 31700
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - io-backends
+  - vdd_3_3-supply
+  - vdd-supply
+  - vdda-supply
+  - vddc-supply
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        dac@0 {
+            compatible = "adi,ad9739a";
+            reg = <0>;
+
+            clocks = <&dac_clk>;
+
+            io-backends = <&iio_backend>;
+
+            vdd_3_3-supply = <&vdd_3_3>;
+            vdd-supply = <&vdd>;
+            vdda-supply = <&vdd_3_3>;
+            vddc-supply = <&vdd>;
+        };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 2137eb452376..76e872e320d7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1234,6 +1234,14 @@ W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
 F:	drivers/iio/adc/ad7780.c
 
+ANALOG DEVICES INC AD9739a DRIVER
+M:	Nuno Sa <nuno.sa@analog.com>
+M:	Dragos Bogdan <dragos.bogdan@analog.com>
+L:	linux-iio@vger.kernel.org
+S:	Supported
+W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
+
 ANALOG DEVICES INC ADA4250 DRIVER
 M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
 L:	linux-iio@vger.kernel.org

-- 
2.44.0


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

* [PATCH 08/10] iio: backend: add new functionality
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

From: Nuno Sa <nuno.sa@analog.com>

This adds the needed backend ops for supporting a backend inerfacing
with an high speed dac. The new ops are:

* data_source_set();
* set_sampling_freq();
* extend_chan_spec();
* ext_info_set();
* ext_info_get().

Also to note the new helpers that are meant to be used by the backends
when extending an IIO channel (adding extended info):

* iio_backend_ext_info_set();
* iio_backend_ext_info_get().

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/industrialio-backend.c | 144 +++++++++++++++++++++++++++++++++++++
 include/linux/iio/backend.h        |  49 +++++++++++++
 2 files changed, 193 insertions(+)

diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 2fea2bbbe47f..0a26dd8c6343 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -43,6 +43,7 @@
 #include <linux/types.h>
 
 #include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
 
 struct iio_backend {
 	struct list_head entry;
@@ -186,6 +187,44 @@ int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
 }
 EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND);
 
+/**
+ * iio_backend_data_source_set - Select data source
+ * @back:	Backend device
+ * @chan:	Channel number
+ * @data:	Data source
+ *
+ * A given backend may have different sources to stream/sync data. This allows
+ * to choose that source.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+				enum iio_backend_data_source data)
+{
+	if (data >= IIO_BACKEND_DATA_SOURCE_MAX)
+		return -EINVAL;
+
+	return iio_backend_op_call(back, data_source_set, chan, data);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND);
+
+/**
+ * iio_backend_set_sampling_freq - Set channel sampling rate
+ * @back:		Backend device
+ * @chan:		Channel number
+ * @sample_rate:	Sample rate
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+				  u64 sample_rate)
+{
+	return iio_backend_op_call(back, set_sample_rate, chan, sample_rate);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND);
+
 static void iio_backend_free_buffer(void *arg)
 {
 	struct iio_backend_buffer_pair *pair = arg;
@@ -231,6 +270,111 @@ int devm_iio_backend_request_buffer(struct device *dev,
 }
 EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
 
+/**
+ * iio_backend_ext_info_get - IIO ext_info read callback
+ * @indio_dev:	IIO device
+ * @private:	Data private to the driver
+ * @chan:	IIO channel
+ * @buf:	Buffer where to place the attribute data
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not
+ * a way to get te backend from indio_dev). This is the getter.
+ *
+ * RETURNS:
+ * Number of bytes written to buf, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_backend *back = iio_device_get_drvdata(indio_dev);
+
+	return iio_backend_op_call(back, ext_info_get, private, chan, buf);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND);
+
+/**
+ * iio_backend_ext_info_set - IIO ext_info write callback
+ * @indio_dev:	IIO device
+ * @private:	Data private to the driver
+ * @chan:	IIO channel
+ * @buf:	Buffer holding the sysfs attribute
+ * @len:	Buffer length
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not
+ * a way to get te backend from indio_dev). This is the setter.
+ *
+ * RETURNS:
+ * Buffer length on success, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan,
+				 const char *buf, size_t len)
+{
+	struct iio_backend *back = iio_device_get_drvdata(indio_dev);
+
+	return iio_backend_op_call(back, ext_info_set, private, chan, buf, len);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND);
+
+/**
+ * iio_backend_extend_chan_spec - Extend an IIO channel
+ * @indio_dev:	IIO device
+ * @back:	Backend device
+ * @chan:	IIO channel
+ *
+ * Some backends may have their own functionalities and hence capable of
+ * extending a frontend's channel.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+				 struct iio_backend *back,
+				 struct iio_chan_spec *chan)
+{
+	const struct iio_chan_spec_ext_info *ext_info = chan->ext_info;
+	int ret;
+
+	ret = iio_backend_op_call(back, extend_chan_spec, chan);
+	if (ret)
+		return ret;
+	/*
+	 * Let's keep things simple for now. Don't allow to overwrite the
+	 * frontend's extended info. If ever needed, we can support appending
+	 * it.
+	 */
+	if (ext_info && chan->ext_info != ext_info)
+		return -EOPNOTSUPP;
+	if (!chan->ext_info)
+		return 0;
+	/*
+	 * !\NOTE: this will break as soon as we have multiple backends on one
+	 * frontend and all of them extend channels. In that case, the core
+	 * backend code has no way to get the correct backend given the
+	 * iio device.
+	 *
+	 * One solution for this could be introducing a new backend
+	 * dedicated callback in struct iio_info so we can callback into the
+	 * frontend so it can give us the right backend given a chan_spec.
+	 */
+	iio_device_set_drvdata(indio_dev, back);
+
+	/* Don't allow backends to get creative and force their own handlers */
+	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
+		if (ext_info->read != iio_backend_ext_info_get)
+			return -EINVAL;
+		if (ext_info->write != iio_backend_ext_info_set)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
+
 static void iio_backend_release(void *arg)
 {
 	struct iio_backend *back = arg;
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
index a6d79381866e..09ff2f8f9fd8 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -4,6 +4,7 @@
 
 #include <linux/types.h>
 
+struct iio_chan_spec;
 struct fwnode_handle;
 struct iio_backend;
 struct device;
@@ -15,6 +16,26 @@ enum iio_backend_data_type {
 	IIO_BACKEND_DATA_TYPE_MAX
 };
 
+enum iio_backend_data_source {
+	IIO_BACKEND_INTERNAL_CW,
+	IIO_BACKEND_EXTERNAL,
+	IIO_BACKEND_DATA_SOURCE_MAX
+};
+
+/**
+ * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
+ * @_name:	Attribute name
+ * @_shared:	Whether the attribute is shared between all channels
+ * @_what:	Data private to the driver
+ */
+#define IIO_BACKEND_EX_INFO(_name, _shared, _what) {	\
+	.name = (_name),				\
+	.shared = (_shared),				\
+	.read =  iio_backend_ext_info_get,		\
+	.write = iio_backend_ext_info_set,		\
+	.private = (_what),				\
+}
+
 /**
  * struct iio_backend_data_fmt - Backend data format
  * @type:		Data type.
@@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
  * @chan_enable:	Enable one channel.
  * @chan_disable:	Disable one channel.
  * @data_format_set:	Configure the data format for a specific channel.
+ * @data_source_set:	Configure the data source for a specific channel.
+ * @set_sample_rate:	Configure the sampling rate for a specific channel.
  * @request_buffer:	Request an IIO buffer.
  * @free_buffer:	Free an IIO buffer.
+ * @extend_chan_spec:	Extend an IIO channel.
+ * @ext_info_set:	Extended info setter.
+ * @ext_info_get:	Extended info getter.
  **/
 struct iio_backend_ops {
 	int (*enable)(struct iio_backend *back);
@@ -45,10 +71,21 @@ struct iio_backend_ops {
 	int (*chan_disable)(struct iio_backend *back, unsigned int chan);
 	int (*data_format_set)(struct iio_backend *back, unsigned int chan,
 			       const struct iio_backend_data_fmt *data);
+	int (*data_source_set)(struct iio_backend *back, unsigned int chan,
+			       enum iio_backend_data_source data);
+	int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
+			       u64 sample_rate);
 	struct iio_buffer *(*request_buffer)(struct iio_backend *back,
 					     struct iio_dev *indio_dev);
 	void (*free_buffer)(struct iio_backend *back,
 			    struct iio_buffer *buffer);
+	int (*extend_chan_spec)(struct iio_backend *back,
+				struct iio_chan_spec *chan);
+	int (*ext_info_set)(struct iio_backend *back, uintptr_t private,
+			    const struct iio_chan_spec *chan,
+			    const char *buf, size_t len);
+	int (*ext_info_get)(struct iio_backend *back, uintptr_t private,
+			    const struct iio_chan_spec *chan, char *buf);
 };
 
 int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan);
@@ -56,10 +93,22 @@ int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan);
 int devm_iio_backend_enable(struct device *dev, struct iio_backend *back);
 int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
 				const struct iio_backend_data_fmt *data);
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+				enum iio_backend_data_source data);
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+				  u64 sample_rate);
 int devm_iio_backend_request_buffer(struct device *dev,
 				    struct iio_backend *back,
 				    struct iio_dev *indio_dev);
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan,
+				 const char *buf, size_t len);
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan, char *buf);
 
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+				 struct iio_backend *back,
+				 struct iio_chan_spec *chan);
 void *iio_backend_get_priv(const struct iio_backend *conv);
 struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name);
 struct iio_backend *

-- 
2.44.0



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

* [PATCH 08/10] iio: backend: add new functionality
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

This adds the needed backend ops for supporting a backend inerfacing
with an high speed dac. The new ops are:

* data_source_set();
* set_sampling_freq();
* extend_chan_spec();
* ext_info_set();
* ext_info_get().

Also to note the new helpers that are meant to be used by the backends
when extending an IIO channel (adding extended info):

* iio_backend_ext_info_set();
* iio_backend_ext_info_get().

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 drivers/iio/industrialio-backend.c | 144 +++++++++++++++++++++++++++++++++++++
 include/linux/iio/backend.h        |  49 +++++++++++++
 2 files changed, 193 insertions(+)

diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 2fea2bbbe47f..0a26dd8c6343 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -43,6 +43,7 @@
 #include <linux/types.h>
 
 #include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
 
 struct iio_backend {
 	struct list_head entry;
@@ -186,6 +187,44 @@ int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
 }
 EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND);
 
+/**
+ * iio_backend_data_source_set - Select data source
+ * @back:	Backend device
+ * @chan:	Channel number
+ * @data:	Data source
+ *
+ * A given backend may have different sources to stream/sync data. This allows
+ * to choose that source.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+				enum iio_backend_data_source data)
+{
+	if (data >= IIO_BACKEND_DATA_SOURCE_MAX)
+		return -EINVAL;
+
+	return iio_backend_op_call(back, data_source_set, chan, data);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND);
+
+/**
+ * iio_backend_set_sampling_freq - Set channel sampling rate
+ * @back:		Backend device
+ * @chan:		Channel number
+ * @sample_rate:	Sample rate
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+				  u64 sample_rate)
+{
+	return iio_backend_op_call(back, set_sample_rate, chan, sample_rate);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND);
+
 static void iio_backend_free_buffer(void *arg)
 {
 	struct iio_backend_buffer_pair *pair = arg;
@@ -231,6 +270,111 @@ int devm_iio_backend_request_buffer(struct device *dev,
 }
 EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
 
+/**
+ * iio_backend_ext_info_get - IIO ext_info read callback
+ * @indio_dev:	IIO device
+ * @private:	Data private to the driver
+ * @chan:	IIO channel
+ * @buf:	Buffer where to place the attribute data
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not
+ * a way to get te backend from indio_dev). This is the getter.
+ *
+ * RETURNS:
+ * Number of bytes written to buf, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_backend *back = iio_device_get_drvdata(indio_dev);
+
+	return iio_backend_op_call(back, ext_info_get, private, chan, buf);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND);
+
+/**
+ * iio_backend_ext_info_set - IIO ext_info write callback
+ * @indio_dev:	IIO device
+ * @private:	Data private to the driver
+ * @chan:	IIO channel
+ * @buf:	Buffer holding the sysfs attribute
+ * @len:	Buffer length
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not
+ * a way to get te backend from indio_dev). This is the setter.
+ *
+ * RETURNS:
+ * Buffer length on success, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan,
+				 const char *buf, size_t len)
+{
+	struct iio_backend *back = iio_device_get_drvdata(indio_dev);
+
+	return iio_backend_op_call(back, ext_info_set, private, chan, buf, len);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND);
+
+/**
+ * iio_backend_extend_chan_spec - Extend an IIO channel
+ * @indio_dev:	IIO device
+ * @back:	Backend device
+ * @chan:	IIO channel
+ *
+ * Some backends may have their own functionalities and hence capable of
+ * extending a frontend's channel.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+				 struct iio_backend *back,
+				 struct iio_chan_spec *chan)
+{
+	const struct iio_chan_spec_ext_info *ext_info = chan->ext_info;
+	int ret;
+
+	ret = iio_backend_op_call(back, extend_chan_spec, chan);
+	if (ret)
+		return ret;
+	/*
+	 * Let's keep things simple for now. Don't allow to overwrite the
+	 * frontend's extended info. If ever needed, we can support appending
+	 * it.
+	 */
+	if (ext_info && chan->ext_info != ext_info)
+		return -EOPNOTSUPP;
+	if (!chan->ext_info)
+		return 0;
+	/*
+	 * !\NOTE: this will break as soon as we have multiple backends on one
+	 * frontend and all of them extend channels. In that case, the core
+	 * backend code has no way to get the correct backend given the
+	 * iio device.
+	 *
+	 * One solution for this could be introducing a new backend
+	 * dedicated callback in struct iio_info so we can callback into the
+	 * frontend so it can give us the right backend given a chan_spec.
+	 */
+	iio_device_set_drvdata(indio_dev, back);
+
+	/* Don't allow backends to get creative and force their own handlers */
+	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
+		if (ext_info->read != iio_backend_ext_info_get)
+			return -EINVAL;
+		if (ext_info->write != iio_backend_ext_info_set)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
+
 static void iio_backend_release(void *arg)
 {
 	struct iio_backend *back = arg;
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
index a6d79381866e..09ff2f8f9fd8 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -4,6 +4,7 @@
 
 #include <linux/types.h>
 
+struct iio_chan_spec;
 struct fwnode_handle;
 struct iio_backend;
 struct device;
@@ -15,6 +16,26 @@ enum iio_backend_data_type {
 	IIO_BACKEND_DATA_TYPE_MAX
 };
 
+enum iio_backend_data_source {
+	IIO_BACKEND_INTERNAL_CW,
+	IIO_BACKEND_EXTERNAL,
+	IIO_BACKEND_DATA_SOURCE_MAX
+};
+
+/**
+ * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
+ * @_name:	Attribute name
+ * @_shared:	Whether the attribute is shared between all channels
+ * @_what:	Data private to the driver
+ */
+#define IIO_BACKEND_EX_INFO(_name, _shared, _what) {	\
+	.name = (_name),				\
+	.shared = (_shared),				\
+	.read =  iio_backend_ext_info_get,		\
+	.write = iio_backend_ext_info_set,		\
+	.private = (_what),				\
+}
+
 /**
  * struct iio_backend_data_fmt - Backend data format
  * @type:		Data type.
@@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
  * @chan_enable:	Enable one channel.
  * @chan_disable:	Disable one channel.
  * @data_format_set:	Configure the data format for a specific channel.
+ * @data_source_set:	Configure the data source for a specific channel.
+ * @set_sample_rate:	Configure the sampling rate for a specific channel.
  * @request_buffer:	Request an IIO buffer.
  * @free_buffer:	Free an IIO buffer.
+ * @extend_chan_spec:	Extend an IIO channel.
+ * @ext_info_set:	Extended info setter.
+ * @ext_info_get:	Extended info getter.
  **/
 struct iio_backend_ops {
 	int (*enable)(struct iio_backend *back);
@@ -45,10 +71,21 @@ struct iio_backend_ops {
 	int (*chan_disable)(struct iio_backend *back, unsigned int chan);
 	int (*data_format_set)(struct iio_backend *back, unsigned int chan,
 			       const struct iio_backend_data_fmt *data);
+	int (*data_source_set)(struct iio_backend *back, unsigned int chan,
+			       enum iio_backend_data_source data);
+	int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
+			       u64 sample_rate);
 	struct iio_buffer *(*request_buffer)(struct iio_backend *back,
 					     struct iio_dev *indio_dev);
 	void (*free_buffer)(struct iio_backend *back,
 			    struct iio_buffer *buffer);
+	int (*extend_chan_spec)(struct iio_backend *back,
+				struct iio_chan_spec *chan);
+	int (*ext_info_set)(struct iio_backend *back, uintptr_t private,
+			    const struct iio_chan_spec *chan,
+			    const char *buf, size_t len);
+	int (*ext_info_get)(struct iio_backend *back, uintptr_t private,
+			    const struct iio_chan_spec *chan, char *buf);
 };
 
 int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan);
@@ -56,10 +93,22 @@ int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan);
 int devm_iio_backend_enable(struct device *dev, struct iio_backend *back);
 int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
 				const struct iio_backend_data_fmt *data);
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+				enum iio_backend_data_source data);
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+				  u64 sample_rate);
 int devm_iio_backend_request_buffer(struct device *dev,
 				    struct iio_backend *back,
 				    struct iio_dev *indio_dev);
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan,
+				 const char *buf, size_t len);
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+				 const struct iio_chan_spec *chan, char *buf);
 
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+				 struct iio_backend *back,
+				 struct iio_chan_spec *chan);
 void *iio_backend_get_priv(const struct iio_backend *conv);
 struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name);
 struct iio_backend *

-- 
2.44.0


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

* [PATCH 09/10] iio: dac: add support for AXI DAC IP core
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

From: Nuno Sa <nuno.sa@analog.com>

Support the Analog Devices Generic AXI DAC IP core. The IP core is used
for interfacing with digital-to-analog (DAC) converters that require either
a high-speed serial interface (JESD204B/C) or a source synchronous parallel
interface (LVDS/CMOS). Typically (for such devices) SPI will be used for
configuration only, while this IP core handles the streaming of data into
memory via DMA.

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 MAINTAINERS                   |   1 +
 drivers/iio/dac/Kconfig       |  21 ++
 drivers/iio/dac/Makefile      |   1 +
 drivers/iio/dac/adi-axi-dac.c | 644 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 667 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 76e872e320d7..505f28dc6da6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1413,6 +1413,7 @@ L:	linux-iio@vger.kernel.org
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+F:	drivers/iio/dac/adi-axi-dac.c
 
 ANALOG DEVICES INC DMA DRIVERS
 M:	Lars-Peter Clausen <lars@metafoo.de>
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 34eb40bb9529..7c0a8caa9a34 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,27 @@ config AD5624R_SPI
 	  Say yes here to build support for Analog Devices AD5624R, AD5644R and
 	  AD5664R converters (DAC). This driver uses the common SPI interface.
 
+config ADI_AXI_DAC
+	tristate "Analog Devices Generic AXI DAC IP core driver"
+	select IIO_BUFFER
+	select IIO_BUFFER_DMAENGINE
+	select REGMAP_MMIO
+	select IIO_BACKEND
+	help
+	  Say yes here to build support for Analog Devices Generic
+	  AXI DAC IP core. The IP core is used for interfacing with
+	  digital-to-analog (DAC) converters that require either a high-speed
+	  serial interface (JESD204B/C) or a source synchronous parallel
+	  interface (LVDS/CMOS).
+	  Typically (for such devices) SPI will be used for configuration only,
+	  while this IP core handles the streaming of data into memory via DMA.
+
+	  Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+	  If unsure, say N (but it's safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adi-axi-dac.
+
 config LTC2688
 	tristate "Analog Devices LTC2688 DAC spi driver"
 	depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 55bf89739d14..6bcaa65434b2 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
 obj-$(CONFIG_AD7293) += ad7293.o
 obj-$(CONFIG_AD7303) += ad7303.o
 obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
 obj-$(CONFIG_CIO_DAC) += cio-dac.o
 obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
 obj-$(CONFIG_DS4424) += ds4424.o
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
new file mode 100644
index 000000000000..0022ecb4e4bb
--- /dev/null
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices Generic AXI DAC IP core
+ * Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ *
+ * Copyright 2016-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/limits.h>
+#include <linux/kstrtox.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#include <linux/fpga/adi-axi-common.h>
+#include <linux/iio/backend.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+
+/*
+ * Register definitions:
+ *   https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
+ */
+
+/* Base controls */
+#define AXI_DAC_REG_CONFIG		0x0c
+#define	   AXI_DDS_DISABLE		BIT(6)
+
+ /* DAC controls */
+#define AXI_DAC_REG_RSTN		0x0040
+#define   AXI_DAC_RSTN_CE_N		BIT(2)
+#define   AXI_DAC_RSTN_MMCM_RSTN	BIT(1)
+#define   AXI_DAC_RSTN_RSTN		BIT(0)
+#define AXI_DAC_REG_CNTRL_1		0x0044
+#define   AXI_DAC_SYNC			BIT(0)
+#define AXI_DAC_REG_CNTRL_2		0x0048
+#define	  ADI_DAC_R1_MODE		BIT(4)
+#define AXI_DAC_DRP_STATUS		0x0074
+#define   AXI_DAC_DRP_LOCKED		BIT(17)
+/* DAC Channel controls */
+#define AXI_DAC_REG_CHAN_CNTRL_1(c)	(0x0400 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_3(c)	(0x0408 + (c) * 0x40)
+#define   AXI_DAC_SCALE_SIGN		BIT(15)
+#define   AXI_DAC_SCALE_INT		BIT(14)
+#define   AXI_DAC_SCALE			GENMASK(14, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_2(c)	(0x0404 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_4(c)	(0x040c + (c) * 0x40)
+#define   AXI_DAC_PHASE			GENMASK(31, 16)
+#define   AXI_DAC_FREQUENCY		GENMASK(15, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_7(c)	(0x0418 + (c) * 0x40)
+#define   AXI_DAC_DATA_SEL		GENMASK(3, 0)
+
+/* 360 degrees in rad */
+#define AXI_DAC_2_PI_MEGA		6283190
+enum {
+	AXI_DAC_DATA_INTERNAL_TONE,
+	AXI_DAC_DATA_DMA = 2,
+};
+
+struct axi_dac_state {
+	struct regmap *regmap;
+	struct device *dev;
+	/*
+	 * lock to protect multiple accesses to the device registers and global
+	 * data/variables.
+	 */
+	struct mutex lock;
+	u64 dac_clk;
+	u32 reg_config;
+	bool int_tone;
+};
+
+static int axi_dac_enable(struct iio_backend *back)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+	unsigned int __val;
+	int ret;
+
+	guard(mutex)(&st->lock);
+	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+			      AXI_DAC_RSTN_MMCM_RSTN);
+	if (ret)
+		return ret;
+	/*
+	 * Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all
+	 * designs really use it but if they don't we still get the lock bit
+	 * set. So let's do it all the time so the code is generic.
+	 */
+	ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val,
+				       __val & AXI_DAC_DRP_LOCKED, 100, 1000);
+	if (ret)
+		return ret;
+
+	return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+			       AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN);
+}
+
+static void axi_dac_disable(struct iio_backend *back)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	guard(mutex)(&st->lock);
+	regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+}
+
+static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back,
+						 struct iio_dev *indio_dev)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+	struct iio_buffer *buffer;
+	const char *dma_name;
+	int ret;
+
+	if (device_property_read_string(st->dev, "dma-names", &dma_name))
+		dma_name = "tx";
+
+	buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name);
+	if (IS_ERR(buffer)) {
+		dev_err(st->dev, "Could not get DMA buffer, %ld\n",
+			PTR_ERR(buffer));
+		return ERR_CAST(buffer);
+	}
+
+	indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+	iio_buffer_set_dir(buffer, IIO_BUFFER_DIRECTION_OUT);
+
+	ret = iio_device_attach_buffer(indio_dev, buffer);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return buffer;
+}
+
+static void axi_dac_free_buffer(struct iio_backend *back,
+				struct iio_buffer *buffer)
+{
+	iio_dmaengine_buffer_free(buffer);
+}
+
+enum {
+	AXI_DAC_FREQ_TONE_1,
+	AXI_DAC_FREQ_TONE_2,
+	AXI_DAC_SCALE_TONE_1,
+	AXI_DAC_SCALE_TONE_2,
+	AXI_DAC_PHASE_TONE_1,
+	AXI_DAC_PHASE_TONE_2,
+};
+
+static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
+				   unsigned int tone, unsigned int *freq)
+{
+	u32 reg, raw;
+	int ret;
+
+	if (!st->dac_clk) {
+		dev_err(st->dev, "Sampling rate is 0...\n");
+		return -EINVAL;
+	}
+
+	if (tone == AXI_DAC_FREQ_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+
+	ret = regmap_read(st->regmap, reg, &raw);
+	if (ret)
+		return ret;
+
+	raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
+	*freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
+
+	return 0;
+}
+
+static int axi_dac_frequency_get(struct axi_dac_state *st,
+				 const struct iio_chan_spec *chan, char *buf,
+				 unsigned int tone)
+{
+	unsigned int freq;
+	int ret;
+
+	scoped_guard(mutex, &st->lock) {
+		ret = __axi_dac_frequency_get(st, chan->channel, tone, &freq);
+		if (ret)
+			return ret;
+	}
+
+	return sysfs_emit(buf, "%u\n", freq);
+}
+
+static int axi_dac_scale_get(struct axi_dac_state *st,
+			     const struct iio_chan_spec *chan, char *buf,
+			     unsigned int tone)
+{
+	unsigned int scale, sign;
+	int ret, vals[2];
+	u32 reg, raw;
+
+	if (tone == AXI_DAC_SCALE_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+
+	ret = regmap_read(st->regmap, reg, &raw);
+	if (ret)
+		return ret;
+
+	sign = FIELD_GET(AXI_DAC_SCALE_SIGN, raw);
+	raw = FIELD_GET(AXI_DAC_SCALE, raw);
+	scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, AXI_DAC_SCALE_INT);
+
+	vals[0] = scale / MEGA;
+	vals[1] = scale % MEGA;
+
+	if (sign) {
+		vals[0] *= -1;
+		if (!vals[0])
+			vals[1] *= -1;
+	}
+
+	return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+				vals);
+}
+
+static int axi_dac_phase_get(struct axi_dac_state *st,
+			     const struct iio_chan_spec *chan, char *buf,
+			     unsigned int tone)
+{
+	u32 reg, raw, phase;
+	int ret, vals[2];
+
+	if (tone == AXI_DAC_PHASE_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+
+	ret = regmap_read(st->regmap, reg, &raw);
+	if (ret)
+		return ret;
+
+	raw = FIELD_GET(AXI_DAC_PHASE, raw);
+	phase = DIV_ROUND_CLOSEST_ULL((u64)raw * AXI_DAC_2_PI_MEGA, U16_MAX);
+
+	vals[0] = phase / MEGA;
+	vals[1] = phase % MEGA;
+
+	return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+				vals);
+}
+
+static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan,
+				   u64 sample_rate, unsigned int freq,
+				   unsigned int tone)
+{
+	u32 reg;
+	u16 raw;
+	int ret;
+
+	if (!sample_rate || freq > sample_rate / 2) {
+		dev_err(st->dev, "Invalid frequency(%u) dac_clk(%llu)\n",
+			freq, sample_rate);
+		return -EINVAL;
+	}
+
+	if (tone == AXI_DAC_FREQ_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+
+	raw = DIV64_U64_ROUND_CLOSEST((u64)freq * BIT(16), sample_rate);
+
+	ret = regmap_update_bits(st->regmap,  reg, AXI_DAC_FREQUENCY, raw);
+	if (ret)
+		return ret;
+
+	/* synchronize channels */
+	return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+}
+
+static int axi_dac_frequency_set(struct axi_dac_state *st,
+				 const struct iio_chan_spec *chan,
+				 const char *buf, size_t len, unsigned int tone)
+{
+	unsigned int freq;
+	int ret;
+
+	ret = kstrtou32(buf, 10, &freq);
+	if (ret)
+		return ret;
+
+	guard(mutex)(&st->lock);
+	ret = __axi_dac_frequency_set(st, chan->channel, st->dac_clk, freq,
+				      tone);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static int axi_dac_scale_set(struct axi_dac_state *st,
+			     const struct iio_chan_spec *chan,
+			     const char *buf, size_t len, unsigned int tone)
+{
+	int integer, frac, scale;
+	u32 raw = 0, reg;
+	int ret;
+
+	ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+	if (ret)
+		return ret;
+
+	scale = integer * MEGA + frac;
+	if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
+		return -EINVAL;
+
+	/*  format is 1.1.14 (sign, integer and fractional bits) */
+	if (scale < 0) {
+		raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
+		scale *= -1;
+	}
+
+	raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
+
+	if (tone == AXI_DAC_SCALE_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+
+	guard(mutex)(&st->lock);
+	ret = regmap_write(st->regmap, reg, raw);
+	if (ret)
+		return ret;
+
+	/* synchronize channels */
+	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static int axi_dac_phase_set(struct axi_dac_state *st,
+			     const struct iio_chan_spec *chan,
+			     const char *buf, size_t len, unsigned int tone)
+{
+	int integer, frac, phase;
+	u32 raw, reg;
+	int ret;
+
+	ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+	if (ret)
+		return ret;
+
+	phase = integer * MEGA + frac;
+	if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
+		return -EINVAL;
+
+	raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
+
+	if (tone == AXI_DAC_PHASE_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+
+	guard(mutex)(&st->lock);
+	ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
+				 FIELD_PREP(AXI_DAC_PHASE, raw));
+	if (ret)
+		return ret;
+
+	/* synchronize channels */
+	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
+				const struct iio_chan_spec *chan,
+				const char *buf, size_t len)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	switch (private) {
+	case AXI_DAC_FREQ_TONE_1:
+	case AXI_DAC_FREQ_TONE_2:
+		return axi_dac_frequency_set(st, chan, buf, len, private);
+	case AXI_DAC_SCALE_TONE_1:
+	case AXI_DAC_SCALE_TONE_2:
+		return axi_dac_scale_set(st, chan, buf, len, private);
+	case AXI_DAC_PHASE_TONE_1:
+	case AXI_DAC_PHASE_TONE_2:
+		return axi_dac_phase_set(st, chan, buf, len, private);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
+				const struct iio_chan_spec *chan, char *buf)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	switch (private) {
+	case AXI_DAC_FREQ_TONE_1:
+	case AXI_DAC_FREQ_TONE_2:
+		return axi_dac_frequency_get(st, chan, buf, private);
+	case AXI_DAC_SCALE_TONE_1:
+	case AXI_DAC_SCALE_TONE_2:
+		return axi_dac_scale_get(st, chan, buf, private);
+	case AXI_DAC_PHASE_TONE_1:
+	case AXI_DAC_PHASE_TONE_2:
+		return axi_dac_phase_get(st, chan, buf, private);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
+	IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
+	IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
+	IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
+	IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
+	IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
+	IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
+	{}
+};
+
+static int axi_dac_extend_chan(struct iio_backend *back,
+			       struct iio_chan_spec *chan)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	if (chan->type != IIO_ALTVOLTAGE)
+		return -EINVAL;
+	if (st->reg_config & AXI_DDS_DISABLE)
+		/* nothing to extend */
+		return 0;
+
+	chan->ext_info = axi_dac_ext_info;
+
+	return 0;
+}
+
+static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan,
+				   enum iio_backend_data_source data)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	switch (data) {
+	case IIO_BACKEND_INTERNAL_CW:
+		return regmap_update_bits(st->regmap,
+					  AXI_DAC_REG_CHAN_CNTRL_7(chan),
+					  AXI_DAC_DATA_SEL,
+					  AXI_DAC_DATA_INTERNAL_TONE);
+	case IIO_BACKEND_EXTERNAL:
+		return regmap_update_bits(st->regmap,
+					  AXI_DAC_REG_CHAN_CNTRL_7(chan),
+					  AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
+				   u64 sample_rate)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+	unsigned int freq;
+	int ret, tone;
+
+	if (!sample_rate)
+		return -EINVAL;
+	if (st->reg_config & AXI_DDS_DISABLE)
+		/* nothing to care if DDS is disabled */
+		return 0;
+
+	guard(mutex)(&st->lock);
+	/*
+	 * If dac_clk is 0 then this must be the first time we're being notified
+	 * about the interface sample rate. Hence, just update our internal
+	 * variable and bail... If it's not 0, then we get the current DDS
+	 * frequency (for the old rate) and update the registers for the new
+	 * sample rate.
+	 */
+	if (!st->dac_clk) {
+		st->dac_clk = sample_rate;
+		return 0;
+	}
+
+	for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) {
+		ret = __axi_dac_frequency_get(st, chan, tone, &freq);
+		if (ret)
+			return ret;
+
+		ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq);
+		if (ret)
+			return ret;
+	}
+
+	st->dac_clk = sample_rate;
+
+	return 0;
+}
+
+static const struct iio_backend_ops axi_dac_generic = {
+	.enable = axi_dac_enable,
+	.disable = axi_dac_disable,
+	.request_buffer = axi_dac_request_buffer,
+	.free_buffer = axi_dac_free_buffer,
+	.extend_chan_spec = axi_dac_extend_chan,
+	.ext_info_set = axi_dac_ext_info_set,
+	.ext_info_get = axi_dac_ext_info_get,
+	.data_source_set = axi_dac_data_source_set,
+	.set_sample_rate = axi_dac_set_sample_rate,
+};
+
+static const struct regmap_config axi_dac_regmap_config = {
+	.val_bits = 32,
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.max_register = 0x0800,
+};
+
+static int axi_dac_probe(struct platform_device *pdev)
+{
+	const unsigned int *expected_ver;
+	struct axi_dac_state *st;
+	void __iomem *base;
+	unsigned int ver;
+	struct clk *clk;
+	int ret;
+
+	st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+	if (!st)
+		return -ENOMEM;
+
+	expected_ver = device_get_match_data(&pdev->dev);
+	if (!expected_ver)
+		return -ENODEV;
+
+	clk = devm_clk_get_enabled(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	st->dev = &pdev->dev;
+	st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					   &axi_dac_regmap_config);
+	if (IS_ERR(st->regmap))
+		return PTR_ERR(st->regmap);
+
+	/*
+	 * Force disable the core. Up to the frontend to enable us. And we can
+	 * still read/write registers...
+	 */
+	ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver);
+	if (ret)
+		return ret;
+
+	if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
+		dev_err(&pdev->dev,
+			"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+			ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
+			ADI_AXI_PCORE_VER_MINOR(*expected_ver),
+			ADI_AXI_PCORE_VER_PATCH(*expected_ver),
+			ADI_AXI_PCORE_VER_MAJOR(ver),
+			ADI_AXI_PCORE_VER_MINOR(ver),
+			ADI_AXI_PCORE_VER_PATCH(ver));
+		return -ENODEV;
+	}
+
+	/* Let's get the core read only configuration */
+	ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config);
+	if (ret)
+		return ret;
+
+	/*
+	 * In some designs, setting the R1_MODE bit to 0 (which is the default
+	 * value) causes all channels of the frontend to be routed to the same
+	 * DMA (so they are sampled together). This is for things like
+	 * Multiple-Input and Multiple-Output (MIMO). As most of the times we
+	 * want independent channels let's override the core's default value and
+	 * set the R1_MODE bit.
+	 */
+	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_2, ADI_DAC_R1_MODE);
+	if (ret)
+		return ret;
+
+	mutex_init(&st->lock);
+	ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st);
+	if (ret)
+		return ret;
+
+	dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
+		 ADI_AXI_PCORE_VER_MAJOR(ver),
+		 ADI_AXI_PCORE_VER_MINOR(ver),
+		 ADI_AXI_PCORE_VER_PATCH(ver));
+
+	return 0;
+}
+
+static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b');
+
+static const struct of_device_id axi_dac_of_match[] = {
+	{ .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info },
+	{}
+};
+MODULE_DEVICE_TABLE(of, axi_dac_of_match);
+
+static struct platform_driver axi_dac_driver = {
+	.driver = {
+		.name = "adi-axi-dac",
+		.of_match_table = axi_dac_of_match,
+	},
+	.probe = axi_dac_probe,
+};
+module_platform_driver(axi_dac_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices Generic AXI DAC IP core driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);
+MODULE_IMPORT_NS(IIO_BACKEND);

-- 
2.44.0



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

* [PATCH 09/10] iio: dac: add support for AXI DAC IP core
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

Support the Analog Devices Generic AXI DAC IP core. The IP core is used
for interfacing with digital-to-analog (DAC) converters that require either
a high-speed serial interface (JESD204B/C) or a source synchronous parallel
interface (LVDS/CMOS). Typically (for such devices) SPI will be used for
configuration only, while this IP core handles the streaming of data into
memory via DMA.

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 MAINTAINERS                   |   1 +
 drivers/iio/dac/Kconfig       |  21 ++
 drivers/iio/dac/Makefile      |   1 +
 drivers/iio/dac/adi-axi-dac.c | 644 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 667 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 76e872e320d7..505f28dc6da6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1413,6 +1413,7 @@ L:	linux-iio@vger.kernel.org
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+F:	drivers/iio/dac/adi-axi-dac.c
 
 ANALOG DEVICES INC DMA DRIVERS
 M:	Lars-Peter Clausen <lars@metafoo.de>
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 34eb40bb9529..7c0a8caa9a34 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,27 @@ config AD5624R_SPI
 	  Say yes here to build support for Analog Devices AD5624R, AD5644R and
 	  AD5664R converters (DAC). This driver uses the common SPI interface.
 
+config ADI_AXI_DAC
+	tristate "Analog Devices Generic AXI DAC IP core driver"
+	select IIO_BUFFER
+	select IIO_BUFFER_DMAENGINE
+	select REGMAP_MMIO
+	select IIO_BACKEND
+	help
+	  Say yes here to build support for Analog Devices Generic
+	  AXI DAC IP core. The IP core is used for interfacing with
+	  digital-to-analog (DAC) converters that require either a high-speed
+	  serial interface (JESD204B/C) or a source synchronous parallel
+	  interface (LVDS/CMOS).
+	  Typically (for such devices) SPI will be used for configuration only,
+	  while this IP core handles the streaming of data into memory via DMA.
+
+	  Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+	  If unsure, say N (but it's safe to say "Y").
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adi-axi-dac.
+
 config LTC2688
 	tristate "Analog Devices LTC2688 DAC spi driver"
 	depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 55bf89739d14..6bcaa65434b2 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
 obj-$(CONFIG_AD7293) += ad7293.o
 obj-$(CONFIG_AD7303) += ad7303.o
 obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
 obj-$(CONFIG_CIO_DAC) += cio-dac.o
 obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
 obj-$(CONFIG_DS4424) += ds4424.o
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
new file mode 100644
index 000000000000..0022ecb4e4bb
--- /dev/null
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -0,0 +1,644 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices Generic AXI DAC IP core
+ * Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ *
+ * Copyright 2016-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/limits.h>
+#include <linux/kstrtox.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#include <linux/fpga/adi-axi-common.h>
+#include <linux/iio/backend.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+
+/*
+ * Register definitions:
+ *   https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
+ */
+
+/* Base controls */
+#define AXI_DAC_REG_CONFIG		0x0c
+#define	   AXI_DDS_DISABLE		BIT(6)
+
+ /* DAC controls */
+#define AXI_DAC_REG_RSTN		0x0040
+#define   AXI_DAC_RSTN_CE_N		BIT(2)
+#define   AXI_DAC_RSTN_MMCM_RSTN	BIT(1)
+#define   AXI_DAC_RSTN_RSTN		BIT(0)
+#define AXI_DAC_REG_CNTRL_1		0x0044
+#define   AXI_DAC_SYNC			BIT(0)
+#define AXI_DAC_REG_CNTRL_2		0x0048
+#define	  ADI_DAC_R1_MODE		BIT(4)
+#define AXI_DAC_DRP_STATUS		0x0074
+#define   AXI_DAC_DRP_LOCKED		BIT(17)
+/* DAC Channel controls */
+#define AXI_DAC_REG_CHAN_CNTRL_1(c)	(0x0400 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_3(c)	(0x0408 + (c) * 0x40)
+#define   AXI_DAC_SCALE_SIGN		BIT(15)
+#define   AXI_DAC_SCALE_INT		BIT(14)
+#define   AXI_DAC_SCALE			GENMASK(14, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_2(c)	(0x0404 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_4(c)	(0x040c + (c) * 0x40)
+#define   AXI_DAC_PHASE			GENMASK(31, 16)
+#define   AXI_DAC_FREQUENCY		GENMASK(15, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_7(c)	(0x0418 + (c) * 0x40)
+#define   AXI_DAC_DATA_SEL		GENMASK(3, 0)
+
+/* 360 degrees in rad */
+#define AXI_DAC_2_PI_MEGA		6283190
+enum {
+	AXI_DAC_DATA_INTERNAL_TONE,
+	AXI_DAC_DATA_DMA = 2,
+};
+
+struct axi_dac_state {
+	struct regmap *regmap;
+	struct device *dev;
+	/*
+	 * lock to protect multiple accesses to the device registers and global
+	 * data/variables.
+	 */
+	struct mutex lock;
+	u64 dac_clk;
+	u32 reg_config;
+	bool int_tone;
+};
+
+static int axi_dac_enable(struct iio_backend *back)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+	unsigned int __val;
+	int ret;
+
+	guard(mutex)(&st->lock);
+	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+			      AXI_DAC_RSTN_MMCM_RSTN);
+	if (ret)
+		return ret;
+	/*
+	 * Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all
+	 * designs really use it but if they don't we still get the lock bit
+	 * set. So let's do it all the time so the code is generic.
+	 */
+	ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val,
+				       __val & AXI_DAC_DRP_LOCKED, 100, 1000);
+	if (ret)
+		return ret;
+
+	return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+			       AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN);
+}
+
+static void axi_dac_disable(struct iio_backend *back)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	guard(mutex)(&st->lock);
+	regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+}
+
+static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back,
+						 struct iio_dev *indio_dev)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+	struct iio_buffer *buffer;
+	const char *dma_name;
+	int ret;
+
+	if (device_property_read_string(st->dev, "dma-names", &dma_name))
+		dma_name = "tx";
+
+	buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name);
+	if (IS_ERR(buffer)) {
+		dev_err(st->dev, "Could not get DMA buffer, %ld\n",
+			PTR_ERR(buffer));
+		return ERR_CAST(buffer);
+	}
+
+	indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+	iio_buffer_set_dir(buffer, IIO_BUFFER_DIRECTION_OUT);
+
+	ret = iio_device_attach_buffer(indio_dev, buffer);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return buffer;
+}
+
+static void axi_dac_free_buffer(struct iio_backend *back,
+				struct iio_buffer *buffer)
+{
+	iio_dmaengine_buffer_free(buffer);
+}
+
+enum {
+	AXI_DAC_FREQ_TONE_1,
+	AXI_DAC_FREQ_TONE_2,
+	AXI_DAC_SCALE_TONE_1,
+	AXI_DAC_SCALE_TONE_2,
+	AXI_DAC_PHASE_TONE_1,
+	AXI_DAC_PHASE_TONE_2,
+};
+
+static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
+				   unsigned int tone, unsigned int *freq)
+{
+	u32 reg, raw;
+	int ret;
+
+	if (!st->dac_clk) {
+		dev_err(st->dev, "Sampling rate is 0...\n");
+		return -EINVAL;
+	}
+
+	if (tone == AXI_DAC_FREQ_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+
+	ret = regmap_read(st->regmap, reg, &raw);
+	if (ret)
+		return ret;
+
+	raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
+	*freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
+
+	return 0;
+}
+
+static int axi_dac_frequency_get(struct axi_dac_state *st,
+				 const struct iio_chan_spec *chan, char *buf,
+				 unsigned int tone)
+{
+	unsigned int freq;
+	int ret;
+
+	scoped_guard(mutex, &st->lock) {
+		ret = __axi_dac_frequency_get(st, chan->channel, tone, &freq);
+		if (ret)
+			return ret;
+	}
+
+	return sysfs_emit(buf, "%u\n", freq);
+}
+
+static int axi_dac_scale_get(struct axi_dac_state *st,
+			     const struct iio_chan_spec *chan, char *buf,
+			     unsigned int tone)
+{
+	unsigned int scale, sign;
+	int ret, vals[2];
+	u32 reg, raw;
+
+	if (tone == AXI_DAC_SCALE_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+
+	ret = regmap_read(st->regmap, reg, &raw);
+	if (ret)
+		return ret;
+
+	sign = FIELD_GET(AXI_DAC_SCALE_SIGN, raw);
+	raw = FIELD_GET(AXI_DAC_SCALE, raw);
+	scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, AXI_DAC_SCALE_INT);
+
+	vals[0] = scale / MEGA;
+	vals[1] = scale % MEGA;
+
+	if (sign) {
+		vals[0] *= -1;
+		if (!vals[0])
+			vals[1] *= -1;
+	}
+
+	return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+				vals);
+}
+
+static int axi_dac_phase_get(struct axi_dac_state *st,
+			     const struct iio_chan_spec *chan, char *buf,
+			     unsigned int tone)
+{
+	u32 reg, raw, phase;
+	int ret, vals[2];
+
+	if (tone == AXI_DAC_PHASE_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+
+	ret = regmap_read(st->regmap, reg, &raw);
+	if (ret)
+		return ret;
+
+	raw = FIELD_GET(AXI_DAC_PHASE, raw);
+	phase = DIV_ROUND_CLOSEST_ULL((u64)raw * AXI_DAC_2_PI_MEGA, U16_MAX);
+
+	vals[0] = phase / MEGA;
+	vals[1] = phase % MEGA;
+
+	return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+				vals);
+}
+
+static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan,
+				   u64 sample_rate, unsigned int freq,
+				   unsigned int tone)
+{
+	u32 reg;
+	u16 raw;
+	int ret;
+
+	if (!sample_rate || freq > sample_rate / 2) {
+		dev_err(st->dev, "Invalid frequency(%u) dac_clk(%llu)\n",
+			freq, sample_rate);
+		return -EINVAL;
+	}
+
+	if (tone == AXI_DAC_FREQ_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+
+	raw = DIV64_U64_ROUND_CLOSEST((u64)freq * BIT(16), sample_rate);
+
+	ret = regmap_update_bits(st->regmap,  reg, AXI_DAC_FREQUENCY, raw);
+	if (ret)
+		return ret;
+
+	/* synchronize channels */
+	return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+}
+
+static int axi_dac_frequency_set(struct axi_dac_state *st,
+				 const struct iio_chan_spec *chan,
+				 const char *buf, size_t len, unsigned int tone)
+{
+	unsigned int freq;
+	int ret;
+
+	ret = kstrtou32(buf, 10, &freq);
+	if (ret)
+		return ret;
+
+	guard(mutex)(&st->lock);
+	ret = __axi_dac_frequency_set(st, chan->channel, st->dac_clk, freq,
+				      tone);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static int axi_dac_scale_set(struct axi_dac_state *st,
+			     const struct iio_chan_spec *chan,
+			     const char *buf, size_t len, unsigned int tone)
+{
+	int integer, frac, scale;
+	u32 raw = 0, reg;
+	int ret;
+
+	ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+	if (ret)
+		return ret;
+
+	scale = integer * MEGA + frac;
+	if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
+		return -EINVAL;
+
+	/*  format is 1.1.14 (sign, integer and fractional bits) */
+	if (scale < 0) {
+		raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
+		scale *= -1;
+	}
+
+	raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
+
+	if (tone == AXI_DAC_SCALE_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+
+	guard(mutex)(&st->lock);
+	ret = regmap_write(st->regmap, reg, raw);
+	if (ret)
+		return ret;
+
+	/* synchronize channels */
+	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static int axi_dac_phase_set(struct axi_dac_state *st,
+			     const struct iio_chan_spec *chan,
+			     const char *buf, size_t len, unsigned int tone)
+{
+	int integer, frac, phase;
+	u32 raw, reg;
+	int ret;
+
+	ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+	if (ret)
+		return ret;
+
+	phase = integer * MEGA + frac;
+	if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
+		return -EINVAL;
+
+	raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
+
+	if (tone == AXI_DAC_PHASE_TONE_1)
+		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+	else
+		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+
+	guard(mutex)(&st->lock);
+	ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
+				 FIELD_PREP(AXI_DAC_PHASE, raw));
+	if (ret)
+		return ret;
+
+	/* synchronize channels */
+	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
+				const struct iio_chan_spec *chan,
+				const char *buf, size_t len)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	switch (private) {
+	case AXI_DAC_FREQ_TONE_1:
+	case AXI_DAC_FREQ_TONE_2:
+		return axi_dac_frequency_set(st, chan, buf, len, private);
+	case AXI_DAC_SCALE_TONE_1:
+	case AXI_DAC_SCALE_TONE_2:
+		return axi_dac_scale_set(st, chan, buf, len, private);
+	case AXI_DAC_PHASE_TONE_1:
+	case AXI_DAC_PHASE_TONE_2:
+		return axi_dac_phase_set(st, chan, buf, len, private);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
+				const struct iio_chan_spec *chan, char *buf)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	switch (private) {
+	case AXI_DAC_FREQ_TONE_1:
+	case AXI_DAC_FREQ_TONE_2:
+		return axi_dac_frequency_get(st, chan, buf, private);
+	case AXI_DAC_SCALE_TONE_1:
+	case AXI_DAC_SCALE_TONE_2:
+		return axi_dac_scale_get(st, chan, buf, private);
+	case AXI_DAC_PHASE_TONE_1:
+	case AXI_DAC_PHASE_TONE_2:
+		return axi_dac_phase_get(st, chan, buf, private);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
+	IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
+	IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
+	IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
+	IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
+	IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
+	IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
+	{}
+};
+
+static int axi_dac_extend_chan(struct iio_backend *back,
+			       struct iio_chan_spec *chan)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	if (chan->type != IIO_ALTVOLTAGE)
+		return -EINVAL;
+	if (st->reg_config & AXI_DDS_DISABLE)
+		/* nothing to extend */
+		return 0;
+
+	chan->ext_info = axi_dac_ext_info;
+
+	return 0;
+}
+
+static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan,
+				   enum iio_backend_data_source data)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+
+	switch (data) {
+	case IIO_BACKEND_INTERNAL_CW:
+		return regmap_update_bits(st->regmap,
+					  AXI_DAC_REG_CHAN_CNTRL_7(chan),
+					  AXI_DAC_DATA_SEL,
+					  AXI_DAC_DATA_INTERNAL_TONE);
+	case IIO_BACKEND_EXTERNAL:
+		return regmap_update_bits(st->regmap,
+					  AXI_DAC_REG_CHAN_CNTRL_7(chan),
+					  AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
+				   u64 sample_rate)
+{
+	struct axi_dac_state *st = iio_backend_get_priv(back);
+	unsigned int freq;
+	int ret, tone;
+
+	if (!sample_rate)
+		return -EINVAL;
+	if (st->reg_config & AXI_DDS_DISABLE)
+		/* nothing to care if DDS is disabled */
+		return 0;
+
+	guard(mutex)(&st->lock);
+	/*
+	 * If dac_clk is 0 then this must be the first time we're being notified
+	 * about the interface sample rate. Hence, just update our internal
+	 * variable and bail... If it's not 0, then we get the current DDS
+	 * frequency (for the old rate) and update the registers for the new
+	 * sample rate.
+	 */
+	if (!st->dac_clk) {
+		st->dac_clk = sample_rate;
+		return 0;
+	}
+
+	for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) {
+		ret = __axi_dac_frequency_get(st, chan, tone, &freq);
+		if (ret)
+			return ret;
+
+		ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq);
+		if (ret)
+			return ret;
+	}
+
+	st->dac_clk = sample_rate;
+
+	return 0;
+}
+
+static const struct iio_backend_ops axi_dac_generic = {
+	.enable = axi_dac_enable,
+	.disable = axi_dac_disable,
+	.request_buffer = axi_dac_request_buffer,
+	.free_buffer = axi_dac_free_buffer,
+	.extend_chan_spec = axi_dac_extend_chan,
+	.ext_info_set = axi_dac_ext_info_set,
+	.ext_info_get = axi_dac_ext_info_get,
+	.data_source_set = axi_dac_data_source_set,
+	.set_sample_rate = axi_dac_set_sample_rate,
+};
+
+static const struct regmap_config axi_dac_regmap_config = {
+	.val_bits = 32,
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.max_register = 0x0800,
+};
+
+static int axi_dac_probe(struct platform_device *pdev)
+{
+	const unsigned int *expected_ver;
+	struct axi_dac_state *st;
+	void __iomem *base;
+	unsigned int ver;
+	struct clk *clk;
+	int ret;
+
+	st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+	if (!st)
+		return -ENOMEM;
+
+	expected_ver = device_get_match_data(&pdev->dev);
+	if (!expected_ver)
+		return -ENODEV;
+
+	clk = devm_clk_get_enabled(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	st->dev = &pdev->dev;
+	st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+					   &axi_dac_regmap_config);
+	if (IS_ERR(st->regmap))
+		return PTR_ERR(st->regmap);
+
+	/*
+	 * Force disable the core. Up to the frontend to enable us. And we can
+	 * still read/write registers...
+	 */
+	ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver);
+	if (ret)
+		return ret;
+
+	if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
+		dev_err(&pdev->dev,
+			"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+			ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
+			ADI_AXI_PCORE_VER_MINOR(*expected_ver),
+			ADI_AXI_PCORE_VER_PATCH(*expected_ver),
+			ADI_AXI_PCORE_VER_MAJOR(ver),
+			ADI_AXI_PCORE_VER_MINOR(ver),
+			ADI_AXI_PCORE_VER_PATCH(ver));
+		return -ENODEV;
+	}
+
+	/* Let's get the core read only configuration */
+	ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config);
+	if (ret)
+		return ret;
+
+	/*
+	 * In some designs, setting the R1_MODE bit to 0 (which is the default
+	 * value) causes all channels of the frontend to be routed to the same
+	 * DMA (so they are sampled together). This is for things like
+	 * Multiple-Input and Multiple-Output (MIMO). As most of the times we
+	 * want independent channels let's override the core's default value and
+	 * set the R1_MODE bit.
+	 */
+	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_2, ADI_DAC_R1_MODE);
+	if (ret)
+		return ret;
+
+	mutex_init(&st->lock);
+	ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st);
+	if (ret)
+		return ret;
+
+	dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
+		 ADI_AXI_PCORE_VER_MAJOR(ver),
+		 ADI_AXI_PCORE_VER_MINOR(ver),
+		 ADI_AXI_PCORE_VER_PATCH(ver));
+
+	return 0;
+}
+
+static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b');
+
+static const struct of_device_id axi_dac_of_match[] = {
+	{ .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info },
+	{}
+};
+MODULE_DEVICE_TABLE(of, axi_dac_of_match);
+
+static struct platform_driver axi_dac_driver = {
+	.driver = {
+		.name = "adi-axi-dac",
+		.of_match_table = axi_dac_of_match,
+	},
+	.probe = axi_dac_probe,
+};
+module_platform_driver(axi_dac_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices Generic AXI DAC IP core driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);
+MODULE_IMPORT_NS(IIO_BACKEND);

-- 
2.44.0


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

* [PATCH 10/10] iio: dac: support the ad9739a RF DAC
  2024-03-28 13:22 ` Nuno Sa
@ 2024-03-28 13:22   ` Nuno Sa
  -1 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa via B4 Relay @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

From: Nuno Sa <nuno.sa@analog.com>

The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
of synthesizing wideband signals from dc up to 3 GHz.

A dual-port, source synchronous, LVDS interface simplifies the digital
interface with existing FGPA/ASIC technology. On-chip controllers are used
to manage external and internal clock domain variations over temperature to
ensure reliable data transfer from the host to the DAC core.

Co-developed-by: Dragos Bogdan <dragos.bogdan@analog.com>
Signed-off-by: Dragos Bogdan <dragos.bogdan@analog.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 Documentation/ABI/testing/sysfs-bus-iio-ad9739a |  17 +
 MAINTAINERS                                     |   1 +
 drivers/iio/dac/Kconfig                         |  16 +
 drivers/iio/dac/Makefile                        |   1 +
 drivers/iio/dac/ad9739a.c                       | 445 ++++++++++++++++++++++++
 5 files changed, 480 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ad9739a b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
new file mode 100644
index 000000000000..8a8a5cd10386
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
@@ -0,0 +1,17 @@
+What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
+KernelVersion:	6.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Dac operating mode. One of the following modes can be selected:
+         * normal: This is DAC normal mode.
+         * mixed-mode: In this mode the output is effectively chopped at the
+                       DAC sample rate. This has the effect of reducing the
+                       power of the fundamental signal while increasing the
+                       power of the images centered around the DAC sample rate,
+                       thus improving the output power of these images.
+
+What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
+KernelVersion:	6.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Available operating modes.
diff --git a/MAINTAINERS b/MAINTAINERS
index 505f28dc6da6..8ad79cf70552 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1241,6 +1241,7 @@ L:	linux-iio@vger.kernel.org
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
+F:	drivers/iio/dac/ad9739a.c
 
 ANALOG DEVICES INC ADA4250 DRIVER
 M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 7c0a8caa9a34..ee0d9798d8b4 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,22 @@ config AD5624R_SPI
 	  Say yes here to build support for Analog Devices AD5624R, AD5644R and
 	  AD5664R converters (DAC). This driver uses the common SPI interface.
 
+config AD9739A
+	tristate "Analog Devices AD9739A RF DAC spi driver"
+	depends on SPI
+	select REGMAP_SPI
+	select IIO_BACKEND
+	help
+	  Say yes here to build support for Analog Devices AD9739A Digital-to
+	  Analog Converter.
+
+	  The driver requires the assistance of the AXI DAC IP core to operate,
+	  since SPI is used for configuration only, while data has to be
+	  streamed into memory via DMA.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ad9739a.
+
 config ADI_AXI_DAC
 	tristate "Analog Devices Generic AXI DAC IP core driver"
 	select IIO_BUFFER
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 6bcaa65434b2..8432a81a19dc 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
 obj-$(CONFIG_AD7293) += ad7293.o
 obj-$(CONFIG_AD7303) += ad7303.o
 obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_AD9739A) += ad9739a.o
 obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
 obj-$(CONFIG_CIO_DAC) += cio-dac.o
 obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
new file mode 100644
index 000000000000..46431fa345a5
--- /dev/null
+++ b/drivers/iio/dac/ad9739a.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD9739a SPI DAC driver
+ *
+ * Copyright 2015-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#define AD9739A_REG_MODE		0
+#define   AD9739A_RESET_MASK		BIT(5)
+#define AD9739A_REG_FSC_1		0x06
+#define AD9739A_REG_FSC_2		0x07
+#define   AD9739A_FSC_MSB		GENMASK(1, 0)
+#define AD9739A_REG_DEC_CNT		0x8
+#define   AD9739A_DAC_DEC		GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT1	0x10
+#define   AD9739A_RCVR_LOOP_EN_MASK	GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT4	0x13
+#define   AD9739A_FINE_DEL_SKW_MASK	GENMASK(3, 0)
+#define AD9739A_REG_LVDS_REC_STAT9	0x21
+#define   AD9739A_RCVR_TRACK_AND_LOCK	(BIT(3) | BIT(0))
+#define AD9739A_REG_CROSS_CNT1		0x22
+#define AD9739A_REG_CROSS_CNT2		0x23
+#define AD9739A_REG_PHS_DET		0x24
+#define AD9739A_REG_MU_DUTY		0x25
+#define AD9739A_REG_MU_CNT1		0x26
+#define   AD9739A_MU_EN_MASK		BIT(0)
+#define AD9739A_REG_MU_CNT2		0x27
+#define AD9739A_REG_MU_CNT3		0x28
+#define AD9739A_REG_MU_CNT4		0x29
+#define   AD9739A_MU_CNT4_DEFAULT	0xcb
+#define AD9739A_REG_MU_STAT1		0x2A
+#define   AD9739A_MU_LOCK_MASK		BIT(0)
+#define AD9739A_REG_ANA_CNT_1		0x32
+#define AD9739A_REG_ID			0x35
+
+#define AD9739A_ID			0x24
+#define AD9739A_REG_IS_RESERVED(reg)	\
+	((reg) == 0x5 || (reg) == 0x9 || (reg) == 0x0E || (reg) == 0x0D || \
+	 (reg) == 0x2B || (reg) == 0x2C || (reg) == 0x34)
+
+#define AD9739A_FSC_MIN		8700
+#define AD9739A_FSC_MAX		31700
+#define AD9739A_FSC_RANGE	(AD9739A_FSC_MAX - AD9739A_FSC_MIN + 1)
+
+#define AD9739A_MIN_DAC_CLK	(1600 * MEGA)
+#define AD9739A_MAX_DAC_CLK	(2500 * MEGA)
+#define AD9739A_DAC_CLK_RANGE	(AD9739A_MAX_DAC_CLK - AD9739A_MIN_DAC_CLK + 1)
+/* as recommended by the datasheet */
+#define AD9739A_LOCK_N_TRIES	3
+
+struct ad9739a_state {
+	struct iio_backend *back;
+	struct regmap *regmap;
+	unsigned long sample_rate;
+};
+
+enum {
+	AD9739A_NORMAL_MODE,
+	AD9739A_MIXED_MODE = 2,
+};
+
+static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
+				 const struct iio_chan_spec *chan)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+	u32 mode;
+	int ret;
+
+	ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
+	if (ret)
+		return ret;
+
+	mode = FIELD_GET(AD9739A_DAC_DEC, mode);
+	/* sanity check we get valid values from the HW */
+	if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
+		return -EIO;
+	if (!mode)
+		return AD9739A_NORMAL_MODE;
+
+	return AD9739A_MIXED_MODE - 1;
+}
+
+static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
+				 const struct iio_chan_spec *chan, u32 mode)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+
+	if (mode == AD9739A_MIXED_MODE - 1)
+		mode = AD9739A_MIXED_MODE;
+
+	return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
+				  AD9739A_DAC_DEC, mode);
+}
+
+static int ad9739a_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = st->sample_rate;
+		*val2 = 0;
+		return IIO_VAL_INT_64;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ad9739a_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+
+	return iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
+}
+
+static int ad9739a_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+
+	return iio_backend_data_source_set(st->back, 0,
+					   IIO_BACKEND_INTERNAL_CW);
+}
+
+static bool ad9739a_reg_accessible(struct device *dev, unsigned int reg)
+{
+	if (AD9739A_REG_IS_RESERVED(reg))
+		return false;
+	if (reg > AD9739A_REG_MU_STAT1 && reg < AD9739A_REG_ANA_CNT_1)
+		return false;
+
+	return true;
+}
+
+static int ad9739a_reset(struct device *dev, const struct ad9739a_state *st)
+{
+	struct gpio_desc *gpio;
+	int ret;
+
+	gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	if (gpio) {
+		/* minimum pulse width of 40ns */
+		ndelay(40);
+		gpiod_set_value_cansleep(gpio, 0);
+		return 0;
+	}
+
+	/* bring all registers to their default state */
+	ret = regmap_set_bits(st->regmap, AD9739A_REG_MODE, AD9739A_RESET_MASK);
+	if (ret)
+		return ret;
+
+	ndelay(40);
+
+	return regmap_clear_bits(st->regmap, AD9739A_REG_MODE,
+				 AD9739A_RESET_MASK);
+}
+
+/*
+ * Recommended values (as per datasheet) for the dac clk common mode voltage
+ * and Mu controller. Look at table 29.
+ */
+static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
+	/* DAC clk common mode voltage */
+	{AD9739A_REG_CROSS_CNT1, 0x0f},
+	{AD9739A_REG_CROSS_CNT2, 0x0f},
+	/* Mu controller configuration */
+	{AD9739A_REG_PHS_DET, 0x30},
+	{AD9739A_REG_MU_DUTY, 0x80},
+	{AD9739A_REG_MU_CNT2, 0x44},
+	{AD9739A_REG_MU_CNT3, 0x6c},
+};
+
+static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
+{
+	unsigned int i = 0, lock, fsc;
+	u32 fsc_raw;
+	int ret;
+
+	ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
+				     ARRAY_SIZE(ad9739a_clk_mu_ctrl));
+	if (ret)
+		return ret;
+
+	/*
+	 * Try to get the MU lock. Repeat the below steps AD9739A_LOCK_N_TRIES
+	 * (as specified by the datasheet) until we get the lock.
+	 */
+	do {
+		ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
+				   AD9739A_MU_CNT4_DEFAULT);
+		if (ret)
+			return ret;
+
+		/* Enable the Mu controller search and track mode. */
+		ret = regmap_set_bits(st->regmap, AD9739A_REG_MU_CNT1,
+				      AD9739A_MU_EN_MASK);
+		if (ret)
+			return ret;
+
+		/* Ensure the DLL loop is locked */
+		ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
+					       lock, lock & AD9739A_MU_LOCK_MASK,
+					       0, 1000);
+	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+	if (i == AD9739A_LOCK_N_TRIES)
+		return dev_err_probe(dev, ret, "Mu lock timeout\n");
+
+	/* Receiver tracking and lock. Same deal as the Mu controller */
+	i = 0;
+	do {
+		ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
+					 AD9739A_FINE_DEL_SKW_MASK,
+					 FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2));
+		if (ret)
+			return ret;
+
+		/* Disable the receiver and the loop. */
+		ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
+		if (ret)
+			return ret;
+
+		/*
+		 * Re-enable the loop so it falls out of lock and begins the
+		 * search/track routine again.
+		 */
+		ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
+				      AD9739A_RCVR_LOOP_EN_MASK);
+		if (ret)
+			return ret;
+
+		/* Ensure the DLL loop is locked */
+		ret = regmap_read_poll_timeout(st->regmap,
+					       AD9739A_REG_LVDS_REC_STAT9, lock,
+					       lock == AD9739A_RCVR_TRACK_AND_LOCK,
+					       0, 1000);
+	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+	if (i == AD9739A_LOCK_N_TRIES)
+		return dev_err_probe(dev, ret, "Receiver lock timeout\n");
+
+	ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
+	if (ret && ret == -EINVAL)
+		return 0;
+	if (ret)
+		return ret;
+	if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
+		return dev_err_probe(dev, -EINVAL,
+				     "Invalid full scale current(%u) [%u %u]\n",
+				     fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
+	/*
+	 * IOUTFS is given by
+	 *	Ioutfs = 0.0226 * FSC + 8.58
+	 * and is given in mA. Hence we'll have to multiply by 10 * MILLI in
+	 * order to get rid of the fractional.
+	 */
+	fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
+
+	ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(st->regmap, AD9739A_REG_FSC_1,
+				  AD9739A_FSC_MSB, fsc_raw >> 8);
+}
+
+static const char * const ad9739a_modes_avail[] = { "normal", "mixed-mode" };
+
+static const struct iio_enum ad9739a_modes = {
+	.items = ad9739a_modes_avail,
+	.num_items = ARRAY_SIZE(ad9739a_modes_avail),
+	.get = ad9739a_oper_mode_get,
+	.set = ad9739a_oper_mode_set,
+};
+
+static const struct iio_chan_spec_ext_info ad9739a_ext_info[] = {
+	IIO_ENUM_AVAILABLE("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+	IIO_ENUM("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+};
+
+static struct iio_chan_spec ad9739a_channels[] = {
+	{
+		.type = IIO_ALTVOLTAGE,
+		.indexed = 1,
+		.output = 1,
+		.scan_index = -1,
+	},
+	{
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.output = 1,
+		.ext_info = ad9739a_ext_info,
+		.scan_type = {
+			.sign = 's',
+			.storagebits = 16,
+			.realbits = 16,
+		},
+	}
+};
+
+static const struct iio_info ad9739a_info = {
+	.read_raw = ad9739a_read_raw,
+};
+
+static const struct iio_buffer_setup_ops ad9739a_buffer_setup_ops = {
+	.preenable = &ad9739a_buffer_preenable,
+	.postdisable = &ad9739a_buffer_postdisable,
+};
+
+static const struct regmap_config ad9739a_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.readable_reg = ad9739a_reg_accessible,
+	.writeable_reg = ad9739a_reg_accessible,
+	.max_register = AD9739A_REG_ID,
+};
+
+static int ad9739a_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct iio_dev *indio_dev;
+	struct ad9739a_state *st;
+	unsigned int id;
+	struct clk *clk;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+
+	clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n");
+
+	st->sample_rate = clk_get_rate(clk);
+	if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
+		      AD9739A_DAC_CLK_RANGE))
+		return dev_err_probe(dev, -EINVAL,
+				     "Invalid dac clk range(%lu) [%lu %lu]\n",
+				     st->sample_rate, AD9739A_MIN_DAC_CLK,
+				     AD9739A_MAX_DAC_CLK);
+
+	st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
+	if (IS_ERR(st->regmap))
+		return PTR_ERR(st->regmap);
+
+	ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
+	if (ret)
+		return ret;
+
+	if (id != AD9739A_ID)
+		return dev_err_probe(dev, -ENODEV, "Unrecognized CHIP_ID 0x%X",
+				     id);
+
+	ret = ad9739a_reset(dev, st);
+	if (ret)
+		return ret;
+
+	ret = ad9739a_init(dev, st);
+	if (ret)
+		return ret;
+
+	st->back = devm_iio_backend_get(dev, NULL);
+	if (IS_ERR(st->back))
+		return PTR_ERR(st->back);
+
+	ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
+	if (ret)
+		return ret;
+
+	ret = iio_backend_extend_chan_spec(indio_dev, st->back,
+					   &ad9739a_channels[0]);
+	if (ret)
+		return ret;
+
+	ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate);
+	if (ret)
+		return ret;
+
+	ret = devm_iio_backend_enable(dev, st->back);
+	if (ret)
+		return ret;
+
+	indio_dev->name = "ad9739a";
+	indio_dev->info = &ad9739a_info;
+	indio_dev->channels = ad9739a_channels;
+	indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels);
+	indio_dev->setup_ops = &ad9739a_buffer_setup_ops;
+
+	return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ad9739a_of_match[] = {
+	{ .compatible = "adi,ad9739a" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ad9739a_of_match);
+
+static const struct spi_device_id ad9739a_id[] = {
+	{"ad9739a"},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, ad9739a_id);
+
+static struct spi_driver ad9739a_driver = {
+	.driver = {
+		.name = "ad9739a",
+		.of_match_table = ad9739a_of_match,
+	},
+	.probe = ad9739a_probe,
+	.id_table = ad9739a_id,
+};
+module_spi_driver(ad9739a_driver);
+
+MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9739 DAC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_BACKEND);

-- 
2.44.0



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

* [PATCH 10/10] iio: dac: support the ad9739a RF DAC
@ 2024-03-28 13:22   ` Nuno Sa
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sa @ 2024-03-28 13:22 UTC (permalink / raw)
  To: linux-iio, devicetree
  Cc: Dragos Bogdan, Jonathan Cameron, Lars-Peter Clausen,
	Michael Hennerich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan, Nuno Sa

The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
of synthesizing wideband signals from dc up to 3 GHz.

A dual-port, source synchronous, LVDS interface simplifies the digital
interface with existing FGPA/ASIC technology. On-chip controllers are used
to manage external and internal clock domain variations over temperature to
ensure reliable data transfer from the host to the DAC core.

Co-developed-by: Dragos Bogdan <dragos.bogdan@analog.com>
Signed-off-by: Dragos Bogdan <dragos.bogdan@analog.com>
Signed-off-by: Nuno Sa <nuno.sa@analog.com>
---
 Documentation/ABI/testing/sysfs-bus-iio-ad9739a |  17 +
 MAINTAINERS                                     |   1 +
 drivers/iio/dac/Kconfig                         |  16 +
 drivers/iio/dac/Makefile                        |   1 +
 drivers/iio/dac/ad9739a.c                       | 445 ++++++++++++++++++++++++
 5 files changed, 480 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ad9739a b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
new file mode 100644
index 000000000000..8a8a5cd10386
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
@@ -0,0 +1,17 @@
+What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
+KernelVersion:	6.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Dac operating mode. One of the following modes can be selected:
+         * normal: This is DAC normal mode.
+         * mixed-mode: In this mode the output is effectively chopped at the
+                       DAC sample rate. This has the effect of reducing the
+                       power of the fundamental signal while increasing the
+                       power of the images centered around the DAC sample rate,
+                       thus improving the output power of these images.
+
+What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
+KernelVersion:	6.9
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Available operating modes.
diff --git a/MAINTAINERS b/MAINTAINERS
index 505f28dc6da6..8ad79cf70552 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1241,6 +1241,7 @@ L:	linux-iio@vger.kernel.org
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
+F:	drivers/iio/dac/ad9739a.c
 
 ANALOG DEVICES INC ADA4250 DRIVER
 M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 7c0a8caa9a34..ee0d9798d8b4 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,22 @@ config AD5624R_SPI
 	  Say yes here to build support for Analog Devices AD5624R, AD5644R and
 	  AD5664R converters (DAC). This driver uses the common SPI interface.
 
+config AD9739A
+	tristate "Analog Devices AD9739A RF DAC spi driver"
+	depends on SPI
+	select REGMAP_SPI
+	select IIO_BACKEND
+	help
+	  Say yes here to build support for Analog Devices AD9739A Digital-to
+	  Analog Converter.
+
+	  The driver requires the assistance of the AXI DAC IP core to operate,
+	  since SPI is used for configuration only, while data has to be
+	  streamed into memory via DMA.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ad9739a.
+
 config ADI_AXI_DAC
 	tristate "Analog Devices Generic AXI DAC IP core driver"
 	select IIO_BUFFER
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 6bcaa65434b2..8432a81a19dc 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
 obj-$(CONFIG_AD7293) += ad7293.o
 obj-$(CONFIG_AD7303) += ad7303.o
 obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_AD9739A) += ad9739a.o
 obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
 obj-$(CONFIG_CIO_DAC) += cio-dac.o
 obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
new file mode 100644
index 000000000000..46431fa345a5
--- /dev/null
+++ b/drivers/iio/dac/ad9739a.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD9739a SPI DAC driver
+ *
+ * Copyright 2015-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#define AD9739A_REG_MODE		0
+#define   AD9739A_RESET_MASK		BIT(5)
+#define AD9739A_REG_FSC_1		0x06
+#define AD9739A_REG_FSC_2		0x07
+#define   AD9739A_FSC_MSB		GENMASK(1, 0)
+#define AD9739A_REG_DEC_CNT		0x8
+#define   AD9739A_DAC_DEC		GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT1	0x10
+#define   AD9739A_RCVR_LOOP_EN_MASK	GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT4	0x13
+#define   AD9739A_FINE_DEL_SKW_MASK	GENMASK(3, 0)
+#define AD9739A_REG_LVDS_REC_STAT9	0x21
+#define   AD9739A_RCVR_TRACK_AND_LOCK	(BIT(3) | BIT(0))
+#define AD9739A_REG_CROSS_CNT1		0x22
+#define AD9739A_REG_CROSS_CNT2		0x23
+#define AD9739A_REG_PHS_DET		0x24
+#define AD9739A_REG_MU_DUTY		0x25
+#define AD9739A_REG_MU_CNT1		0x26
+#define   AD9739A_MU_EN_MASK		BIT(0)
+#define AD9739A_REG_MU_CNT2		0x27
+#define AD9739A_REG_MU_CNT3		0x28
+#define AD9739A_REG_MU_CNT4		0x29
+#define   AD9739A_MU_CNT4_DEFAULT	0xcb
+#define AD9739A_REG_MU_STAT1		0x2A
+#define   AD9739A_MU_LOCK_MASK		BIT(0)
+#define AD9739A_REG_ANA_CNT_1		0x32
+#define AD9739A_REG_ID			0x35
+
+#define AD9739A_ID			0x24
+#define AD9739A_REG_IS_RESERVED(reg)	\
+	((reg) == 0x5 || (reg) == 0x9 || (reg) == 0x0E || (reg) == 0x0D || \
+	 (reg) == 0x2B || (reg) == 0x2C || (reg) == 0x34)
+
+#define AD9739A_FSC_MIN		8700
+#define AD9739A_FSC_MAX		31700
+#define AD9739A_FSC_RANGE	(AD9739A_FSC_MAX - AD9739A_FSC_MIN + 1)
+
+#define AD9739A_MIN_DAC_CLK	(1600 * MEGA)
+#define AD9739A_MAX_DAC_CLK	(2500 * MEGA)
+#define AD9739A_DAC_CLK_RANGE	(AD9739A_MAX_DAC_CLK - AD9739A_MIN_DAC_CLK + 1)
+/* as recommended by the datasheet */
+#define AD9739A_LOCK_N_TRIES	3
+
+struct ad9739a_state {
+	struct iio_backend *back;
+	struct regmap *regmap;
+	unsigned long sample_rate;
+};
+
+enum {
+	AD9739A_NORMAL_MODE,
+	AD9739A_MIXED_MODE = 2,
+};
+
+static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
+				 const struct iio_chan_spec *chan)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+	u32 mode;
+	int ret;
+
+	ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
+	if (ret)
+		return ret;
+
+	mode = FIELD_GET(AD9739A_DAC_DEC, mode);
+	/* sanity check we get valid values from the HW */
+	if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
+		return -EIO;
+	if (!mode)
+		return AD9739A_NORMAL_MODE;
+
+	return AD9739A_MIXED_MODE - 1;
+}
+
+static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
+				 const struct iio_chan_spec *chan, u32 mode)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+
+	if (mode == AD9739A_MIXED_MODE - 1)
+		mode = AD9739A_MIXED_MODE;
+
+	return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
+				  AD9739A_DAC_DEC, mode);
+}
+
+static int ad9739a_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = st->sample_rate;
+		*val2 = 0;
+		return IIO_VAL_INT_64;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ad9739a_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+
+	return iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
+}
+
+static int ad9739a_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct ad9739a_state *st = iio_priv(indio_dev);
+
+	return iio_backend_data_source_set(st->back, 0,
+					   IIO_BACKEND_INTERNAL_CW);
+}
+
+static bool ad9739a_reg_accessible(struct device *dev, unsigned int reg)
+{
+	if (AD9739A_REG_IS_RESERVED(reg))
+		return false;
+	if (reg > AD9739A_REG_MU_STAT1 && reg < AD9739A_REG_ANA_CNT_1)
+		return false;
+
+	return true;
+}
+
+static int ad9739a_reset(struct device *dev, const struct ad9739a_state *st)
+{
+	struct gpio_desc *gpio;
+	int ret;
+
+	gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	if (gpio) {
+		/* minimum pulse width of 40ns */
+		ndelay(40);
+		gpiod_set_value_cansleep(gpio, 0);
+		return 0;
+	}
+
+	/* bring all registers to their default state */
+	ret = regmap_set_bits(st->regmap, AD9739A_REG_MODE, AD9739A_RESET_MASK);
+	if (ret)
+		return ret;
+
+	ndelay(40);
+
+	return regmap_clear_bits(st->regmap, AD9739A_REG_MODE,
+				 AD9739A_RESET_MASK);
+}
+
+/*
+ * Recommended values (as per datasheet) for the dac clk common mode voltage
+ * and Mu controller. Look at table 29.
+ */
+static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
+	/* DAC clk common mode voltage */
+	{AD9739A_REG_CROSS_CNT1, 0x0f},
+	{AD9739A_REG_CROSS_CNT2, 0x0f},
+	/* Mu controller configuration */
+	{AD9739A_REG_PHS_DET, 0x30},
+	{AD9739A_REG_MU_DUTY, 0x80},
+	{AD9739A_REG_MU_CNT2, 0x44},
+	{AD9739A_REG_MU_CNT3, 0x6c},
+};
+
+static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
+{
+	unsigned int i = 0, lock, fsc;
+	u32 fsc_raw;
+	int ret;
+
+	ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
+				     ARRAY_SIZE(ad9739a_clk_mu_ctrl));
+	if (ret)
+		return ret;
+
+	/*
+	 * Try to get the MU lock. Repeat the below steps AD9739A_LOCK_N_TRIES
+	 * (as specified by the datasheet) until we get the lock.
+	 */
+	do {
+		ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
+				   AD9739A_MU_CNT4_DEFAULT);
+		if (ret)
+			return ret;
+
+		/* Enable the Mu controller search and track mode. */
+		ret = regmap_set_bits(st->regmap, AD9739A_REG_MU_CNT1,
+				      AD9739A_MU_EN_MASK);
+		if (ret)
+			return ret;
+
+		/* Ensure the DLL loop is locked */
+		ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
+					       lock, lock & AD9739A_MU_LOCK_MASK,
+					       0, 1000);
+	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+	if (i == AD9739A_LOCK_N_TRIES)
+		return dev_err_probe(dev, ret, "Mu lock timeout\n");
+
+	/* Receiver tracking and lock. Same deal as the Mu controller */
+	i = 0;
+	do {
+		ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
+					 AD9739A_FINE_DEL_SKW_MASK,
+					 FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2));
+		if (ret)
+			return ret;
+
+		/* Disable the receiver and the loop. */
+		ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
+		if (ret)
+			return ret;
+
+		/*
+		 * Re-enable the loop so it falls out of lock and begins the
+		 * search/track routine again.
+		 */
+		ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
+				      AD9739A_RCVR_LOOP_EN_MASK);
+		if (ret)
+			return ret;
+
+		/* Ensure the DLL loop is locked */
+		ret = regmap_read_poll_timeout(st->regmap,
+					       AD9739A_REG_LVDS_REC_STAT9, lock,
+					       lock == AD9739A_RCVR_TRACK_AND_LOCK,
+					       0, 1000);
+	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+	if (i == AD9739A_LOCK_N_TRIES)
+		return dev_err_probe(dev, ret, "Receiver lock timeout\n");
+
+	ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
+	if (ret && ret == -EINVAL)
+		return 0;
+	if (ret)
+		return ret;
+	if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
+		return dev_err_probe(dev, -EINVAL,
+				     "Invalid full scale current(%u) [%u %u]\n",
+				     fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
+	/*
+	 * IOUTFS is given by
+	 *	Ioutfs = 0.0226 * FSC + 8.58
+	 * and is given in mA. Hence we'll have to multiply by 10 * MILLI in
+	 * order to get rid of the fractional.
+	 */
+	fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
+
+	ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(st->regmap, AD9739A_REG_FSC_1,
+				  AD9739A_FSC_MSB, fsc_raw >> 8);
+}
+
+static const char * const ad9739a_modes_avail[] = { "normal", "mixed-mode" };
+
+static const struct iio_enum ad9739a_modes = {
+	.items = ad9739a_modes_avail,
+	.num_items = ARRAY_SIZE(ad9739a_modes_avail),
+	.get = ad9739a_oper_mode_get,
+	.set = ad9739a_oper_mode_set,
+};
+
+static const struct iio_chan_spec_ext_info ad9739a_ext_info[] = {
+	IIO_ENUM_AVAILABLE("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+	IIO_ENUM("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+};
+
+static struct iio_chan_spec ad9739a_channels[] = {
+	{
+		.type = IIO_ALTVOLTAGE,
+		.indexed = 1,
+		.output = 1,
+		.scan_index = -1,
+	},
+	{
+		.type = IIO_VOLTAGE,
+		.indexed = 1,
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.output = 1,
+		.ext_info = ad9739a_ext_info,
+		.scan_type = {
+			.sign = 's',
+			.storagebits = 16,
+			.realbits = 16,
+		},
+	}
+};
+
+static const struct iio_info ad9739a_info = {
+	.read_raw = ad9739a_read_raw,
+};
+
+static const struct iio_buffer_setup_ops ad9739a_buffer_setup_ops = {
+	.preenable = &ad9739a_buffer_preenable,
+	.postdisable = &ad9739a_buffer_postdisable,
+};
+
+static const struct regmap_config ad9739a_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.readable_reg = ad9739a_reg_accessible,
+	.writeable_reg = ad9739a_reg_accessible,
+	.max_register = AD9739A_REG_ID,
+};
+
+static int ad9739a_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct iio_dev *indio_dev;
+	struct ad9739a_state *st;
+	unsigned int id;
+	struct clk *clk;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+
+	clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n");
+
+	st->sample_rate = clk_get_rate(clk);
+	if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
+		      AD9739A_DAC_CLK_RANGE))
+		return dev_err_probe(dev, -EINVAL,
+				     "Invalid dac clk range(%lu) [%lu %lu]\n",
+				     st->sample_rate, AD9739A_MIN_DAC_CLK,
+				     AD9739A_MAX_DAC_CLK);
+
+	st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
+	if (IS_ERR(st->regmap))
+		return PTR_ERR(st->regmap);
+
+	ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
+	if (ret)
+		return ret;
+
+	if (id != AD9739A_ID)
+		return dev_err_probe(dev, -ENODEV, "Unrecognized CHIP_ID 0x%X",
+				     id);
+
+	ret = ad9739a_reset(dev, st);
+	if (ret)
+		return ret;
+
+	ret = ad9739a_init(dev, st);
+	if (ret)
+		return ret;
+
+	st->back = devm_iio_backend_get(dev, NULL);
+	if (IS_ERR(st->back))
+		return PTR_ERR(st->back);
+
+	ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
+	if (ret)
+		return ret;
+
+	ret = iio_backend_extend_chan_spec(indio_dev, st->back,
+					   &ad9739a_channels[0]);
+	if (ret)
+		return ret;
+
+	ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate);
+	if (ret)
+		return ret;
+
+	ret = devm_iio_backend_enable(dev, st->back);
+	if (ret)
+		return ret;
+
+	indio_dev->name = "ad9739a";
+	indio_dev->info = &ad9739a_info;
+	indio_dev->channels = ad9739a_channels;
+	indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels);
+	indio_dev->setup_ops = &ad9739a_buffer_setup_ops;
+
+	return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ad9739a_of_match[] = {
+	{ .compatible = "adi,ad9739a" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ad9739a_of_match);
+
+static const struct spi_device_id ad9739a_id[] = {
+	{"ad9739a"},
+	{}
+};
+MODULE_DEVICE_TABLE(spi, ad9739a_id);
+
+static struct spi_driver ad9739a_driver = {
+	.driver = {
+		.name = "ad9739a",
+		.of_match_table = ad9739a_of_match,
+	},
+	.probe = ad9739a_probe,
+	.id_table = ad9739a_id,
+};
+module_spi_driver(ad9739a_driver);
+
+MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9739 DAC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_BACKEND);

-- 
2.44.0


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

* Re: [PATCH 01/10] iio: buffer: add helper for setting direction
  2024-03-28 13:22   ` Nuno Sa
  (?)
@ 2024-03-28 14:36   ` Jonathan Cameron
  2024-03-28 15:18     ` Nuno Sá
  -1 siblings, 1 reply; 43+ messages in thread
From: Jonathan Cameron @ 2024-03-28 14:36 UTC (permalink / raw)
  To: Nuno Sa via B4 Relay
  Cc: nuno.sa, linux-iio, devicetree, Dragos Bogdan,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 28 Mar 2024 14:22:25 +0100
Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:

> From: Nuno Sa <nuno.sa@analog.com>
> 
> Simple helper for setting the buffer direction when it's allocated using
> iio_dmaengine_buffer_alloc().
> 
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
I wonder if we should align with the approach for triggered-buffers with and _ext
form of the registration function that takes a direction.  It seems odd to allocate
one then change the direction.

Jonathan

> ---
>  drivers/iio/industrialio-buffer.c | 7 +++++++
>  include/linux/iio/buffer.h        | 3 +++
>  2 files changed, 10 insertions(+)
> 
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index 1d950a3e153b..4b1ca6ad86ee 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -1956,6 +1956,13 @@ void iio_buffer_put(struct iio_buffer *buffer)
>  }
>  EXPORT_SYMBOL_GPL(iio_buffer_put);
>  
> +void iio_buffer_set_dir(struct iio_buffer *buffer,
> +			enum iio_buffer_direction dir)
> +{
> +	buffer->direction = dir;
> +}
> +EXPORT_SYMBOL_GPL(iio_buffer_set_dir);
> +
>  /**
>   * iio_device_attach_buffer - Attach a buffer to a IIO device
>   * @indio_dev: The device the buffer should be attached to
> diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
> index 418b1307d3f2..7e70bb5adc01 100644
> --- a/include/linux/iio/buffer.h
> +++ b/include/linux/iio/buffer.h
> @@ -55,4 +55,7 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
>  int iio_device_attach_buffer(struct iio_dev *indio_dev,
>  			     struct iio_buffer *buffer);
>  
> +void iio_buffer_set_dir(struct iio_buffer *buffer,
> +			enum iio_buffer_direction dir);
> +
>  #endif /* _IIO_BUFFER_GENERIC_H_ */
> 


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

* Re: [PATCH 08/10] iio: backend: add new functionality
  2024-03-28 13:22   ` Nuno Sa
  (?)
@ 2024-03-28 15:16   ` Jonathan Cameron
  2024-03-28 15:42     ` Nuno Sá
  -1 siblings, 1 reply; 43+ messages in thread
From: Jonathan Cameron @ 2024-03-28 15:16 UTC (permalink / raw)
  To: Nuno Sa via B4 Relay
  Cc: nuno.sa, linux-iio, devicetree, Dragos Bogdan,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 28 Mar 2024 14:22:32 +0100
Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:

> From: Nuno Sa <nuno.sa@analog.com>
> 
> This adds the needed backend ops for supporting a backend inerfacing
> with an high speed dac. The new ops are:
> 
> * data_source_set();
> * set_sampling_freq();
> * extend_chan_spec();
> * ext_info_set();
> * ext_info_get().
> 
> Also to note the new helpers that are meant to be used by the backends
> when extending an IIO channel (adding extended info):
> 
> * iio_backend_ext_info_set();
> * iio_backend_ext_info_get().
> 
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
I'm pretty flexible on this so far as I think we are still learning how front
ends and backends should interact. Maybe we'll figure that out in the medium
term and rework this stuff. For now it looks fine. A few minor things inline.
>  
> +/**
> + * iio_backend_ext_info_get - IIO ext_info read callback
> + * @indio_dev:	IIO device
> + * @private:	Data private to the driver
> + * @chan:	IIO channel
> + * @buf:	Buffer where to place the attribute data
> + *
> + * This helper is intended to be used by backends that extend an IIO channel
> + * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
> + * backends are not supposed to give their own callbacks (as they would not
> + * a way to get te backend from indio_dev). This is the getter.

te->the?


> +/**
> + * iio_backend_extend_chan_spec - Extend an IIO channel
> + * @indio_dev:	IIO device
> + * @back:	Backend device
> + * @chan:	IIO channel
> + *
> + * Some backends may have their own functionalities and hence capable of
> + * extending a frontend's channel.
> + *
> + * RETURNS:
> + * 0 on success, negative error number on failure.
> + */
> +int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
> +				 struct iio_backend *back,
> +				 struct iio_chan_spec *chan)
> +{
> +	const struct iio_chan_spec_ext_info *ext_info = chan->ext_info;
This is getting confusing.  So this one is the front end value of ext_info?
Name it as such frontend_ext_info

> +	int ret;
> +
> +	ret = iio_backend_op_call(back, extend_chan_spec, chan);
> +	if (ret)
> +		return ret;
> +	/*
> +	 * Let's keep things simple for now. Don't allow to overwrite the
> +	 * frontend's extended info. If ever needed, we can support appending
> +	 * it.
> +	 */
> +	if (ext_info && chan->ext_info != ext_info)
> +		return -EOPNOTSUPP;
> +	if (!chan->ext_info)

This is checking if the backend added anything? Perhaps a comment on that
as we don't need a backend_ext_info local variable...

> +		return 0;
> +	/*
> +	 * !\NOTE: this will break as soon as we have multiple backends on one
> +	 * frontend and all of them extend channels. In that case, the core
> +	 * backend code has no way to get the correct backend given the
> +	 * iio device.
> +	 *
> +	 * One solution for this could be introducing a new backend
> +	 * dedicated callback in struct iio_info so we can callback into the
> +	 * frontend so it can give us the right backend given a chan_spec.
> +	 */

Hmm. This is indeed messy.  Could we associate it with the buffer as presuably
a front end with multiple backends is using multiple IIO buffers?

As you say a dance via the front end would work fine.


> +	iio_device_set_drvdata(indio_dev, back);
> +
> +	/* Don't allow backends to get creative and force their own handlers */
> +	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> +		if (ext_info->read != iio_backend_ext_info_get)
> +			return -EINVAL;
> +		if (ext_info->write != iio_backend_ext_info_set)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);

> diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
> index a6d79381866e..09ff2f8f9fd8 100644
> --- a/include/linux/iio/backend.h
> +++ b/include/linux/iio/backend.h
> @@ -4,6 +4,7 @@
>  
>  #include <linux/types.h>
>  
> +struct iio_chan_spec;
>  struct fwnode_handle;
>  struct iio_backend;
>  struct device;
> @@ -15,6 +16,26 @@ enum iio_backend_data_type {
>  	IIO_BACKEND_DATA_TYPE_MAX
>  };
>  
> +enum iio_backend_data_source {
> +	IIO_BACKEND_INTERNAL_CW,

CW?  Either expand out what ever that is in definition of add a comment
at least.

> +	IIO_BACKEND_EXTERNAL,
What does external mean in this case?
> +	IIO_BACKEND_DATA_SOURCE_MAX
> +};
> +
> +/**
> + * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
> + * @_name:	Attribute name
> + * @_shared:	Whether the attribute is shared between all channels
> + * @_what:	Data private to the driver
> + */
> +#define IIO_BACKEND_EX_INFO(_name, _shared, _what) {	\
> +	.name = (_name),				\
> +	.shared = (_shared),				\
> +	.read =  iio_backend_ext_info_get,		\
> +	.write = iio_backend_ext_info_set,		\
> +	.private = (_what),				\
> +}
> +
>  /**
>   * struct iio_backend_data_fmt - Backend data format
>   * @type:		Data type.
> @@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
>   * @chan_enable:	Enable one channel.
>   * @chan_disable:	Disable one channel.
>   * @data_format_set:	Configure the data format for a specific channel.
> + * @data_source_set:	Configure the data source for a specific channel.
> + * @set_sample_rate:	Configure the sampling rate for a specific channel.
>   * @request_buffer:	Request an IIO buffer.
>   * @free_buffer:	Free an IIO buffer.
> + * @extend_chan_spec:	Extend an IIO channel.
> + * @ext_info_set:	Extended info setter.
> + * @ext_info_get:	Extended info getter.
>   **/
>  struct iio_backend_ops {
>  	int (*enable)(struct iio_backend *back);
> @@ -45,10 +71,21 @@ struct iio_backend_ops {
>  	int (*chan_disable)(struct iio_backend *back, unsigned int chan);
>  	int (*data_format_set)(struct iio_backend *back, unsigned int chan,
>  			       const struct iio_backend_data_fmt *data);
> +	int (*data_source_set)(struct iio_backend *back, unsigned int chan,
> +			       enum iio_backend_data_source data);
> +	int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
> +			       u64 sample_rate);

Name the parameter that so we know the units.  _hz?  


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

* Re: [PATCH 01/10] iio: buffer: add helper for setting direction
  2024-03-28 14:36   ` Jonathan Cameron
@ 2024-03-28 15:18     ` Nuno Sá
  2024-03-28 15:54       ` Jonathan Cameron
  0 siblings, 1 reply; 43+ messages in thread
From: Nuno Sá @ 2024-03-28 15:18 UTC (permalink / raw)
  To: Jonathan Cameron, Nuno Sa via B4 Relay
  Cc: nuno.sa, linux-iio, devicetree, Dragos Bogdan,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 2024-03-28 at 14:36 +0000, Jonathan Cameron wrote:
> On Thu, 28 Mar 2024 14:22:25 +0100
> Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> 
> > From: Nuno Sa <nuno.sa@analog.com>
> > 
> > Simple helper for setting the buffer direction when it's allocated using
> > iio_dmaengine_buffer_alloc().
> > 
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> I wonder if we should align with the approach for triggered-buffers with and _ext
> form of the registration function that takes a direction.  It seems odd to allocate
> one then change the direction.
> 

I agree it feels odd but I did not wanted to include buffer_impl.h in places that
should not have it :)

This patchseries adds the direction to devm_iio_dmaengine_buffer_setup(). Maybe what
we need is to have a non devm variant iio_dmaengine_buffer_setup() and turn
iio_dmaengine_buffer_alloc() static again. Maybe that would make things a bit more
consistent. In fact looking closer into that file, I would get rid of:

devm_iio_dmaengine_buffer_alloc() and __devm_iio_dmaengine_buffer_free() 

and have:

devm_iio_dmaengine_buffer_setup() and iio_dmaengine_buffer_setup() that make use of
iio_dmaengine_buffer_free() and iio_dmaengine_buffer_alloc(). 

I think it would make more sense like the above. Thoughts?

- Nuno Sá


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

* Re: [PATCH 09/10] iio: dac: add support for AXI DAC IP core
  2024-03-28 13:22   ` Nuno Sa
  (?)
@ 2024-03-28 15:35   ` Jonathan Cameron
  2024-03-28 16:43     ` Nuno Sá
  -1 siblings, 1 reply; 43+ messages in thread
From: Jonathan Cameron @ 2024-03-28 15:35 UTC (permalink / raw)
  To: Nuno Sa via B4 Relay
  Cc: nuno.sa, linux-iio, devicetree, Dragos Bogdan,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 28 Mar 2024 14:22:33 +0100
Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:

> From: Nuno Sa <nuno.sa@analog.com>
> 
> Support the Analog Devices Generic AXI DAC IP core. The IP core is used
> for interfacing with digital-to-analog (DAC) converters that require either
> a high-speed serial interface (JESD204B/C) or a source synchronous parallel
> interface (LVDS/CMOS). Typically (for such devices) SPI will be used for
> configuration only, while this IP core handles the streaming of data into
> memory via DMA.
> 
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>


A few minor things inline, but mostly seems fine to me.

Jonathan


...

> diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> new file mode 100644
> index 000000000000..0022ecb4e4bb
> --- /dev/null
> +++ b/drivers/iio/dac/adi-axi-dac.c


> +
> +enum {
> +	AXI_DAC_FREQ_TONE_1,
> +	AXI_DAC_FREQ_TONE_2,
> +	AXI_DAC_SCALE_TONE_1,
> +	AXI_DAC_SCALE_TONE_2,
> +	AXI_DAC_PHASE_TONE_1,
> +	AXI_DAC_PHASE_TONE_2,
> +};
> +
> +static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
> +				   unsigned int tone, unsigned int *freq)
> +{
> +	u32 reg, raw;
> +	int ret;
> +
> +	if (!st->dac_clk) {
> +		dev_err(st->dev, "Sampling rate is 0...\n");
> +		return -EINVAL;
> +	}
> +
> +	if (tone == AXI_DAC_FREQ_TONE_1)

Given this is matching 2 out of enum with other values, it would be more
locally readable as a switch statement with an error returning default.
Then we wouldn't need to look at the caller.

Or at the caller convert from the enum to 0,1 for all these functions.



> +		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
> +	else
> +		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
> +
> +	ret = regmap_read(st->regmap, reg, &raw);
> +	if (ret)
> +		return ret;
> +
> +	raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
> +	*freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
> +
> +	return 0;
> +}

...

> +static int axi_dac_scale_set(struct axi_dac_state *st,
> +			     const struct iio_chan_spec *chan,
> +			     const char *buf, size_t len, unsigned int tone)
> +{
> +	int integer, frac, scale;
> +	u32 raw = 0, reg;
> +	int ret;
> +
> +	ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
> +	if (ret)
> +		return ret;
> +
> +	scale = integer * MEGA + frac;
> +	if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
> +		return -EINVAL;
> +
> +	/*  format is 1.1.14 (sign, integer and fractional bits) */
> +	if (scale < 0) {
> +		raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
> +		scale *= -1;
> +	}
> +
> +	raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
> +
> +	if (tone == AXI_DAC_SCALE_TONE_1)
> +		reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
> +	else
> +		reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
> +
> +	guard(mutex)(&st->lock);
> +	ret = regmap_write(st->regmap, reg, raw);
> +	if (ret)
> +		return ret;
> +
> +	/* synchronize channels */
> +	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
> +	if (ret)
> +		return ret;
> +
> +	return len;
> +}
> +
> +static int axi_dac_phase_set(struct axi_dac_state *st,
> +			     const struct iio_chan_spec *chan,
> +			     const char *buf, size_t len, unsigned int tone)
> +{
> +	int integer, frac, phase;
> +	u32 raw, reg;
> +	int ret;
> +
> +	ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);

> +	if (ret)
> +		return ret;
> +
> +	phase = integer * MEGA + frac;
> +	if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
> +		return -EINVAL;
> +
> +	raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
> +
> +	if (tone == AXI_DAC_PHASE_TONE_1)
Preference for a switch so it's clear there are only 2 choices.
> +		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
> +	else
> +		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
> +
> +	guard(mutex)(&st->lock);
> +	ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
> +				 FIELD_PREP(AXI_DAC_PHASE, raw));
> +	if (ret)
> +		return ret;
> +
> +	/* synchronize channels */
> +	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
> +	if (ret)
> +		return ret;
> +
> +	return len;
> +}
> +
> +static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
> +				const struct iio_chan_spec *chan,
> +				const char *buf, size_t len)
> +{
> +	struct axi_dac_state *st = iio_backend_get_priv(back);
> +
> +	switch (private) {
> +	case AXI_DAC_FREQ_TONE_1:
> +	case AXI_DAC_FREQ_TONE_2:

Same as the get path - convert to which tone here so that the enum becomes
a tone index for the functions called and the mapping to that single enum
is kept clear of the lower level code.

> +		return axi_dac_frequency_set(st, chan, buf, len, private);
> +	case AXI_DAC_SCALE_TONE_1:
> +	case AXI_DAC_SCALE_TONE_2:
> +		return axi_dac_scale_set(st, chan, buf, len, private);
> +	case AXI_DAC_PHASE_TONE_1:
> +	case AXI_DAC_PHASE_TONE_2:
> +		return axi_dac_phase_set(st, chan, buf, len, private);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
> +				const struct iio_chan_spec *chan, char *buf)
> +{
> +	struct axi_dac_state *st = iio_backend_get_priv(back);
> +
> +	switch (private) {
> +	case AXI_DAC_FREQ_TONE_1:
> +	case AXI_DAC_FREQ_TONE_2:
> +		return axi_dac_frequency_get(st, chan, buf, private);
I'd break out private as an unsigned int here and then - AXI_DAC_FREQ_TONE_1
so that it is just which tone for all the calls made from here.
Similar for the following ones.

> +	case AXI_DAC_SCALE_TONE_1:
> +	case AXI_DAC_SCALE_TONE_2:
> +		return axi_dac_scale_get(st, chan, buf, private);
> +	case AXI_DAC_PHASE_TONE_1:
> +	case AXI_DAC_PHASE_TONE_2:
> +		return axi_dac_phase_get(st, chan, buf, private);
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
> +	IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
> +	IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
> +	IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
> +	IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
> +	IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
> +	IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
> +	{}
> +};
> +
> +static int axi_dac_extend_chan(struct iio_backend *back,
> +			       struct iio_chan_spec *chan)
> +{
> +	struct axi_dac_state *st = iio_backend_get_priv(back);
> +
> +	if (chan->type != IIO_ALTVOLTAGE)
> +		return -EINVAL;
> +	if (st->reg_config & AXI_DDS_DISABLE)
> +		/* nothing to extend */
> +		return 0;
> +
> +	chan->ext_info = axi_dac_ext_info;
> +
> +	return 0;
> +}

> +static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
> +				   u64 sample_rate)
> +{
> +	struct axi_dac_state *st = iio_backend_get_priv(back);
> +	unsigned int freq;
> +	int ret, tone;
> +
> +	if (!sample_rate)
> +		return -EINVAL;
> +	if (st->reg_config & AXI_DDS_DISABLE)
> +		/* nothing to care if DDS is disabled */
Rephrase this.  Is the point that the sample rate has no meaning without DDS or
that it has meaning but nothing to do here?
> +		return 0;
> +
> +	guard(mutex)(&st->lock);
> +	/*
> +	 * If dac_clk is 0 then this must be the first time we're being notified
> +	 * about the interface sample rate. Hence, just update our internal
> +	 * variable and bail... If it's not 0, then we get the current DDS
> +	 * frequency (for the old rate) and update the registers for the new
> +	 * sample rate.
> +	 */
> +	if (!st->dac_clk) {
> +		st->dac_clk = sample_rate;
> +		return 0;
> +	}
> +
> +	for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) {
> +		ret = __axi_dac_frequency_get(st, chan, tone, &freq);
> +		if (ret)
> +			return ret;
> +
> +		ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	st->dac_clk = sample_rate;
> +
> +	return 0;
> +}


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

* Re: [PATCH 08/10] iio: backend: add new functionality
  2024-03-28 15:16   ` Jonathan Cameron
@ 2024-03-28 15:42     ` Nuno Sá
  2024-03-28 15:59       ` Jonathan Cameron
  0 siblings, 1 reply; 43+ messages in thread
From: Nuno Sá @ 2024-03-28 15:42 UTC (permalink / raw)
  To: Jonathan Cameron, Nuno Sa via B4 Relay
  Cc: nuno.sa, linux-iio, devicetree, Dragos Bogdan,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 2024-03-28 at 15:16 +0000, Jonathan Cameron wrote:
> On Thu, 28 Mar 2024 14:22:32 +0100
> Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> 
> > From: Nuno Sa <nuno.sa@analog.com>
> > 
> > This adds the needed backend ops for supporting a backend inerfacing
> > with an high speed dac. The new ops are:
> > 
> > * data_source_set();
> > * set_sampling_freq();
> > * extend_chan_spec();
> > * ext_info_set();
> > * ext_info_get().
> > 
> > Also to note the new helpers that are meant to be used by the backends
> > when extending an IIO channel (adding extended info):
> > 
> > * iio_backend_ext_info_set();
> > * iio_backend_ext_info_get().
> > 
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> I'm pretty flexible on this so far as I think we are still learning how front
> ends and backends should interact. Maybe we'll figure that out in the medium
> term and rework this stuff. For now it looks fine. A few minor things inline.
> >  
> > +/**
> > + * iio_backend_ext_info_get - IIO ext_info read callback
> > + * @indio_dev:	IIO device
> > + * @private:	Data private to the driver
> > + * @chan:	IIO channel
> > + * @buf:	Buffer where to place the attribute data
> > + *
> > + * This helper is intended to be used by backends that extend an IIO channel
> > + * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
> > + * backends are not supposed to give their own callbacks (as they would not
> > + * a way to get te backend from indio_dev). This is the getter.
> 
> te->the?

Yes and some more typos :).

> 
> 
> > +/**
> > + * iio_backend_extend_chan_spec - Extend an IIO channel
> > + * @indio_dev:	IIO device
> > + * @back:	Backend device
> > + * @chan:	IIO channel
> > + *
> > + * Some backends may have their own functionalities and hence capable of
> > + * extending a frontend's channel.
> > + *
> > + * RETURNS:
> > + * 0 on success, negative error number on failure.
> > + */
> > +int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
> > +				 struct iio_backend *back,
> > +				 struct iio_chan_spec *chan)
> > +{
> > +	const struct iio_chan_spec_ext_info *ext_info = chan->ext_info;
> This is getting confusing.  So this one is the front end value of ext_info?
> Name it as such frontend_ext_info

Yes, it's the frontend pointer. Just to enforce the below constrain. Will rename as
suggested.

> 
> > +	int ret;
> > +
> > +	ret = iio_backend_op_call(back, extend_chan_spec, chan);
> > +	if (ret)
> > +		return ret;
> > +	/*
> > +	 * Let's keep things simple for now. Don't allow to overwrite the
> > +	 * frontend's extended info. If ever needed, we can support appending
> > +	 * it.
> > +	 */
> > +	if (ext_info && chan->ext_info != ext_info)
> > +		return -EOPNOTSUPP;
> > +	if (!chan->ext_info)
> 
> This is checking if the backend added anything? Perhaps a comment on that
> as we don't need a backend_ext_info local variable...

Yes, but just regarding ext_info as that is the only thing we're handling below
(doing some sanity checks). Note that (as you said above) I'm keeping things a bit
"open" in that the backend is open to extend whatever it wants. With time we may
learn better what do we want constrain or not.

> 
> > +		return 0;
> > +	/*
> > +	 * !\NOTE: this will break as soon as we have multiple backends on one
> > +	 * frontend and all of them extend channels. In that case, the core
> > +	 * backend code has no way to get the correct backend given the
> > +	 * iio device.
> > +	 *
> > +	 * One solution for this could be introducing a new backend
> > +	 * dedicated callback in struct iio_info so we can callback into the
> > +	 * frontend so it can give us the right backend given a chan_spec.
> > +	 */
> 
> Hmm. This is indeed messy.  Could we associate it with the buffer as presuably
> a front end with multiple backends is using multiple IIO buffers?
> 

Hmm, the assumption of having multiple buffers seems plausible to me but considering
the example we have in hands it would be cumbersome to get the backend. Considering
iio_backend_ext_info_get(), how could we get the backend if it was associated to one
of the IIO buffers? I think we would need more "intrusive" changes to make that work
or do you have something in mind=
 
> As you say a dance via the front end would work fine.

I'm happy you're also open for a proper solution already. I mention this in the
cover. My idea was something like (consider the iio_backend_ext_info_get()):

if (!indio_dev->info->get_iio_backend())
	return -EOPNOTSUPP;

back = indio_dev->info->get_iio_backend(indio_dev, chan_spec);

It would be nice to have some "default/generic" implementation for cases where we
only have one backend per frontend so that the frontend would not need to define the
callback.
  
> 
> 
> > +	iio_device_set_drvdata(indio_dev, back);
> > +
> > +	/* Don't allow backends to get creative and force their own handlers */
> > +	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> > +		if (ext_info->read != iio_backend_ext_info_get)
> > +			return -EINVAL;
> > +		if (ext_info->write != iio_backend_ext_info_set)
> > +			return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
> 
> > diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
> > index a6d79381866e..09ff2f8f9fd8 100644
> > --- a/include/linux/iio/backend.h
> > +++ b/include/linux/iio/backend.h
> > @@ -4,6 +4,7 @@
> >  
> >  #include <linux/types.h>
> >  
> > +struct iio_chan_spec;
> >  struct fwnode_handle;
> >  struct iio_backend;
> >  struct device;
> > @@ -15,6 +16,26 @@ enum iio_backend_data_type {
> >  	IIO_BACKEND_DATA_TYPE_MAX
> >  };
> >  
> > +enum iio_backend_data_source {
> > +	IIO_BACKEND_INTERNAL_CW,
> 
> CW?  Either expand out what ever that is in definition of add a comment
> at least.

Continuous wave :)

> 
> > +	IIO_BACKEND_EXTERNAL,
> What does external mean in this case?

In this particular case comes from a DMA source (IP). I thought external to be more
generic but if you prefer, I can do something like IIO_BACKEND_DMA?


> > +	IIO_BACKEND_DATA_SOURCE_MAX
> > +};
> > +
> > +/**
> > + * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
> > + * @_name:	Attribute name
> > + * @_shared:	Whether the attribute is shared between all channels
> > + * @_what:	Data private to the driver
> > + */
> > +#define IIO_BACKEND_EX_INFO(_name, _shared, _what) {	\
> > +	.name = (_name),				\
> > +	.shared = (_shared),				\
> > +	.read =  iio_backend_ext_info_get,		\
> > +	.write = iio_backend_ext_info_set,		\
> > +	.private = (_what),				\
> > +}
> > +
> >  /**
> >   * struct iio_backend_data_fmt - Backend data format
> >   * @type:		Data type.
> > @@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
> >   * @chan_enable:	Enable one channel.
> >   * @chan_disable:	Disable one channel.
> >   * @data_format_set:	Configure the data format for a specific channel.
> > + * @data_source_set:	Configure the data source for a specific channel.
> > + * @set_sample_rate:	Configure the sampling rate for a specific channel.
> >   * @request_buffer:	Request an IIO buffer.
> >   * @free_buffer:	Free an IIO buffer.
> > + * @extend_chan_spec:	Extend an IIO channel.
> > + * @ext_info_set:	Extended info setter.
> > + * @ext_info_get:	Extended info getter.
> >   **/
> >  struct iio_backend_ops {
> >  	int (*enable)(struct iio_backend *back);
> > @@ -45,10 +71,21 @@ struct iio_backend_ops {
> >  	int (*chan_disable)(struct iio_backend *back, unsigned int chan);
> >  	int (*data_format_set)(struct iio_backend *back, unsigned int chan,
> >  			       const struct iio_backend_data_fmt *data);
> > +	int (*data_source_set)(struct iio_backend *back, unsigned int chan,
> > +			       enum iio_backend_data_source data);
> > +	int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
> > +			       u64 sample_rate);
> 
> Name the parameter that so we know the units.  _hz? 

yes. And u64 to not fall in the CCF problem for 32bits :)

- Nuno Sá


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

* Re: [PATCH 10/10] iio: dac: support the ad9739a RF DAC
  2024-03-28 13:22   ` Nuno Sa
  (?)
@ 2024-03-28 15:51   ` Jonathan Cameron
  2024-03-28 16:37     ` Nuno Sá
  -1 siblings, 1 reply; 43+ messages in thread
From: Jonathan Cameron @ 2024-03-28 15:51 UTC (permalink / raw)
  To: Nuno Sa via B4 Relay
  Cc: nuno.sa, linux-iio, devicetree, Dragos Bogdan,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 28 Mar 2024 14:22:34 +0100
Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:

> From: Nuno Sa <nuno.sa@analog.com>
> 
> The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
> of synthesizing wideband signals from dc up to 3 GHz.
DC perhaps

> 
> A dual-port, source synchronous, LVDS interface simplifies the digital
> interface with existing FGPA/ASIC technology. On-chip controllers are used
> to manage external and internal clock domain variations over temperature to
> ensure reliable data transfer from the host to the DAC core.
> 
> Co-developed-by: Dragos Bogdan <dragos.bogdan@analog.com>
> Signed-off-by: Dragos Bogdan <dragos.bogdan@analog.com>
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
Hi Nuno,

A few questions / comments inline but on the whole looking good to me.

Jonathan

> ---
>  Documentation/ABI/testing/sysfs-bus-iio-ad9739a |  17 +
>  MAINTAINERS                                     |   1 +
>  drivers/iio/dac/Kconfig                         |  16 +
>  drivers/iio/dac/Makefile                        |   1 +
>  drivers/iio/dac/ad9739a.c                       | 445 ++++++++++++++++++++++++
>  5 files changed, 480 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ad9739a b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
> new file mode 100644
> index 000000000000..8a8a5cd10386
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
> @@ -0,0 +1,17 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
> +KernelVersion:	6.9
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Dac operating mode. One of the following modes can be selected:

DAC operating mode. ...

> +         * normal: This is DAC normal mode.
> +         * mixed-mode: In this mode the output is effectively chopped at the

Spaces and tabs mixed...

> +                       DAC sample rate. This has the effect of reducing the
> +                       power of the fundamental signal while increasing the
> +                       power of the images centered around the DAC sample rate,
> +                       thus improving the output power of these images.

Any idea why it is called mixed mode?  Name doesn't suggest to me what the Docs say
this does.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
> +KernelVersion:	6.9
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Available operating modes.

>  M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> index 7c0a8caa9a34..ee0d9798d8b4 100644
> --- a/drivers/iio/dac/Kconfig
> +++ b/drivers/iio/dac/Kconfig
> @@ -131,6 +131,22 @@ config AD5624R_SPI
>  	  Say yes here to build support for Analog Devices AD5624R, AD5644R and
>  	  AD5664R converters (DAC). This driver uses the common SPI interface.
>  
> +config AD9739A
> +	tristate "Analog Devices AD9739A RF DAC spi driver"
> +	depends on SPI
> +	select REGMAP_SPI
> +	select IIO_BACKEND
> +	help
> +	  Say yes here to build support for Analog Devices AD9739A Digital-to
> +	  Analog Converter.
> +
> +	  The driver requires the assistance of the AXI DAC IP core to operate,

Maybe a depends on || COMPILE_TEST to increase build coverage (compared to
a hard depends on)

> +	  since SPI is used for configuration only, while data has to be
> +	  streamed into memory via DMA.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called ad9739a.
> +


> diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
> new file mode 100644
> index 000000000000..46431fa345a5
> --- /dev/null
> +++ b/drivers/iio/dac/ad9739a.c

> +
> +enum {
> +	AD9739A_NORMAL_MODE,
> +	AD9739A_MIXED_MODE = 2,

Push these next to the relevant registers and more conventional defines.
Not seeing why the enum helps much here.

> +};
> +
> +static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
> +				 const struct iio_chan_spec *chan)
> +{
> +	struct ad9739a_state *st = iio_priv(indio_dev);
> +	u32 mode;
> +	int ret;
> +
> +	ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
> +	if (ret)
> +		return ret;
> +
> +	mode = FIELD_GET(AD9739A_DAC_DEC, mode);
> +	/* sanity check we get valid values from the HW */
> +	if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
> +		return -EIO;
> +	if (!mode)
> +		return AD9739A_NORMAL_MODE;
> +
> +	return AD9739A_MIXED_MODE - 1;

As below. I'd like to see a mapping function, or lookup table or similar
rather than handling this conversion in code.

> +}
> +
> +static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
> +				 const struct iio_chan_spec *chan, u32 mode)
> +{
> +	struct ad9739a_state *st = iio_priv(indio_dev);
> +
> +	if (mode == AD9739A_MIXED_MODE - 1)
> +		mode = AD9739A_MIXED_MODE;

Why?  Feels like a comment is needed. Or a more obvious conversion function.

> +
> +	return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
> +				  AD9739A_DAC_DEC, mode);
> +}
> +
> +static int ad9739a_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct ad9739a_state *st = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val = st->sample_rate;
> +		*val2 = 0;
> +		return IIO_VAL_INT_64;

Big numbers :)

> +	default:
> +		return -EINVAL;
> +	}
> +}
> +


> +
> +/*
> + * Recommended values (as per datasheet) for the dac clk common mode voltage
> + * and Mu controller. Look at table 29.
> + */
> +static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
> +	/* DAC clk common mode voltage */
> +	{AD9739A_REG_CROSS_CNT1, 0x0f},
	{ AD9739A_REG_CROSS_CNT1, 0x0f },
etc is more readable in my opinion so is always my preference in IIO.

> +	{AD9739A_REG_CROSS_CNT2, 0x0f},
> +	/* Mu controller configuration */
> +	{AD9739A_REG_PHS_DET, 0x30},
> +	{AD9739A_REG_MU_DUTY, 0x80},
> +	{AD9739A_REG_MU_CNT2, 0x44},
> +	{AD9739A_REG_MU_CNT3, 0x6c},
> +};
> +
> +static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
> +{
> +	unsigned int i = 0, lock, fsc;
> +	u32 fsc_raw;
> +	int ret;
> +
> +	ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
> +				     ARRAY_SIZE(ad9739a_clk_mu_ctrl));
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Try to get the MU lock. Repeat the below steps AD9739A_LOCK_N_TRIES
> +	 * (as specified by the datasheet) until we get the lock.
> +	 */
> +	do {
> +		ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
> +				   AD9739A_MU_CNT4_DEFAULT);
> +		if (ret)
> +			return ret;
> +
> +		/* Enable the Mu controller search and track mode. */

MU for consistency

> +		ret = regmap_set_bits(st->regmap, AD9739A_REG_MU_CNT1,
> +				      AD9739A_MU_EN_MASK);
> +		if (ret)
> +			return ret;
> +
> +		/* Ensure the DLL loop is locked */
> +		ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
> +					       lock, lock & AD9739A_MU_LOCK_MASK,
> +					       0, 1000);
		if (ret < 0 && ret != -ETIMEOUT)
			return ret;

i.e. deal with error codes that don't meant it timed out.

> +	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
> +
> +	if (i == AD9739A_LOCK_N_TRIES)
> +		return dev_err_probe(dev, ret, "Mu lock timeout\n");
> +
> +	/* Receiver tracking and lock. Same deal as the Mu controller */

MU or Mu.  Either fine but be consistent in comments. I have no idea what this is
so can't say which is better.

> +	i = 0;
> +	do {
> +		ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
> +					 AD9739A_FINE_DEL_SKW_MASK,
> +					 FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2));
> +		if (ret)
> +			return ret;
> +
> +		/* Disable the receiver and the loop. */
> +		ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
> +		if (ret)
> +			return ret;
> +
> +		/*
> +		 * Re-enable the loop so it falls out of lock and begins the
> +		 * search/track routine again.
> +		 */
> +		ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
> +				      AD9739A_RCVR_LOOP_EN_MASK);
> +		if (ret)
> +			return ret;
> +
> +		/* Ensure the DLL loop is locked */
> +		ret = regmap_read_poll_timeout(st->regmap,
> +					       AD9739A_REG_LVDS_REC_STAT9, lock,
> +					       lock == AD9739A_RCVR_TRACK_AND_LOCK,
> +					       0, 1000);

As above, consider other error codes than -ETIMEOUT;

> +	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
> +
> +	if (i == AD9739A_LOCK_N_TRIES)
> +		return dev_err_probe(dev, ret, "Receiver lock timeout\n");
> +
> +	ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
> +	if (ret && ret == -EINVAL)
> +		return 0;
> +	if (ret)
> +		return ret;
> +	if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Invalid full scale current(%u) [%u %u]\n",
> +				     fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
> +	/*
> +	 * IOUTFS is given by
> +	 *	Ioutfs = 0.0226 * FSC + 8.58
> +	 * and is given in mA. Hence we'll have to multiply by 10 * MILLI in
> +	 * order to get rid of the fractional.
> +	 */
> +	fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
> +
> +	ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
> +	if (ret)
> +		return ret;
> +
> +	return regmap_update_bits(st->regmap, AD9739A_REG_FSC_1,
> +				  AD9739A_FSC_MSB, fsc_raw >> 8);
> +}



> +
> +static int ad9739a_probe(struct spi_device *spi)
> +{
> +	struct device *dev = &spi->dev;
> +	struct iio_dev *indio_dev;
> +	struct ad9739a_state *st;
> +	unsigned int id;
> +	struct clk *clk;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	st = iio_priv(indio_dev);
> +
> +	clk = devm_clk_get_enabled(dev, NULL);
> +	if (IS_ERR(clk))
> +		return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n");
> +
> +	st->sample_rate = clk_get_rate(clk);
> +	if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
> +		      AD9739A_DAC_CLK_RANGE))
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Invalid dac clk range(%lu) [%lu %lu]\n",
> +				     st->sample_rate, AD9739A_MIN_DAC_CLK,
> +				     AD9739A_MAX_DAC_CLK);
> +
> +	st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
> +	if (IS_ERR(st->regmap))
> +		return PTR_ERR(st->regmap);
> +
> +	ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
> +	if (ret)
> +		return ret;
> +
> +	if (id != AD9739A_ID)
> +		return dev_err_probe(dev, -ENODEV, "Unrecognized CHIP_ID 0x%X",
> +				     id);
Do we have to give up here?  Could it be a compatible future part?
If so we should fallback on what firmware told us it was + perhaps a
dev_info() to say we don't recognise the ID register value.


> +
> +	ret = ad9739a_reset(dev, st);
> +	if (ret)
> +		return ret;
> +
> +	ret = ad9739a_init(dev, st);
> +	if (ret)
> +		return ret;
> +
> +	st->back = devm_iio_backend_get(dev, NULL);
> +	if (IS_ERR(st->back))
> +		return PTR_ERR(st->back);
> +
> +	ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_backend_extend_chan_spec(indio_dev, st->back,
> +					   &ad9739a_channels[0]);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_iio_backend_enable(dev, st->back);
> +	if (ret)
> +		return ret;
> +
> +	indio_dev->name = "ad9739a";
> +	indio_dev->info = &ad9739a_info;
> +	indio_dev->channels = ad9739a_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels);
> +	indio_dev->setup_ops = &ad9739a_buffer_setup_ops;
> +
> +	return devm_iio_device_register(&spi->dev, indio_dev);
> +}


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

* Re: [PATCH 01/10] iio: buffer: add helper for setting direction
  2024-03-28 15:18     ` Nuno Sá
@ 2024-03-28 15:54       ` Jonathan Cameron
  0 siblings, 0 replies; 43+ messages in thread
From: Jonathan Cameron @ 2024-03-28 15:54 UTC (permalink / raw)
  To: Nuno Sá
  Cc: Nuno Sa via B4 Relay, nuno.sa, linux-iio, devicetree,
	Dragos Bogdan, Lars-Peter Clausen, Michael Hennerich,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 28 Mar 2024 16:18:04 +0100
Nuno Sá <noname.nuno@gmail.com> wrote:

> On Thu, 2024-03-28 at 14:36 +0000, Jonathan Cameron wrote:
> > On Thu, 28 Mar 2024 14:22:25 +0100
> > Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> >   
> > > From: Nuno Sa <nuno.sa@analog.com>
> > > 
> > > Simple helper for setting the buffer direction when it's allocated using
> > > iio_dmaengine_buffer_alloc().
> > > 
> > > Signed-off-by: Nuno Sa <nuno.sa@analog.com>  
> > I wonder if we should align with the approach for triggered-buffers with and _ext
> > form of the registration function that takes a direction.  It seems odd to allocate
> > one then change the direction.
> >   
> 
> I agree it feels odd but I did not wanted to include buffer_impl.h in places that
> should not have it :)
> 
> This patchseries adds the direction to devm_iio_dmaengine_buffer_setup(). Maybe what
> we need is to have a non devm variant iio_dmaengine_buffer_setup() and turn
> iio_dmaengine_buffer_alloc() static again. Maybe that would make things a bit more
> consistent. In fact looking closer into that file, I would get rid of:
> 
> devm_iio_dmaengine_buffer_alloc() and __devm_iio_dmaengine_buffer_free() 
> 
> and have:
> 
> devm_iio_dmaengine_buffer_setup() and iio_dmaengine_buffer_setup() that make use of
> iio_dmaengine_buffer_free() and iio_dmaengine_buffer_alloc(). 
> 
> I think it would make more sense like the above. Thoughts?

Sounds reasonable to me

Jonathan

> 
> - Nuno Sá
> 


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

* Re: [PATCH 08/10] iio: backend: add new functionality
  2024-03-28 15:42     ` Nuno Sá
@ 2024-03-28 15:59       ` Jonathan Cameron
  2024-03-28 16:54         ` Nuno Sá
  0 siblings, 1 reply; 43+ messages in thread
From: Jonathan Cameron @ 2024-03-28 15:59 UTC (permalink / raw)
  To: Nuno Sá
  Cc: Nuno Sa via B4 Relay, nuno.sa, linux-iio, devicetree,
	Dragos Bogdan, Lars-Peter Clausen, Michael Hennerich,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 28 Mar 2024 16:42:38 +0100
Nuno Sá <noname.nuno@gmail.com> wrote:

> On Thu, 2024-03-28 at 15:16 +0000, Jonathan Cameron wrote:
> > On Thu, 28 Mar 2024 14:22:32 +0100
> > Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> >   
> > > From: Nuno Sa <nuno.sa@analog.com>
> > > 
> > > This adds the needed backend ops for supporting a backend inerfacing
> > > with an high speed dac. The new ops are:
> > > 
> > > * data_source_set();
> > > * set_sampling_freq();
> > > * extend_chan_spec();
> > > * ext_info_set();
> > > * ext_info_get().
> > > 
> > > Also to note the new helpers that are meant to be used by the backends
> > > +		return 0;
> > > +	/*
> > > +	 * !\NOTE: this will break as soon as we have multiple backends on one
> > > +	 * frontend and all of them extend channels. In that case, the core
> > > +	 * backend code has no way to get the correct backend given the
> > > +	 * iio device.
> > > +	 *
> > > +	 * One solution for this could be introducing a new backend
> > > +	 * dedicated callback in struct iio_info so we can callback into the
> > > +	 * frontend so it can give us the right backend given a chan_spec.
> > > +	 */  
> > 
> > Hmm. This is indeed messy.  Could we associate it with the buffer as presuably
> > a front end with multiple backends is using multiple IIO buffers?
> >   
> 
> Hmm, the assumption of having multiple buffers seems plausible to me but considering
> the example we have in hands it would be cumbersome to get the backend. Considering
> iio_backend_ext_info_get(), how could we get the backend if it was associated to one
> of the IIO buffers? I think we would need more "intrusive" changes to make that work
> or do you have something in mind=

Nope. Just trying to get my head around the associations. I hadn't thought about
how to make that visible in the code.  Probably a callabck anyway.

>  
> > As you say a dance via the front end would work fine.  
> 
> I'm happy you're also open for a proper solution already. I mention this in the
> cover. My idea was something like (consider the iio_backend_ext_info_get()):
> 
> if (!indio_dev->info->get_iio_backend())
> 	return -EOPNOTSUPP;
> 
> back = indio_dev->info->get_iio_backend(indio_dev, chan_spec);
> 
> It would be nice to have some "default/generic" implementation for cases where we
> only have one backend per frontend so that the frontend would not need to define the
> callback.
Agreed - either a default that means if the callback isn't provided we get the
single backend or if that proves fiddly at least a standard callback we can
use in all such cases.

>   
> > 
> >   
> > > +	iio_device_set_drvdata(indio_dev, back);
> > > +
> > > +	/* Don't allow backends to get creative and force their own handlers */
> > > +	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> > > +		if (ext_info->read != iio_backend_ext_info_get)
> > > +			return -EINVAL;
> > > +		if (ext_info->write != iio_backend_ext_info_set)
> > > +			return -EINVAL;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);  
> >   
> > > diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
> > > index a6d79381866e..09ff2f8f9fd8 100644
> > > --- a/include/linux/iio/backend.h
> > > +++ b/include/linux/iio/backend.h
> > > @@ -4,6 +4,7 @@
> > >  
> > >  #include <linux/types.h>
> > >  
> > > +struct iio_chan_spec;
> > >  struct fwnode_handle;
> > >  struct iio_backend;
> > >  struct device;
> > > @@ -15,6 +16,26 @@ enum iio_backend_data_type {
> > >  	IIO_BACKEND_DATA_TYPE_MAX
> > >  };
> > >  
> > > +enum iio_backend_data_source {
> > > +	IIO_BACKEND_INTERNAL_CW,  
> > 
> > CW?  Either expand out what ever that is in definition of add a comment
> > at least.  
> 
> Continuous wave :)

Spell that out.

> 
> >   
> > > +	IIO_BACKEND_EXTERNAL,  
> > What does external mean in this case?  
> 
> In this particular case comes from a DMA source (IP). I thought external to be more
> generic but if you prefer, I can do something like IIO_BACKEND_DMA?

So from another IP block?   For that to be reasonably 'generic' we'd need a way
to known where it was coming from.

Now I remember advantage of reviewing on weekends - fewer replies during the reviews :)

Jonathan


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

* Re: [PATCH 10/10] iio: dac: support the ad9739a RF DAC
  2024-03-28 15:51   ` Jonathan Cameron
@ 2024-03-28 16:37     ` Nuno Sá
  2024-03-28 16:52       ` Jonathan Cameron
  0 siblings, 1 reply; 43+ messages in thread
From: Nuno Sá @ 2024-03-28 16:37 UTC (permalink / raw)
  To: Jonathan Cameron, Nuno Sa via B4 Relay
  Cc: nuno.sa, linux-iio, devicetree, Dragos Bogdan,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 2024-03-28 at 15:51 +0000, Jonathan Cameron wrote:
> On Thu, 28 Mar 2024 14:22:34 +0100
> Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> 
> > From: Nuno Sa <nuno.sa@analog.com>
> > 
> > The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
> > of synthesizing wideband signals from dc up to 3 GHz.
> DC perhaps
> 
> > 
> > A dual-port, source synchronous, LVDS interface simplifies the digital
> > interface with existing FGPA/ASIC technology. On-chip controllers are used
> > to manage external and internal clock domain variations over temperature to
> > ensure reliable data transfer from the host to the DAC core.
> > 
> > Co-developed-by: Dragos Bogdan <dragos.bogdan@analog.com>
> > Signed-off-by: Dragos Bogdan <dragos.bogdan@analog.com>
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> Hi Nuno,
> 
> A few questions / comments inline but on the whole looking good to me.
> 
> Jonathan
> 
> > ---
> >  Documentation/ABI/testing/sysfs-bus-iio-ad9739a |  17 +
> >  MAINTAINERS                                     |   1 +
> >  drivers/iio/dac/Kconfig                         |  16 +
> >  drivers/iio/dac/Makefile                        |   1 +
> >  drivers/iio/dac/ad9739a.c                       | 445 ++++++++++++++++++++++++
> >  5 files changed, 480 insertions(+)
> > 
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
> > b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
> > new file mode 100644
> > index 000000000000..8a8a5cd10386
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
> > @@ -0,0 +1,17 @@
> > +What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
> > +KernelVersion:	6.9
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Dac operating mode. One of the following modes can be selected:
> 
> DAC operating mode. ...
> 
> > +         * normal: This is DAC normal mode.
> > +         * mixed-mode: In this mode the output is effectively chopped at the
> 
> Spaces and tabs mixed...
> 
> > +                       DAC sample rate. This has the effect of reducing the
> > +                       power of the fundamental signal while increasing the
> > +                       power of the images centered around the DAC sample rate,
> > +                       thus improving the output power of these images.
> 
> Any idea why it is called mixed mode?  Name doesn't suggest to me what the Docs say
> this does.

Nope, just respecting the datasheet names for the modes. But I may give it another
read. Likely there was a reason for that naming :)

> 
> > +
> > +What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_ava
> > ilable
> > +KernelVersion:	6.9
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Available operating modes.
> 
> >  M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
> > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> > index 7c0a8caa9a34..ee0d9798d8b4 100644
> > --- a/drivers/iio/dac/Kconfig
> > +++ b/drivers/iio/dac/Kconfig
> > @@ -131,6 +131,22 @@ config AD5624R_SPI
> >  	  Say yes here to build support for Analog Devices AD5624R, AD5644R and
> >  	  AD5664R converters (DAC). This driver uses the common SPI interface.
> >  
> > +config AD9739A
> > +	tristate "Analog Devices AD9739A RF DAC spi driver"
> > +	depends on SPI
> > +	select REGMAP_SPI
> > +	select IIO_BACKEND
> > +	help
> > +	  Say yes here to build support for Analog Devices AD9739A Digital-to
> > +	  Analog Converter.
> > +
> > +	  The driver requires the assistance of the AXI DAC IP core to operate,
> 
> Maybe a depends on || COMPILE_TEST to increase build coverage (compared to
> a hard depends on)
> 

Can do that...

> > +	  since SPI is used for configuration only, while data has to be
> > +	  streamed into memory via DMA.
> > +
> > +	  To compile this driver as a module, choose M here: the module will be
> > +	  called ad9739a.
> > +
> 
> 
> > diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
> > new file mode 100644
> > index 000000000000..46431fa345a5
> > --- /dev/null
> > +++ b/drivers/iio/dac/ad9739a.c
> 
> > +
> > +enum {
> > +	AD9739A_NORMAL_MODE,
> > +	AD9739A_MIXED_MODE = 2,
> 
> Push these next to the relevant registers and more conventional defines.
> Not seeing why the enum helps much here.

Alright..

> 
> > +};
> > +
> > +static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
> > +				 const struct iio_chan_spec *chan)
> > +{
> > +	struct ad9739a_state *st = iio_priv(indio_dev);
> > +	u32 mode;
> > +	int ret;
> > +
> > +	ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
> > +	if (ret)
> > +		return ret;
> > +
> > +	mode = FIELD_GET(AD9739A_DAC_DEC, mode);
> > +	/* sanity check we get valid values from the HW */
> > +	if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
> > +		return -EIO;
> > +	if (!mode)
> > +		return AD9739A_NORMAL_MODE;
> > +
> > +	return AD9739A_MIXED_MODE - 1;
> 
> As below. I'd like to see a mapping function, or lookup table or similar
> rather than handling this conversion in code.
> 
> > +}
> > +
> > +static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
> > +				 const struct iio_chan_spec *chan, u32 mode)
> > +{
> > +	struct ad9739a_state *st = iio_priv(indio_dev);
> > +
> > +	if (mode == AD9739A_MIXED_MODE - 1)
> > +		mode = AD9739A_MIXED_MODE;
> 
> Why?  Feels like a comment is needed. Or a more obvious conversion function.
> 

To match what we want to write in the register... With just two values it's too
simple that opt not to have any helper function or table. Would you be fine with a
comment?

> > +
> > +	return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
> > +				  AD9739A_DAC_DEC, mode);
> > +}
> > +
> > +static int ad9739a_read_raw(struct iio_dev *indio_dev,
> > +			    struct iio_chan_spec const *chan,
> > +			    int *val, int *val2, long mask)
> > +{
> > +	struct ad9739a_state *st = iio_priv(indio_dev);
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > +		*val = st->sample_rate;
> > +		*val2 = 0;
> > +		return IIO_VAL_INT_64;
> 
> Big numbers :)

My setup is using 2.5Ghz which is big enough to overflow INT but would work on UINT.
> 

> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> 
> 
> > +
> > +/*
> > + * Recommended values (as per datasheet) for the dac clk common mode voltage
> > + * and Mu controller. Look at table 29.
> > + */
> > +static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
> > +	/* DAC clk common mode voltage */
> > +	{AD9739A_REG_CROSS_CNT1, 0x0f},
> 	{ AD9739A_REG_CROSS_CNT1, 0x0f },
> etc is more readable in my opinion so is always my preference in IIO.
> 
> > +	{AD9739A_REG_CROSS_CNT2, 0x0f},
> > +	/* Mu controller configuration */
> > +	{AD9739A_REG_PHS_DET, 0x30},
> > +	{AD9739A_REG_MU_DUTY, 0x80},
> > +	{AD9739A_REG_MU_CNT2, 0x44},
> > +	{AD9739A_REG_MU_CNT3, 0x6c},
> > +};
> > +
> > +static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
> > +{
> > +	unsigned int i = 0, lock, fsc;
> > +	u32 fsc_raw;
> > +	int ret;
> > +
> > +	ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
> > +				     ARRAY_SIZE(ad9739a_clk_mu_ctrl));
> > +	if (ret)
> > +		return ret;
> > +
> > +	/*
> > +	 * Try to get the MU lock. Repeat the below steps AD9739A_LOCK_N_TRIES
> > +	 * (as specified by the datasheet) until we get the lock.
> > +	 */
> > +	do {
> > +		ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
> > +				   AD9739A_MU_CNT4_DEFAULT);
> > +		if (ret)
> > +			return ret;
> > +
> > +		/* Enable the Mu controller search and track mode. */
> 
> MU for consistency

ack

> 
> > +		ret = regmap_set_bits(st->regmap, AD9739A_REG_MU_CNT1,
> > +				      AD9739A_MU_EN_MASK);
> > +		if (ret)
> > +			return ret;
> > +
> > +		/* Ensure the DLL loop is locked */
> > +		ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
> > +					       lock, lock &
> > AD9739A_MU_LOCK_MASK,
> > +					       0, 1000);
> 		if (ret < 0 && ret != -ETIMEOUT)
> 			return ret;
> 
> i.e. deal with error codes that don't meant it timed out.
> 

Oh yes, that makes sense.

> > +	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
> > +
> > +	if (i == AD9739A_LOCK_N_TRIES)
> > +		return dev_err_probe(dev, ret, "Mu lock timeout\n");
> > +
> > +	/* Receiver tracking and lock. Same deal as the Mu controller */
> 
> MU or Mu.  Either fine but be consistent in comments. I have no idea what this is
> so can't say which is better.
> 
> > +	i = 0;
> > +	do {
> > +		ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
> > +					 AD9739A_FINE_DEL_SKW_MASK,
> > +					 FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK,
> > 2));
> > +		if (ret)
> > +			return ret;
> > +
> > +		/* Disable the receiver and the loop. */
> > +		ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
> > +		if (ret)
> > +			return ret;
> > +
> > +		/*
> > +		 * Re-enable the loop so it falls out of lock and begins the
> > +		 * search/track routine again.
> > +		 */
> > +		ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
> > +				      AD9739A_RCVR_LOOP_EN_MASK);
> > +		if (ret)
> > +			return ret;
> > +
> > +		/* Ensure the DLL loop is locked */
> > +		ret = regmap_read_poll_timeout(st->regmap,
> > +					       AD9739A_REG_LVDS_REC_STAT9, lock,
> > +					       lock ==
> > AD9739A_RCVR_TRACK_AND_LOCK,
> > +					       0, 1000);
> 
> As above, consider other error codes than -ETIMEOUT;
> 
> > +	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
> > +
> > +	if (i == AD9739A_LOCK_N_TRIES)
> > +		return dev_err_probe(dev, ret, "Receiver lock timeout\n");
> > +
> > +	ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
> > +	if (ret && ret == -EINVAL)
> > +		return 0;
> > +	if (ret)
> > +		return ret;
> > +	if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
> > +		return dev_err_probe(dev, -EINVAL,
> > +				     "Invalid full scale current(%u) [%u %u]\n",
> > +				     fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
> > +	/*
> > +	 * IOUTFS is given by
> > +	 *	Ioutfs = 0.0226 * FSC + 8.58
> > +	 * and is given in mA. Hence we'll have to multiply by 10 * MILLI in
> > +	 * order to get rid of the fractional.
> > +	 */
> > +	fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
> > +
> > +	ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return regmap_update_bits(st->regmap, AD9739A_REG_FSC_1,
> > +				  AD9739A_FSC_MSB, fsc_raw >> 8);
> > +}
> 
> 
> 
> > +
> > +static int ad9739a_probe(struct spi_device *spi)
> > +{
> > +	struct device *dev = &spi->dev;
> > +	struct iio_dev *indio_dev;
> > +	struct ad9739a_state *st;
> > +	unsigned int id;
> > +	struct clk *clk;
> > +	int ret;
> > +
> > +	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
> > +	if (!indio_dev)
> > +		return -ENOMEM;
> > +
> > +	st = iio_priv(indio_dev);
> > +
> > +	clk = devm_clk_get_enabled(dev, NULL);
> > +	if (IS_ERR(clk))
> > +		return dev_err_probe(dev, PTR_ERR(clk), "Could not get
> > clkin\n");
> > +
> > +	st->sample_rate = clk_get_rate(clk);
> > +	if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
> > +		      AD9739A_DAC_CLK_RANGE))
> > +		return dev_err_probe(dev, -EINVAL,
> > +				     "Invalid dac clk range(%lu) [%lu %lu]\n",
> > +				     st->sample_rate, AD9739A_MIN_DAC_CLK,
> > +				     AD9739A_MAX_DAC_CLK);
> > +
> > +	st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
> > +	if (IS_ERR(st->regmap))
> > +		return PTR_ERR(st->regmap);
> > +
> > +	ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (id != AD9739A_ID)
> > +		return dev_err_probe(dev, -ENODEV, "Unrecognized CHIP_ID 0x%X",
> > +				     id);
> Do we have to give up here?  Could it be a compatible future part?
> If so we should fallback on what firmware told us it was + perhaps a
> dev_info() to say we don't recognise the ID register value.
> 

I typically prefer to really give up in these cases but no strong opinion... Can turn
this into a dev_warn()...

- Nuno Sá


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

* Re: [PATCH 09/10] iio: dac: add support for AXI DAC IP core
  2024-03-28 15:35   ` Jonathan Cameron
@ 2024-03-28 16:43     ` Nuno Sá
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sá @ 2024-03-28 16:43 UTC (permalink / raw)
  To: Jonathan Cameron, Nuno Sa via B4 Relay
  Cc: nuno.sa, linux-iio, devicetree, Dragos Bogdan,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 2024-03-28 at 15:35 +0000, Jonathan Cameron wrote:
> On Thu, 28 Mar 2024 14:22:33 +0100
> Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> 
> > From: Nuno Sa <nuno.sa@analog.com>
> > 
> > Support the Analog Devices Generic AXI DAC IP core. The IP core is used
> > for interfacing with digital-to-analog (DAC) converters that require either
> > a high-speed serial interface (JESD204B/C) or a source synchronous parallel
> > interface (LVDS/CMOS). Typically (for such devices) SPI will be used for
> > configuration only, while this IP core handles the streaming of data into
> > memory via DMA.
> > 
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> 
> 
> A few minor things inline, but mostly seems fine to me.
> 
> Jonathan
> 
> 
> ...
> 
> > diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
> > new file mode 100644
> > index 000000000000..0022ecb4e4bb
> > --- /dev/null
> > +++ b/drivers/iio/dac/adi-axi-dac.c
> 
> 
> > +
> > +enum {
> > +	AXI_DAC_FREQ_TONE_1,
> > +	AXI_DAC_FREQ_TONE_2,
> > +	AXI_DAC_SCALE_TONE_1,
> > +	AXI_DAC_SCALE_TONE_2,
> > +	AXI_DAC_PHASE_TONE_1,
> > +	AXI_DAC_PHASE_TONE_2,
> > +};
> > +
> > +static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
> > +				   unsigned int tone, unsigned int *freq)
> > +{
> > +	u32 reg, raw;
> > +	int ret;
> > +
> > +	if (!st->dac_clk) {
> > +		dev_err(st->dev, "Sampling rate is 0...\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (tone == AXI_DAC_FREQ_TONE_1)
> 
> Given this is matching 2 out of enum with other values, it would be more
> locally readable as a switch statement with an error returning default.
> Then we wouldn't need to look at the caller.
> 
> Or at the caller convert from the enum to 0,1 for all these functions.
> 

Ok, will see what of the alternatives looks better.

> 
> 
> > +		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
> > +	else
> > +		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
> > +
> > +	ret = regmap_read(st->regmap, reg, &raw);
> > +	if (ret)
> > +		return ret;
> > +
> > +	raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
> > +	*freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
> > +
> > +	return 0;
> > +}
> 
> ...
> 
> > +static int axi_dac_scale_set(struct axi_dac_state *st,
> > +			     const struct iio_chan_spec *chan,
> > +			     const char *buf, size_t len, unsigned int tone)
> > +{
> > +	int integer, frac, scale;
> > +	u32 raw = 0, reg;
> > +	int ret;
> > +
> > +	ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
> > +	if (ret)
> > +		return ret;
> > +
> > +	scale = integer * MEGA + frac;
> > +	if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
> > +		return -EINVAL;
> > +
> > +	/*  format is 1.1.14 (sign, integer and fractional bits) */
> > +	if (scale < 0) {
> > +		raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
> > +		scale *= -1;
> > +	}
> > +
> > +	raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
> > +
> > +	if (tone == AXI_DAC_SCALE_TONE_1)
> > +		reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
> > +	else
> > +		reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
> > +
> > +	guard(mutex)(&st->lock);
> > +	ret = regmap_write(st->regmap, reg, raw);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* synchronize channels */
> > +	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return len;
> > +}
> > +
> > +static int axi_dac_phase_set(struct axi_dac_state *st,
> > +			     const struct iio_chan_spec *chan,
> > +			     const char *buf, size_t len, unsigned int tone)
> > +{
> > +	int integer, frac, phase;
> > +	u32 raw, reg;
> > +	int ret;
> > +
> > +	ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
> 
> > +	if (ret)
> > +		return ret;
> > +
> > +	phase = integer * MEGA + frac;
> > +	if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
> > +		return -EINVAL;
> > +
> > +	raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
> > +
> > +	if (tone == AXI_DAC_PHASE_TONE_1)
> Preference for a switch so it's clear there are only 2 choices.
> > +		reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
> > +	else
> > +		reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
> > +
> > +	guard(mutex)(&st->lock);
> > +	ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
> > +				 FIELD_PREP(AXI_DAC_PHASE, raw));
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* synchronize channels */
> > +	ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return len;
> > +}
> > +
> > +static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
> > +				const struct iio_chan_spec *chan,
> > +				const char *buf, size_t len)
> > +{
> > +	struct axi_dac_state *st = iio_backend_get_priv(back);
> > +
> > +	switch (private) {
> > +	case AXI_DAC_FREQ_TONE_1:
> > +	case AXI_DAC_FREQ_TONE_2:
> 
> Same as the get path - convert to which tone here so that the enum becomes
> a tone index for the functions called and the mapping to that single enum
> is kept clear of the lower level code.
> 
> > +		return axi_dac_frequency_set(st, chan, buf, len, private);
> > +	case AXI_DAC_SCALE_TONE_1:
> > +	case AXI_DAC_SCALE_TONE_2:
> > +		return axi_dac_scale_set(st, chan, buf, len, private);
> > +	case AXI_DAC_PHASE_TONE_1:
> > +	case AXI_DAC_PHASE_TONE_2:
> > +		return axi_dac_phase_set(st, chan, buf, len, private);
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +}
> > +
> > +static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
> > +				const struct iio_chan_spec *chan, char *buf)
> > +{
> > +	struct axi_dac_state *st = iio_backend_get_priv(back);
> > +
> > +	switch (private) {
> > +	case AXI_DAC_FREQ_TONE_1:
> > +	case AXI_DAC_FREQ_TONE_2:
> > +		return axi_dac_frequency_get(st, chan, buf, private);
> I'd break out private as an unsigned int here and then - AXI_DAC_FREQ_TONE_1
> so that it is just which tone for all the calls made from here.
> Similar for the following ones.
> 

ack..

> > +	case AXI_DAC_SCALE_TONE_1:
> > +	case AXI_DAC_SCALE_TONE_2:
> > +		return axi_dac_scale_get(st, chan, buf, private);
> > +	case AXI_DAC_PHASE_TONE_1:
> > +	case AXI_DAC_PHASE_TONE_2:
> > +		return axi_dac_phase_get(st, chan, buf, private);
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +}
> > +
> > +static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
> > +	IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
> > +	IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
> > +	IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
> > +	IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
> > +	IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
> > +	IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
> > +	{}
> > +};
> > +
> > +static int axi_dac_extend_chan(struct iio_backend *back,
> > +			       struct iio_chan_spec *chan)
> > +{
> > +	struct axi_dac_state *st = iio_backend_get_priv(back);
> > +
> > +	if (chan->type != IIO_ALTVOLTAGE)
> > +		return -EINVAL;
> > +	if (st->reg_config & AXI_DDS_DISABLE)
> > +		/* nothing to extend */
> > +		return 0;
> > +
> > +	chan->ext_info = axi_dac_ext_info;
> > +
> > +	return 0;
> > +}
> 
> > +static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
> > +				   u64 sample_rate)
> > +{
> > +	struct axi_dac_state *st = iio_backend_get_priv(back);
> > +	unsigned int freq;
> > +	int ret, tone;
> > +
> > +	if (!sample_rate)
> > +		return -EINVAL;
> > +	if (st->reg_config & AXI_DDS_DISABLE)
> > +		/* nothing to care if DDS is disabled */
> Rephrase this.  Is the point that the sample rate has no meaning without DDS or
> that it has meaning but nothing to do here?

Has no meaning as it's not used with DDS enabled...

- Nuno Sá
> 

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

* Re: [PATCH 10/10] iio: dac: support the ad9739a RF DAC
  2024-03-28 16:37     ` Nuno Sá
@ 2024-03-28 16:52       ` Jonathan Cameron
  0 siblings, 0 replies; 43+ messages in thread
From: Jonathan Cameron @ 2024-03-28 16:52 UTC (permalink / raw)
  To: Nuno Sá
  Cc: Nuno Sa via B4 Relay, nuno.sa, linux-iio, devicetree,
	Dragos Bogdan, Lars-Peter Clausen, Michael Hennerich,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan


> > > +static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
> > > +				 const struct iio_chan_spec *chan, u32 mode)
> > > +{
> > > +	struct ad9739a_state *st = iio_priv(indio_dev);
> > > +
> > > +	if (mode == AD9739A_MIXED_MODE - 1)
> > > +		mode = AD9739A_MIXED_MODE;  
> > 
> > Why?  Feels like a comment is needed. Or a more obvious conversion function.
> >   
> 
> To match what we want to write in the register... With just two values it's too
> simple that opt not to have any helper function or table. Would you be fine with a
> comment?

yes

> 
> > > +
> > > +	return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
> > > +				  AD9739A_DAC_DEC, mode);
> > > +}
> > > +
> > > +static int ad9739a_read_raw(struct iio_dev *indio_dev,
> > > +			    struct iio_chan_spec const *chan,
> > > +			    int *val, int *val2, long mask)
> > > +{
> > > +	struct ad9739a_state *st = iio_priv(indio_dev);
> > > +
> > > +	switch (mask) {
> > > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > > +		*val = st->sample_rate;
> > > +		*val2 = 0;
> > > +		return IIO_VAL_INT_64;  
> > 
> > Big numbers :)  
> 
> My setup is using 2.5Ghz which is big enough to overflow INT but would work on UINT.

I like big numbers so it's fine doing this. Just unusual to force
val2 to 0 so it made me look closer and appreciate just how big these were getting ;)
> > > +	if (id != AD9739A_ID)
> > > +		return dev_err_probe(dev, -ENODEV, "Unrecognized CHIP_ID 0x%X",
> > > +				     id);  
> > Do we have to give up here?  Could it be a compatible future part?
> > If so we should fallback on what firmware told us it was + perhaps a
> > dev_info() to say we don't recognise the ID register value.
> >   
> 
> I typically prefer to really give up in these cases but no strong opinion... Can turn
> this into a dev_warn()...

DT maintainers generally advise carrying on and trusting the firmware.
I used to agree with you that paranoia was good but I can see there point
and we do have cases where this happened in real parts.

Jonathan

> 
> - Nuno Sá
> 


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

* Re: [PATCH 08/10] iio: backend: add new functionality
  2024-03-28 15:59       ` Jonathan Cameron
@ 2024-03-28 16:54         ` Nuno Sá
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sá @ 2024-03-28 16:54 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Nuno Sa via B4 Relay, nuno.sa, linux-iio, devicetree,
	Dragos Bogdan, Lars-Peter Clausen, Michael Hennerich,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, 2024-03-28 at 15:59 +0000, Jonathan Cameron wrote:
> On Thu, 28 Mar 2024 16:42:38 +0100
> Nuno Sá <noname.nuno@gmail.com> wrote:
> 
> > On Thu, 2024-03-28 at 15:16 +0000, Jonathan Cameron wrote:
> > > On Thu, 28 Mar 2024 14:22:32 +0100
> > > Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:
> > >   
> > > > From: Nuno Sa <nuno.sa@analog.com>
> > > > 
> > > > This adds the needed backend ops for supporting a backend inerfacing
> > > > with an high speed dac. The new ops are:
> > > > 
> > > > * data_source_set();
> > > > * set_sampling_freq();
> > > > * extend_chan_spec();
> > > > * ext_info_set();
> > > > * ext_info_get().
> > > > 
> > > > Also to note the new helpers that are meant to be used by the backends
> > > > +		return 0;
> > > > +	/*
> > > > +	 * !\NOTE: this will break as soon as we have multiple backends on
> > > > one
> > > > +	 * frontend and all of them extend channels. In that case, the core
> > > > +	 * backend code has no way to get the correct backend given the
> > > > +	 * iio device.
> > > > +	 *
> > > > +	 * One solution for this could be introducing a new backend
> > > > +	 * dedicated callback in struct iio_info so we can callback into the
> > > > +	 * frontend so it can give us the right backend given a chan_spec.
> > > > +	 */  
> > > 
> > > Hmm. This is indeed messy.  Could we associate it with the buffer as presuably
> > > a front end with multiple backends is using multiple IIO buffers?
> > >   
> > 
> > Hmm, the assumption of having multiple buffers seems plausible to me but
> > considering
> > the example we have in hands it would be cumbersome to get the backend.
> > Considering
> > iio_backend_ext_info_get(), how could we get the backend if it was associated to
> > one
> > of the IIO buffers? I think we would need more "intrusive" changes to make that
> > work
> > or do you have something in mind=
> 
> Nope. Just trying to get my head around the associations. I hadn't thought about
> how to make that visible in the code.  Probably a callabck anyway.
> 
> >  
> > > As you say a dance via the front end would work fine.  
> > 
> > I'm happy you're also open for a proper solution already. I mention this in the
> > cover. My idea was something like (consider the iio_backend_ext_info_get()):
> > 
> > if (!indio_dev->info->get_iio_backend())
> > 	return -EOPNOTSUPP;
> > 
> > back = indio_dev->info->get_iio_backend(indio_dev, chan_spec);
> > 
> > It would be nice to have some "default/generic" implementation for cases where we
> > only have one backend per frontend so that the frontend would not need to define
> > the
> > callback.
> Agreed - either a default that means if the callback isn't provided we get the
> single backend or if that proves fiddly at least a standard callback we can
> use in all such cases.
> 

I'll have to think a bit about it. We may need some association/link between iio_dev
and iio_backend in order to "if the callback isn't provided we get the single
backend". The easiest that comes to my mind without much thinking would be to use
iio_device_set_drvdata()/iio_device_get_drvdata() in case the frontend does not
provide a callback. This would already force callers to assign the indio_dev->info
pointer before this call. Not that nice but acceptable if properly documented I
guess.

Anyways, I'll see if I can think of something better...

> >   
> > > 
> > >   
> > > > +	iio_device_set_drvdata(indio_dev, back);
> > > > +
> > > > +	/* Don't allow backends to get creative and force their own handlers
> > > > */
> > > > +	for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> > > > +		if (ext_info->read != iio_backend_ext_info_get)
> > > > +			return -EINVAL;
> > > > +		if (ext_info->write != iio_backend_ext_info_set)
> > > > +			return -EINVAL;
> > > > +	}
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);  
> > >   
> > > > diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
> > > > index a6d79381866e..09ff2f8f9fd8 100644
> > > > --- a/include/linux/iio/backend.h
> > > > +++ b/include/linux/iio/backend.h
> > > > @@ -4,6 +4,7 @@
> > > >  
> > > >  #include <linux/types.h>
> > > >  
> > > > +struct iio_chan_spec;
> > > >  struct fwnode_handle;
> > > >  struct iio_backend;
> > > >  struct device;
> > > > @@ -15,6 +16,26 @@ enum iio_backend_data_type {
> > > >  	IIO_BACKEND_DATA_TYPE_MAX
> > > >  };
> > > >  
> > > > +enum iio_backend_data_source {
> > > > +	IIO_BACKEND_INTERNAL_CW,  
> > > 
> > > CW?  Either expand out what ever that is in definition of add a comment
> > > at least.  
> > 
> > Continuous wave :)
> 
> Spell that out.
> 
> > 
> > >   
> > > > +	IIO_BACKEND_EXTERNAL,  
> > > What does external mean in this case?  
> > 
> > In this particular case comes from a DMA source (IP). I thought external to be
> > more
> > generic but if you prefer, I can do something like IIO_BACKEND_DMA?
> 
> So from another IP block?   For that to be reasonably 'generic' we'd need a way
> to known where it was coming from.
> 

Yeps, in this case comes from the IIO DMA buffer which in HW means a DMA IP core...

> Now I remember advantage of reviewing on weekends - fewer replies during the
> reviews :)
> 

:)

- Nuno Sá

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

* Re: [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver
  2024-03-28 13:22   ` Nuno Sa
  (?)
@ 2024-03-29 18:46   ` David Lechner
  2024-04-02  7:51     ` Nuno Sá
  -1 siblings, 1 reply; 43+ messages in thread
From: David Lechner @ 2024-03-29 18:46 UTC (permalink / raw)
  To: nuno.sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, Mar 28, 2024 at 8:22 AM Nuno Sa via B4 Relay
<devnull+nuno.sa.analog.com@kernel.org> wrote:
>
> From: Nuno Sa <nuno.sa@analog.com>
>
> This adds the bindings documentation for the AXI DAC driver.
>
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> ---
>  .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   | 62 ++++++++++++++++++++++
>  MAINTAINERS                                        |  7 +++
>  2 files changed, 69 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> new file mode 100644
> index 000000000000..1018fd274f04
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> @@ -0,0 +1,62 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices AXI DAC IP core
> +
> +maintainers:
> +  - Nuno Sa <nuno.sa@analog.com>
> +
> +description: |
> +  Analog Devices Generic AXI DAC IP core for interfacing a DAC device
> +  with a high speed serial (JESD204B/C) or source synchronous parallel
> +  interface (LVDS/CMOS).
> +  Usually, some other interface type (i.e SPI) is used as a control
> +  interface for the actual DAC, while this IP core will interface
> +  to the data-lines of the DAC and handle the streaming of data into
> +  memory via DMA.

Isn't it the other way around for DAC, from memory to hardware?

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

* Re: [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
  2024-03-28 13:22   ` Nuno Sa
  (?)
@ 2024-03-29 19:06   ` David Lechner
  2024-03-30 18:27     ` Krzysztof Kozlowski
  2024-04-02  7:50     ` Nuno Sá
  -1 siblings, 2 replies; 43+ messages in thread
From: David Lechner @ 2024-03-29 19:06 UTC (permalink / raw)
  To: nuno.sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Thu, Mar 28, 2024 at 8:23 AM Nuno Sa via B4 Relay
<devnull+nuno.sa.analog.com@kernel.org> wrote:
>
> From: Nuno Sa <nuno.sa@analog.com>
>
> This adds the bindings documentation for the 14 bit
> RF Digital-to-Analog converter.
>
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> ---
>  .../devicetree/bindings/iio/dac/adi,ad9739a.yaml   | 88 ++++++++++++++++++++++
>  MAINTAINERS                                        |  8 ++
>  2 files changed, 96 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> new file mode 100644
> index 000000000000..24bcec763a9b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> @@ -0,0 +1,88 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices AD9739A RF DAC
> +
> +maintainers:
> +  - Dragos Bogdan <dragos.bogdan@analog.com>
> +  - Nuno Sa <nuno.sa@analog.com>
> +
> +description: |
> +  The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
> +  of synthesizing wideband signals from dc up to 3 GHz.
> +
> +  https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,ad9739a
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1

I also see an IRQ pin on the datasheet. Do we need an interrupts
property for that?

> +
> +  reset-gpios:
> +    maxItems: 1
> +
> +  vdd_3_3-supply:
> +    description: 3.3V Digital input supply.

vdd33-supply would better match the datasheet name

> +
> +  vdd-supply:
> +    description: 1.8V Digital input supply.
> +
> +  vdda-supply:
> +    description: 3.3V Analog input supply.
> +
> +  vddc-supply:
> +    description: 1.8V Clock input supply.

I see a VREF voltage reference input in the datasheet. Do we need a vref-supply?

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

* Re: [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
  2024-03-29 19:06   ` David Lechner
@ 2024-03-30 18:27     ` Krzysztof Kozlowski
  2024-04-02  7:49       ` Nuno Sá
  2024-04-02  7:50     ` Nuno Sá
  1 sibling, 1 reply; 43+ messages in thread
From: Krzysztof Kozlowski @ 2024-03-30 18:27 UTC (permalink / raw)
  To: David Lechner, nuno.sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On 29/03/2024 20:06, David Lechner wrote:
> 
>> +
>> +  reset-gpios:
>> +    maxItems: 1
>> +
>> +  vdd_3_3-supply:
>> +    description: 3.3V Digital input supply.
> 
> vdd33-supply would better match the datasheet name

And would conform to coding style. No underscores in names.

Nuno,
To avoid comments like this one, please read and follow DTS coding style
when you work with DTS or Devicetree bindings.


Best regards,
Krzysztof


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

* Re: [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver
  2024-03-28 13:22   ` Nuno Sa
  (?)
  (?)
@ 2024-04-01 13:59   ` Rob Herring
  2024-04-04 10:03     ` Nuno Sá
  -1 siblings, 1 reply; 43+ messages in thread
From: Rob Herring @ 2024-04-01 13:59 UTC (permalink / raw)
  To: Nuno Sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan

On Thu, Mar 28, 2024 at 02:22:30PM +0100, Nuno Sa wrote:
> This adds the bindings documentation for the AXI DAC driver.

Bindings are for h/w blocks, not 'drivers'.

Reword the subject to only say 'bindings' once.


> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> ---
>  .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   | 62 ++++++++++++++++++++++
>  MAINTAINERS                                        |  7 +++
>  2 files changed, 69 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> new file mode 100644
> index 000000000000..1018fd274f04
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> @@ -0,0 +1,62 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices AXI DAC IP core
> +
> +maintainers:
> +  - Nuno Sa <nuno.sa@analog.com>
> +
> +description: |
> +  Analog Devices Generic AXI DAC IP core for interfacing a DAC device
> +  with a high speed serial (JESD204B/C) or source synchronous parallel
> +  interface (LVDS/CMOS).
> +  Usually, some other interface type (i.e SPI) is used as a control
> +  interface for the actual DAC, while this IP core will interface
> +  to the data-lines of the DAC and handle the streaming of data into
> +  memory via DMA.
> +
> +  https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,axi-dac-9.1.b
> +
> +  reg:
> +    maxItems: 1
> +
> +  dmas:
> +    maxItems: 1
> +
> +  dma-names:
> +    items:
> +      - const: tx

You don't need *-names if there is only 1 entry.

> +
> +  clocks:
> +    maxItems: 1
> +
> +  '#io-backend-cells':
> +    const: 0
> +
> +required:
> +  - compatible
> +  - dmas
> +  - reg
> +  - clocks
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    dac@44a00000 {
> +        compatible = "adi,axi-dac-9.1.b";
> +        reg = <0x44a00000 0x10000>;
> +        dmas = <&tx_dma 0>;
> +        dma-names = "tx";
> +        #io-backend-cells = <0>;
> +        clocks = <&axi_clk>;
> +    };
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a7287cf44869..2137eb452376 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1399,6 +1399,13 @@ F:	sound/soc/codecs/adav*
>  F:	sound/soc/codecs/sigmadsp.*
>  F:	sound/soc/codecs/ssm*
>  
> +ANALOG DEVICES INC AXI DAC DRIVER
> +M:	Nuno Sa <nuno.sa@analog.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Supported
> +W:	https://ez.analog.com/linux-software-drivers
> +F:	Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> +
>  ANALOG DEVICES INC DMA DRIVERS
>  M:	Lars-Peter Clausen <lars@metafoo.de>
>  S:	Supported
> 
> -- 
> 2.44.0
> 

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

* Re: [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
  2024-03-28 13:22   ` Nuno Sa
  (?)
  (?)
@ 2024-04-01 14:02   ` Rob Herring
  -1 siblings, 0 replies; 43+ messages in thread
From: Rob Herring @ 2024-04-01 14:02 UTC (permalink / raw)
  To: Nuno Sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan

On Thu, Mar 28, 2024 at 02:22:31PM +0100, Nuno Sa wrote:
> This adds the bindings documentation for the 14 bit
> RF Digital-to-Analog converter.

Reword the subject to say 'bindings' only once.

> 
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> ---
>  .../devicetree/bindings/iio/dac/adi,ad9739a.yaml   | 88 ++++++++++++++++++++++
>  MAINTAINERS                                        |  8 ++
>  2 files changed, 96 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> new file mode 100644
> index 000000000000..24bcec763a9b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> @@ -0,0 +1,88 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices AD9739A RF DAC
> +
> +maintainers:
> +  - Dragos Bogdan <dragos.bogdan@analog.com>
> +  - Nuno Sa <nuno.sa@analog.com>
> +
> +description: |
> +  The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
> +  of synthesizing wideband signals from dc up to 3 GHz.
> +
> +  https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,ad9739a
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +  reset-gpios:
> +    maxItems: 1
> +
> +  vdd_3_3-supply:

vdd-3-3-supply or vdd-3p3-supply

> +    description: 3.3V Digital input supply.
> +
> +  vdd-supply:
> +    description: 1.8V Digital input supply.
> +
> +  vdda-supply:
> +    description: 3.3V Analog input supply.
> +
> +  vddc-supply:
> +    description: 1.8V Clock input supply.
> +
> +  io-backends:
> +    maxItems: 1
> +
> +  adi,full-scale-microamp:
> +    description: This property represents the DAC full scale current.
> +    minimum: 8700
> +    maximum: 31700
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - io-backends
> +  - vdd_3_3-supply
> +  - vdd-supply
> +  - vdda-supply
> +  - vddc-supply
> +
> +allOf:
> +  - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    spi {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        dac@0 {
> +            compatible = "adi,ad9739a";
> +            reg = <0>;
> +
> +            clocks = <&dac_clk>;
> +
> +            io-backends = <&iio_backend>;
> +
> +            vdd_3_3-supply = <&vdd_3_3>;
> +            vdd-supply = <&vdd>;
> +            vdda-supply = <&vdd_3_3>;
> +            vddc-supply = <&vdd>;
> +        };
> +    };
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2137eb452376..76e872e320d7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1234,6 +1234,14 @@ W:	https://ez.analog.com/linux-software-drivers
>  F:	Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
>  F:	drivers/iio/adc/ad7780.c
>  
> +ANALOG DEVICES INC AD9739a DRIVER
> +M:	Nuno Sa <nuno.sa@analog.com>
> +M:	Dragos Bogdan <dragos.bogdan@analog.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Supported
> +W:	https://ez.analog.com/linux-software-drivers
> +F:	Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> +
>  ANALOG DEVICES INC ADA4250 DRIVER
>  M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
>  L:	linux-iio@vger.kernel.org
> 
> -- 
> 2.44.0
> 

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

* Re: [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
  2024-03-30 18:27     ` Krzysztof Kozlowski
@ 2024-04-02  7:49       ` Nuno Sá
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sá @ 2024-04-02  7:49 UTC (permalink / raw)
  To: Krzysztof Kozlowski, David Lechner, nuno.sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Sat, 2024-03-30 at 19:27 +0100, Krzysztof Kozlowski wrote:
> On 29/03/2024 20:06, David Lechner wrote:
> > 
> > > +
> > > +  reset-gpios:
> > > +    maxItems: 1
> > > +
> > > +  vdd_3_3-supply:
> > > +    description: 3.3V Digital input supply.
> > 
> > vdd33-supply would better match the datasheet name
> 
> And would conform to coding style. No underscores in names.
> 
> Nuno,
> To avoid comments like this one, please read and follow DTS coding style
> when you work with DTS or Devicetree bindings.
> 

Noted...

Thanks!
- Nuno Sá

> 


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

* Re: [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
  2024-03-29 19:06   ` David Lechner
  2024-03-30 18:27     ` Krzysztof Kozlowski
@ 2024-04-02  7:50     ` Nuno Sá
  1 sibling, 0 replies; 43+ messages in thread
From: Nuno Sá @ 2024-04-02  7:50 UTC (permalink / raw)
  To: David Lechner, nuno.sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Fri, 2024-03-29 at 14:06 -0500, David Lechner wrote:
> On Thu, Mar 28, 2024 at 8:23 AM Nuno Sa via B4 Relay
> <devnull+nuno.sa.analog.com@kernel.org> wrote:
> > 
> > From: Nuno Sa <nuno.sa@analog.com>
> > 
> > This adds the bindings documentation for the 14 bit
> > RF Digital-to-Analog converter.
> > 
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> > ---
> >  .../devicetree/bindings/iio/dac/adi,ad9739a.yaml   | 88 ++++++++++++++++++++++
> >  MAINTAINERS                                        |  8 ++
> >  2 files changed, 96 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> > b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> > new file mode 100644
> > index 000000000000..24bcec763a9b
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> > @@ -0,0 +1,88 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Analog Devices AD9739A RF DAC
> > +
> > +maintainers:
> > +  - Dragos Bogdan <dragos.bogdan@analog.com>
> > +  - Nuno Sa <nuno.sa@analog.com>
> > +
> > +description: |
> > +  The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
> > +  of synthesizing wideband signals from dc up to 3 GHz.
> > +
> > + 
> > https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
> > +
> > +properties:
> > +  compatible:
> > +    enum:
> > +      - adi,ad9739a
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    maxItems: 1
> 
> I also see an IRQ pin on the datasheet. Do we need an interrupts
> property for that?
> 

Likely we'll never use it. But yes, it can be documented in the bindings...

> > +
> > +  reset-gpios:
> > +    maxItems: 1
> > +
> > +  vdd_3_3-supply:
> > +    description: 3.3V Digital input supply.
> 
> vdd33-supply would better match the datasheet name
> 
> > +
> > +  vdd-supply:
> > +    description: 1.8V Digital input supply.
> > +
> > +  vdda-supply:
> > +    description: 3.3V Analog input supply.
> > +
> > +  vddc-supply:
> > +    description: 1.8V Clock input supply.
> 
> I see a VREF voltage reference input in the datasheet. Do we need a vref-supply?

Same as the IRQ pin.

- Nuno Sá


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

* Re: [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver
  2024-03-29 18:46   ` David Lechner
@ 2024-04-02  7:51     ` Nuno Sá
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sá @ 2024-04-02  7:51 UTC (permalink / raw)
  To: David Lechner, nuno.sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Olivier Moysan

On Fri, 2024-03-29 at 13:46 -0500, David Lechner wrote:
> On Thu, Mar 28, 2024 at 8:22 AM Nuno Sa via B4 Relay
> <devnull+nuno.sa.analog.com@kernel.org> wrote:
> > 
> > From: Nuno Sa <nuno.sa@analog.com>
> > 
> > This adds the bindings documentation for the AXI DAC driver.
> > 
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> > ---
> >  .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   | 62 ++++++++++++++++++++++
> >  MAINTAINERS                                        |  7 +++
> >  2 files changed, 69 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > new file mode 100644
> > index 000000000000..1018fd274f04
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > @@ -0,0 +1,62 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Analog Devices AXI DAC IP core
> > +
> > +maintainers:
> > +  - Nuno Sa <nuno.sa@analog.com>
> > +
> > +description: |
> > +  Analog Devices Generic AXI DAC IP core for interfacing a DAC device
> > +  with a high speed serial (JESD204B/C) or source synchronous parallel
> > +  interface (LVDS/CMOS).
> > +  Usually, some other interface type (i.e SPI) is used as a control
> > +  interface for the actual DAC, while this IP core will interface
> > +  to the data-lines of the DAC and handle the streaming of data into
> > +  memory via DMA.
> 
> Isn't it the other way around for DAC, from memory to hardware?

Good catch! Yes, copy paste from the axi-adc and obviously missed this :)

- Nuno Sá

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

* Re: [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver
  2024-04-01 13:59   ` Rob Herring
@ 2024-04-04 10:03     ` Nuno Sá
  0 siblings, 0 replies; 43+ messages in thread
From: Nuno Sá @ 2024-04-04 10:03 UTC (permalink / raw)
  To: Rob Herring, Nuno Sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan

On Mon, 2024-04-01 at 08:59 -0500, Rob Herring wrote:
> On Thu, Mar 28, 2024 at 02:22:30PM +0100, Nuno Sa wrote:
> > This adds the bindings documentation for the AXI DAC driver.
> 
> Bindings are for h/w blocks, not 'drivers'.
> 
> Reword the subject to only say 'bindings' once.
> 
> 
> > Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> > ---
> >  .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   | 62
> > ++++++++++++++++++++++
> >  MAINTAINERS                                        |  7 +++
> >  2 files changed, 69 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > new file mode 100644
> > index 000000000000..1018fd274f04
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> > @@ -0,0 +1,62 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Analog Devices AXI DAC IP core
> > +
> > +maintainers:
> > +  - Nuno Sa <nuno.sa@analog.com>
> > +
> > +description: |
> > +  Analog Devices Generic AXI DAC IP core for interfacing a DAC device
> > +  with a high speed serial (JESD204B/C) or source synchronous parallel
> > +  interface (LVDS/CMOS).
> > +  Usually, some other interface type (i.e SPI) is used as a control
> > +  interface for the actual DAC, while this IP core will interface
> > +  to the data-lines of the DAC and handle the streaming of data into
> > +  memory via DMA.
> > +
> > +  https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
> > +
> > +properties:
> > +  compatible:
> > +    enum:
> > +      - adi,axi-dac-9.1.b
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  dmas:
> > +    maxItems: 1
> > +
> > +  dma-names:
> > +    items:
> > +      - const: tx
> 
> You don't need *-names if there is only 1 entry.

Looking at the dmaengine code [1], it actually looks like I should have this
property required, no?

[1]: https://elixir.bootlin.com/linux/v6.9-rc2/source/drivers/dma/of-dma.c#L270

- Nuno Sá



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

end of thread, other threads:[~2024-04-04  9:59 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-28 13:22 [PATCH 00/10] iio: dac: support IIO backends on the output direction Nuno Sa via B4 Relay
2024-03-28 13:22 ` Nuno Sa
2024-03-28 13:22 ` [PATCH 01/10] iio: buffer: add helper for setting direction Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-28 14:36   ` Jonathan Cameron
2024-03-28 15:18     ` Nuno Sá
2024-03-28 15:54       ` Jonathan Cameron
2024-03-28 13:22 ` [PATCH 02/10] iio: buffer-dma: Rename iio_dma_buffer_data_available() Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-28 13:22 ` [PATCH 03/10] iio: buffer-dma: Enable buffer write support Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-28 13:22 ` [PATCH 04/10] iio: buffer-dmaengine: Support specifying buffer direction Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-28 13:22 ` [PATCH 05/10] iio: buffer-dmaengine: Enable write support Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-28 13:22 ` [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-29 18:46   ` David Lechner
2024-04-02  7:51     ` Nuno Sá
2024-04-01 13:59   ` Rob Herring
2024-04-04 10:03     ` Nuno Sá
2024-03-28 13:22 ` [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-29 19:06   ` David Lechner
2024-03-30 18:27     ` Krzysztof Kozlowski
2024-04-02  7:49       ` Nuno Sá
2024-04-02  7:50     ` Nuno Sá
2024-04-01 14:02   ` Rob Herring
2024-03-28 13:22 ` [PATCH 08/10] iio: backend: add new functionality Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-28 15:16   ` Jonathan Cameron
2024-03-28 15:42     ` Nuno Sá
2024-03-28 15:59       ` Jonathan Cameron
2024-03-28 16:54         ` Nuno Sá
2024-03-28 13:22 ` [PATCH 09/10] iio: dac: add support for AXI DAC IP core Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-28 15:35   ` Jonathan Cameron
2024-03-28 16:43     ` Nuno Sá
2024-03-28 13:22 ` [PATCH 10/10] iio: dac: support the ad9739a RF DAC Nuno Sa via B4 Relay
2024-03-28 13:22   ` Nuno Sa
2024-03-28 15:51   ` Jonathan Cameron
2024-03-28 16:37     ` Nuno Sá
2024-03-28 16:52       ` Jonathan Cameron

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