]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iio: dac: ad5791: Add offload support
authorAxel Haslam <ahaslam@baylibre.com>
Fri, 7 Feb 2025 20:09:14 +0000 (14:09 -0600)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Tue, 11 Feb 2025 19:32:31 +0000 (19:32 +0000)
Add SPI offload support to stream TX buffers using DMA.
This allows loading samples to the DAC with a rate of 1 MSPS.

Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Nuno Sa <nuno.sa@analog.com>
Signed-off-by: David Lechner <dlechner@baylibre.com>
Link: https://patch.msgid.link/20250207-dlech-mainline-spi-engine-offload-2-v8-17-e48a489be48c@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/dac/Kconfig
drivers/iio/dac/ad5791.c

index 5690a37267d86ea3ec805adfa32e13c052864061..4811ea973125a0dea1f8a9cdee1e0c045bc21981 100644 (file)
@@ -296,6 +296,9 @@ config AD5770R
 config AD5791
        tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver"
        depends on SPI
+       select SPI_OFFLOAD
+       select IIO_BUFFER
+       select IIO_BUFFER_DMAENGINE
        help
          Say yes here to build support for Analog Devices AD5760, AD5780,
          AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to
index 4f28f49071c244bfcd21c7553aa3e39e9751490a..07848be3f8d5654418ebbac7b33c14fd1014a2c8 100644 (file)
 #include <linux/module.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/spi/offload/consumer.h>
 #include <linux/spi/spi.h>
 #include <linux/sysfs.h>
+#include <linux/units.h>
 
+#include <linux/iio/buffer-dmaengine.h>
 #include <linux/iio/dac/ad5791.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
  * struct ad5791_chip_info - chip specific information
  * @name:              name of the dac chip
  * @channel:           channel specification
+ * @channel_offload:   channel specification for offload
  * @get_lin_comp:      function pointer to the device specific function
  */
 struct ad5791_chip_info {
        const char *name;
        const struct iio_chan_spec channel;
+       const struct iio_chan_spec channel_offload;
        int (*get_lin_comp)(unsigned int span);
 };
 
@@ -81,6 +86,11 @@ struct ad5791_chip_info {
  * @gpio_clear:                clear gpio
  * @gpio_ldac:         load dac gpio
  * @chip_info:         chip model specific constants
+ * @offload_msg:       spi message used for offload
+ * @offload_xfer:      spi transfer used for offload
+ * @offload:           offload device
+ * @offload_trigger:   offload trigger
+ * @offload_trigger_hz:        offload sample rate
  * @vref_mv:           actual reference voltage used
  * @vref_neg_mv:       voltage of the negative supply
  * @ctrl:              control register cache
@@ -96,6 +106,11 @@ struct ad5791_state {
        struct gpio_desc                *gpio_clear;
        struct gpio_desc                *gpio_ldac;
        const struct ad5791_chip_info   *chip_info;
+       struct spi_message              offload_msg;
+       struct spi_transfer             offload_xfer;
+       struct spi_offload              *offload;
+       struct spi_offload_trigger      *offload_trigger;
+       unsigned int                    offload_trigger_hz;
        unsigned short                  vref_mv;
        unsigned int                    vref_neg_mv;
        unsigned                        ctrl;
@@ -232,6 +247,25 @@ static int ad5780_get_lin_comp(unsigned int span)
                return AD5780_LINCOMP_10_20;
 }
 
+static int ad5791_set_sample_freq(struct ad5791_state *st, int val)
+{
+       struct spi_offload_trigger_config config = {
+               .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
+               .periodic = {
+                       .frequency_hz = val,
+               },
+       };
+       int ret;
+
+       ret = spi_offload_trigger_validate(st->offload_trigger, &config);
+       if (ret)
+               return ret;
+
+       st->offload_trigger_hz = config.periodic.frequency_hz;
+
+       return 0;
+}
+
 static int ad5791_read_raw(struct iio_dev *indio_dev,
                           struct iio_chan_spec const *chan,
                           int *val,
@@ -259,6 +293,9 @@ static int ad5791_read_raw(struct iio_dev *indio_dev,
                do_div(val64, st->vref_mv);
                *val = -val64;
                return IIO_VAL_INT;
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               *val = st->offload_trigger_hz;
+               return IIO_VAL_INT;
        default:
                return -EINVAL;
        }
@@ -299,6 +336,24 @@ static const struct ad5791_chip_info _name##_chip_info = {         \
                        },                                              \
                        .ext_info = ad5791_ext_info,                    \
        },                                                              \
+       .channel_offload = {                                            \
+                       .type = IIO_VOLTAGE,                            \
+                       .output = 1,                                    \
+                       .indexed = 1,                                   \
+                       .address = AD5791_ADDR_DAC0,                    \
+                       .channel = 0,                                   \
+                       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
+                       .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |  \
+                               BIT(IIO_CHAN_INFO_OFFSET),              \
+                       .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+                       .scan_type = {                                  \
+                               .sign = 'u',                            \
+                               .realbits = (bits),                     \
+                               .storagebits = 32,                      \
+                               .shift = (_shift),                      \
+                       },                                              \
+                       .ext_info = ad5791_ext_info,                    \
+       },                                                              \
 }
 
 AD5791_DEFINE_CHIP_INFO(ad5760, 16, 4, ad5780_get_lin_comp);
@@ -322,14 +377,106 @@ static int ad5791_write_raw(struct iio_dev *indio_dev,
 
                return ad5791_spi_write(st, chan->address, val);
 
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               if (val < 1)
+                       return -EINVAL;
+               return ad5791_set_sample_freq(st, val);
        default:
                return -EINVAL;
        }
 }
 
+static int ad5791_write_raw_get_fmt(struct iio_dev *indio_dev,
+                                   struct iio_chan_spec const *chan,
+                                   long mask)
+{
+       switch (mask) {
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               return IIO_VAL_INT;
+       default:
+               return IIO_VAL_INT_PLUS_MICRO;
+       }
+}
+
+static int ad5791_buffer_preenable(struct iio_dev *indio_dev)
+{
+       struct ad5791_state *st = iio_priv(indio_dev);
+       struct spi_offload_trigger_config config = {
+               .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
+               .periodic = {
+                       .frequency_hz = st->offload_trigger_hz,
+               },
+       };
+
+       if (st->pwr_down)
+               return -EINVAL;
+
+       return spi_offload_trigger_enable(st->offload, st->offload_trigger,
+                                        &config);
+}
+
+static int ad5791_buffer_postdisable(struct iio_dev *indio_dev)
+{
+       struct ad5791_state *st = iio_priv(indio_dev);
+
+       spi_offload_trigger_disable(st->offload, st->offload_trigger);
+
+       return 0;
+}
+
+static const struct iio_buffer_setup_ops ad5791_buffer_setup_ops = {
+       .preenable = &ad5791_buffer_preenable,
+       .postdisable = &ad5791_buffer_postdisable,
+};
+
+static int ad5791_offload_setup(struct iio_dev *indio_dev)
+{
+       struct ad5791_state *st = iio_priv(indio_dev);
+       struct spi_device *spi = st->spi;
+       struct dma_chan *tx_dma;
+       int ret;
+
+       st->offload_trigger = devm_spi_offload_trigger_get(&spi->dev,
+               st->offload, SPI_OFFLOAD_TRIGGER_PERIODIC);
+       if (IS_ERR(st->offload_trigger))
+               return dev_err_probe(&spi->dev, PTR_ERR(st->offload_trigger),
+                                    "failed to get offload trigger\n");
+
+       ret = ad5791_set_sample_freq(st, 1 * MEGA);
+       if (ret)
+               return dev_err_probe(&spi->dev, ret,
+                                    "failed to init sample rate\n");
+
+       tx_dma = devm_spi_offload_tx_stream_request_dma_chan(&spi->dev,
+                                                            st->offload);
+       if (IS_ERR(tx_dma))
+               return dev_err_probe(&spi->dev, PTR_ERR(tx_dma),
+                                    "failed to get offload TX DMA\n");
+
+       ret = devm_iio_dmaengine_buffer_setup_with_handle(&spi->dev,
+               indio_dev, tx_dma, IIO_BUFFER_DIRECTION_OUT);
+       if (ret)
+               return ret;
+
+       st->offload_xfer.len = 4;
+       st->offload_xfer.bits_per_word = 24;
+       st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_TX_STREAM;
+
+       spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1);
+       st->offload_msg.offload = st->offload;
+
+       return devm_spi_optimize_message(&spi->dev, st->spi, &st->offload_msg);
+}
+
 static const struct iio_info ad5791_info = {
        .read_raw = &ad5791_read_raw,
        .write_raw = &ad5791_write_raw,
+       .write_raw_get_fmt = &ad5791_write_raw_get_fmt,
+};
+
+static const struct spi_offload_config ad5791_offload_config = {
+       .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+                           SPI_OFFLOAD_CAP_TX_STREAM_DMA,
 };
 
 static int ad5791_probe(struct spi_device *spi)
@@ -416,6 +563,21 @@ static int ad5791_probe(struct spi_device *spi)
        indio_dev->channels = &st->chip_info->channel;
        indio_dev->num_channels = 1;
        indio_dev->name = st->chip_info->name;
+
+       st->offload = devm_spi_offload_get(&spi->dev, spi, &ad5791_offload_config);
+       ret = PTR_ERR_OR_ZERO(st->offload);
+       if (ret && ret != -ENODEV)
+               return dev_err_probe(&spi->dev, ret, "failed to get offload\n");
+
+       if (ret != -ENODEV) {
+               indio_dev->channels = &st->chip_info->channel_offload;
+               indio_dev->setup_ops = &ad5791_buffer_setup_ops;
+               ret = ad5791_offload_setup(indio_dev);
+               if (ret)
+                       return dev_err_probe(&spi->dev, ret,
+                                            "fail to setup offload\n");
+       }
+
        return devm_iio_device_register(&spi->dev, indio_dev);
 }
 
@@ -452,3 +614,4 @@ module_spi_driver(ad5791_driver);
 MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
 MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC");
 MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");