209 lines
5.3 KiB
C
209 lines
5.3 KiB
C
|
// 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);
|
||
|
}
|