blob: 923650d53d4c82e87b542f87c3a0fbf6170dadc8 [file] [log] [blame]
// 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);
}