[PATCH 06/13] iio: buffer: add hardware triggered buffer support

From: David Lechner
Date: Wed Jan 10 2024 - 14:52:30 EST


This adds a new mode INDIO_HW_BUFFER_TRIGGERED to the IIO subsystem.

This mode is essentially the hardware version of INDIO_BUFFER_TRIGGERED
where the trigger has the semantics of INDIO_HARDWARE_TRIGGERED and the
buffer has the semantics of INDIO_BUFFER_HARDWARE.

So basically INDIO_HW_BUFFER_TRIGGERED is the same as
INDIO_BUFFER_HARDWARE except that it also enables the trigger when the
buffer is enabled.

Signed-off-by: David Lechner <dlechner@xxxxxxxxxxxx>
---
drivers/iio/industrialio-buffer.c | 43 ++++++++++++++++++++++++++++++++++++---
include/linux/iio/iio.h | 16 ++++++++++++---
2 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 176d31d9f9d8..ffee3043c65a 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -27,6 +27,7 @@
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/buffer_impl.h>
+#include <linux/iio/trigger.h>

static const char * const iio_endian_prefix[] = {
[IIO_BE] = "be",
@@ -867,8 +868,17 @@ static int iio_verify_update(struct iio_dev *indio_dev,
insert_buffer->watermark);
}

- /* Definitely possible for devices to support both of these. */
- if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
+ /* Definitely possible for devices to support all of these. */
+ if (modes & INDIO_HW_BUFFER_TRIGGERED) {
+ /*
+ * Keep things simple for now and only allow a single buffer to
+ * be connected in hardware mode.
+ */
+ if (insert_buffer && !list_empty(&iio_dev_opaque->buffer_list))
+ return -EINVAL;
+ config->mode = INDIO_HW_BUFFER_TRIGGERED;
+ strict_scanmask = true;
+ } else if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
config->mode = INDIO_BUFFER_TRIGGERED;
} else if (modes & INDIO_BUFFER_HARDWARE) {
/*
@@ -1107,11 +1117,21 @@ static int iio_enable_buffers(struct iio_dev *indio_dev,
}
}

+ if (iio_dev_opaque->currentmode == INDIO_HW_BUFFER_TRIGGERED) {
+ struct iio_trigger *trig = indio_dev->trig;
+
+ if (trig->ops && trig->ops->set_trigger_state) {
+ ret = trig->ops->set_trigger_state(trig, true);
+ if (ret)
+ goto err_disable_buffers;
+ }
+ }
+
if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED) {
ret = iio_trigger_attach_poll_func(indio_dev->trig,
indio_dev->pollfunc);
if (ret)
- goto err_disable_buffers;
+ goto err_disable_hw_trigger;
}

if (indio_dev->setup_ops->postenable) {
@@ -1130,6 +1150,16 @@ static int iio_enable_buffers(struct iio_dev *indio_dev,
iio_trigger_detach_poll_func(indio_dev->trig,
indio_dev->pollfunc);
}
+err_disable_hw_trigger:
+ if (iio_dev_opaque->currentmode == INDIO_HW_BUFFER_TRIGGERED) {
+ struct iio_trigger *trig = indio_dev->trig;
+
+ if (trig->ops && trig->ops->set_trigger_state) {
+ ret = trig->ops->set_trigger_state(trig, false);
+ if (ret)
+ return ret;
+ }
+ }
err_disable_buffers:
buffer = list_prepare_entry(tmp, &iio_dev_opaque->buffer_list, buffer_list);
list_for_each_entry_continue_reverse(buffer, &iio_dev_opaque->buffer_list,
@@ -1174,6 +1204,13 @@ static int iio_disable_buffers(struct iio_dev *indio_dev)
indio_dev->pollfunc);
}

+ if (iio_dev_opaque->currentmode == INDIO_HW_BUFFER_TRIGGERED) {
+ struct iio_trigger *trig = indio_dev->trig;
+
+ if (trig->ops && trig->ops->set_trigger_state)
+ trig->ops->set_trigger_state(trig, false);
+ }
+
list_for_each_entry(buffer, &iio_dev_opaque->buffer_list, buffer_list) {
ret2 = iio_buffer_disable(buffer, indio_dev);
if (ret2 && !ret)
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index d0ce3b71106a..16f62bd38041 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -366,6 +366,11 @@ s64 iio_get_time_ns(const struct iio_dev *indio_dev);
* they must be managed by the core, but without the entire interrupts/poll
* functions burden. Interrupts are irrelevant as the data flow is hardware
* mediated and distributed.
+ * @INDIO_HW_BUFFER_TRIGGERED: Very unusual mode.
+ * This is similar to INDIO_BUFFER_TRIGGERED but everything is done in hardware
+ * therefore there are no poll functions attached. It also implies the semantics
+ * of both INDIO_HARDWARE_TRIGGERED for the trigger and INDIO_BUFFER_HARDWARE
+ * for the buffer.
*/
#define INDIO_DIRECT_MODE 0x01
#define INDIO_BUFFER_TRIGGERED 0x02
@@ -373,14 +378,19 @@ s64 iio_get_time_ns(const struct iio_dev *indio_dev);
#define INDIO_BUFFER_HARDWARE 0x08
#define INDIO_EVENT_TRIGGERED 0x10
#define INDIO_HARDWARE_TRIGGERED 0x20
+#define INDIO_HW_BUFFER_TRIGGERED 0x40

-#define INDIO_ALL_BUFFER_MODES \
- (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE)
+#define INDIO_ALL_BUFFER_MODES \
+ (INDIO_BUFFER_TRIGGERED \
+ | INDIO_BUFFER_HARDWARE \
+ | INDIO_BUFFER_SOFTWARE \
+ | INDIO_HW_BUFFER_TRIGGERED)

#define INDIO_ALL_TRIGGERED_MODES \
(INDIO_BUFFER_TRIGGERED \
| INDIO_EVENT_TRIGGERED \
- | INDIO_HARDWARE_TRIGGERED)
+ | INDIO_HARDWARE_TRIGGERED \
+ | INDIO_HW_BUFFER_TRIGGERED)

#define INDIO_MAX_RAW_ELEMENTS 4


--
2.43.0