iio: dac: ad5791: Add offload support
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>
This commit is contained in:
committed by
Jonathan Cameron
parent
c91c294c72
commit
192b669b93
@@ -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
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
#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>
|
||||
@@ -64,11 +67,13 @@
|
||||
* 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");
|
||||
|
||||
Reference in New Issue
Block a user