// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021-2023 Digiteq Automotive * author: Martin Tuma * * 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 #include #include #include #include #include #include #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); }