Re: [PATCH v6 17/17] iio: dac: ad5791: Add offload support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 2024-12-11 at 14:54 -0600, David Lechner wrote:
> From: Axel Haslam <ahaslam@xxxxxxxxxxxx>
> 
> 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@xxxxxxxxxxxx>
> Signed-off-by: David Lechner <dlechner@xxxxxxxxxxxx>
> ---

Reviewed-by: Nuno Sa <nuno.sa@xxxxxxxxxx>

> 
> v6 changes: new patch in v6
> ---
>  drivers/iio/dac/Kconfig  |   3 +
>  drivers/iio/dac/ad5791.c | 150
> +++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 153 insertions(+)
> 
> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> index
> 4cde34e8c8e3356aa41bcd2cba38d67d5c6f8049..f6c5cb632acbdc2432f60b163452bb0c5f89
> fa72 100644
> --- a/drivers/iio/dac/Kconfig
> +++ b/drivers/iio/dac/Kconfig
> @@ -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
> diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c
> index
> 24462cb020e19e8e2c6faa13109ac047cf423c37..a2953a9a4e5d5bc17c9c4a8281be4b41b1af
> 5de8 100644
> --- a/drivers/iio/dac/ad5791.c
> +++ b/drivers/iio/dac/ad5791.c
> @@ -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,16 +377,95 @@ 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 < 0 || val2 < 0)
> +			return -EINVAL;
> +		return ad5791_set_sample_freq(st, val);
>  	default:
>  		return -EINVAL;
>  	}
>  }
>  
> +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,
>  };
>  
> +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)
>  {
>  	const struct ad5791_platform_data *pdata = dev_get_platdata(&spi-
> >dev);
> @@ -416,6 +550,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 +601,4 @@ module_spi_driver(ad5791_driver);
>  MODULE_AUTHOR("Michael Hennerich <michael.hennerich@xxxxxxxxxx>");
>  MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC");
>  MODULE_LICENSE("GPL v2");
> +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
> 






[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux