On Tue, 02 Jun 2026 17:33:57 +0100 Rodrigo Alencar via B4 Relay <[email protected]> wrote:
> From: Rodrigo Alencar <[email protected]> > > Implement trigger handler by leveraging the LDAC gpio to update all DAC > channels at once when it is available. Also, the multiple channel writes > can be flushed at once with the sync() operation. > > Signed-off-by: Rodrigo Alencar <[email protected]> One passing comment inline. + some musings... I'd love to avoid the need for lots of drivers to call iio_trigger_notify_done() manually as it always results in ugly code paths. > --- > drivers/iio/dac/Kconfig | 2 ++ > drivers/iio/dac/ad5686.c | 59 > ++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 61 insertions(+) > > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig > index 657c68e75542..5f14fcd780e2 100644 > --- a/drivers/iio/dac/Kconfig > +++ b/drivers/iio/dac/Kconfig > @@ -240,6 +240,8 @@ config LTC2688 > > config AD5686 > tristate > + select IIO_BUFFER > + select IIO_TRIGGERED_BUFFER > > config AD5686_SPI > tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)" > diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c > index a4cc0f86ea54..5052df44ab1c 100644 > --- a/drivers/iio/dac/ad5686.c > +++ b/drivers/iio/dac/ad5686.c > @@ -19,7 +19,11 @@ > #include <linux/sysfs.h> > #include <linux/wordpart.h> > > +#include <linux/iio/buffer.h> > #include <linux/iio/iio.h> > +#include <linux/iio/trigger.h> > +#include <linux/iio/trigger_consumer.h> > +#include <linux/iio/triggered_buffer.h> > > #include "ad5686.h" > > @@ -245,6 +249,7 @@ static const struct iio_chan_spec_ext_info > ad5686_ext_info[] = { > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ > .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ > .address = addr, \ > + .scan_index = chan, \ > .scan_type = { \ > .sign = 'u', \ > .realbits = (bits), \ > @@ -469,6 +474,53 @@ const struct ad5686_chip_info ad5679r_chip_info = { > }; > EXPORT_SYMBOL_NS_GPL(ad5679r_chip_info, "IIO_AD5686"); > > +static irqreturn_t ad5686_trigger_handler(int irq, void *p) > +{ > + struct iio_poll_func *pf = p; > + struct iio_dev *indio_dev = pf->indio_dev; > + struct iio_buffer *buffer = indio_dev->buffer; > + struct ad5686_state *st = iio_priv(indio_dev); > + u16 val[AD5686_MAX_CHANNELS] = { }; > + int ret, ch, i = 0; > + bool async_update; > + u8 cmd; > + > + ret = iio_pop_from_buffer(buffer, val); > + if (ret) > + goto out; > + > + mutex_lock(&st->lock); > + > + async_update = st->ldac_gpio && > bitmap_weight(indio_dev->active_scan_mask, > + > iio_get_masklength(indio_dev)) > 1; > + if (async_update) { > + /* use ldac to update all channels simultaneously */ > + cmd = AD5686_CMD_WRITE_INPUT_N; > + gpiod_set_value_cansleep(st->ldac_gpio, 0); > + } else { > + cmd = AD5686_CMD_WRITE_INPUT_N_UPDATE_N; > + } > + > + iio_for_each_active_channel(indio_dev, ch) { > + ret = st->ops->write(st, cmd, indio_dev->channels[ch].address, > val[i++]); > + if (ret) > + goto cleanup; > + } > + > + if (st->ops->sync) > + ret = st->ops->sync(st); /* flush all pending transfers */ > + > +cleanup: > + if (async_update) Error paths are always fun. Do we care about setting ldac_gpio to 1 if we failed to write the channel values? That will set any that did successfully update, but not all of them. Note I'm not sure on the right answer for this. There may not be one! > + gpiod_set_value_cansleep(st->ldac_gpio, 1); > + > + mutex_unlock(&st->lock); > +out: > + iio_trigger_notify_done(indio_dev->trig); We get this pattern so often (though not always). Feels like maybe we should put some effort into a generic opt in solution for this. A job for another day but options that come to mind. 1) (hideous) a flag 2) Maybe an alternative callback. thread_always_complete or something like that. Pain to wire through all the calls though and injecting the necessary wrapper isn't great either. Implementation wise would be a case of popping in a wrapper function in iio_trigger_attach_poll() call to request_threaded_irq(). 3) Maybe a helper macro? Bit ugly as we'd need one to generate the wrapper function and another to use the same name for the registration function. Hmm. Those are all ugly (maybe 2 is ok ish). Suggestions welcome! > + > + return IRQ_HANDLED; > +} > + > int ad5686_probe(struct device *dev, > const struct ad5686_chip_info *chip_info, > const char *name, const struct ad5686_bus_ops *ops, > @@ -569,6 +621,13 @@ int ad5686_probe(struct device *dev, > return -EINVAL; > } > > + ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, > + &ad5686_trigger_handler, > + IIO_BUFFER_DIRECTION_OUT, > + NULL, NULL); > + if (ret) > + return ret; > + > return devm_iio_device_register(dev, indio_dev); > } > EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686"); >

