diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:39:57 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:39:57 +0000 |
commit | dc50eab76b709d68175a358d6e23a5a3890764d3 (patch) | |
tree | c754d0390db060af0213ff994f0ac310e4cfd6e9 /drivers/media/pci/mgb4/mgb4_trigger.c | |
parent | Adding debian version 6.6.15-2. (diff) | |
download | linux-dc50eab76b709d68175a358d6e23a5a3890764d3.tar.xz linux-dc50eab76b709d68175a358d6e23a5a3890764d3.zip |
Merging upstream version 6.7.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/media/pci/mgb4/mgb4_trigger.c')
-rw-r--r-- | drivers/media/pci/mgb4/mgb4_trigger.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/drivers/media/pci/mgb4/mgb4_trigger.c b/drivers/media/pci/mgb4/mgb4_trigger.c new file mode 100644 index 0000000000..923650d53d --- /dev/null +++ b/drivers/media/pci/mgb4/mgb4_trigger.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021-2023 Digiteq Automotive + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> + * + * This module handles the IIO trigger device. The card has two signal inputs + * for event triggers that can be used to record events related to the video + * stream. A standard linux IIO device with triggered buffer capability is + * created and configured that can be used to fetch the events with the same + * clock source as the video frames. + */ + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/pci.h> +#include <linux/dma/amd_xdma.h> +#include "mgb4_core.h" +#include "mgb4_trigger.h" + +struct trigger_data { + struct mgb4_dev *mgbdev; + struct iio_trigger *trig; +}; + +static int trigger_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct trigger_data *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + *val = mgb4_read_reg(&st->mgbdev->video, 0xA0); + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int trigger_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct trigger_data *st = iio_priv(indio_dev); + int irq = xdma_get_user_irq(st->mgbdev->xdev, 11); + + if (state) + xdma_enable_user_irq(st->mgbdev->xdev, irq); + else + xdma_disable_user_irq(st->mgbdev->xdev, irq); + + return 0; +} + +static const struct iio_trigger_ops trigger_ops = { + .set_trigger_state = &trigger_set_state, +}; + +static const struct iio_info trigger_info = { + .read_raw = trigger_read_raw, +}; + +#define TRIGGER_CHANNEL(_si) { \ + .type = IIO_ACTIVITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = _si, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .shift = 0, \ + .endianness = IIO_CPU \ + }, \ +} + +static const struct iio_chan_spec trigger_channels[] = { + TRIGGER_CHANNEL(0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static irqreturn_t trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct trigger_data *st = iio_priv(indio_dev); + struct { + u32 data; + s64 ts __aligned(8); + } scan; + + scan.data = mgb4_read_reg(&st->mgbdev->video, 0xA0); + mgb4_write_reg(&st->mgbdev->video, 0xA0, scan.data); + + iio_push_to_buffers_with_timestamp(indio_dev, &scan, pf->timestamp); + iio_trigger_notify_done(indio_dev->trig); + + mgb4_write_reg(&st->mgbdev->video, 0xB4, 1U << 11); + + return IRQ_HANDLED; +} + +static int probe_trigger(struct iio_dev *indio_dev, int irq) +{ + int ret; + struct trigger_data *st = iio_priv(indio_dev); + + st->trig = iio_trigger_alloc(&st->mgbdev->pdev->dev, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + ret = request_irq(irq, &iio_trigger_generic_data_rdy_poll, 0, + "mgb4-trigger", st->trig); + if (ret) + goto error_free_trig; + + st->trig->ops = &trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = iio_trigger_register(st->trig); + if (ret) + goto error_free_irq; + + indio_dev->trig = iio_trigger_get(st->trig); + + return 0; + +error_free_irq: + free_irq(irq, st->trig); +error_free_trig: + iio_trigger_free(st->trig); + + return ret; +} + +static void remove_trigger(struct iio_dev *indio_dev, int irq) +{ + struct trigger_data *st = iio_priv(indio_dev); + + iio_trigger_unregister(st->trig); + free_irq(irq, st->trig); + iio_trigger_free(st->trig); +} + +struct iio_dev *mgb4_trigger_create(struct mgb4_dev *mgbdev) +{ + struct iio_dev *indio_dev; + struct trigger_data *data; + struct pci_dev *pdev = mgbdev->pdev; + struct device *dev = &pdev->dev; + int rv, irq; + + indio_dev = iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return NULL; + + indio_dev->info = &trigger_info; + indio_dev->name = "mgb4"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = trigger_channels; + indio_dev->num_channels = ARRAY_SIZE(trigger_channels); + + data = iio_priv(indio_dev); + data->mgbdev = mgbdev; + + irq = xdma_get_user_irq(mgbdev->xdev, 11); + rv = probe_trigger(indio_dev, irq); + if (rv < 0) { + dev_err(dev, "iio triggered setup failed\n"); + goto error_alloc; + } + rv = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + trigger_handler, NULL); + if (rv < 0) { + dev_err(dev, "iio triggered buffer setup failed\n"); + goto error_trigger; + } + rv = iio_device_register(indio_dev); + if (rv < 0) { + dev_err(dev, "iio device register failed\n"); + goto error_buffer; + } + + return indio_dev; + +error_buffer: + iio_triggered_buffer_cleanup(indio_dev); +error_trigger: + remove_trigger(indio_dev, irq); +error_alloc: + iio_device_free(indio_dev); + + return NULL; +} + +void mgb4_trigger_free(struct iio_dev *indio_dev) +{ + struct trigger_data *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + remove_trigger(indio_dev, xdma_get_user_irq(st->mgbdev->xdev, 11)); + iio_device_free(indio_dev); +} |