Commit 0ab13674 authored by Martin Tůma's avatar Martin Tůma Committed by Hans Verkuil

media: pci: mgb4: Added Digiteq Automotive MGB4 driver

Digiteq Automotive MGB4 is a modular frame grabber PCIe card for automotive
video interfaces. As for now, two modules - FPD-Link and GMSL - are
available and supported by the driver. The card has two inputs and two
outputs (FPD-Link only).

In addition to the video interfaces it also provides a trigger signal
interface and a MTD interface for FPGA firmware upload.
Signed-off-by: default avatarMartin Tůma <martin.tuma@digiteqautomotive.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
parent fd6627cb
......@@ -6087,6 +6087,13 @@ L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/gpio/gpio-gpio-mm.c
DIGITEQ AUTOMOTIVE MGB4 V4L2 DRIVER
M: Martin Tuma <martin.tuma@digiteqautomotive.com>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/media/mgb4.rst
F: drivers/media/pci/mgb4/
DIOLAN U2C-12 I2C DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: linux-i2c@vger.kernel.org
......
......@@ -13,6 +13,7 @@ if MEDIA_PCI_SUPPORT
if MEDIA_CAMERA_SUPPORT
comment "Media capture support"
source "drivers/media/pci/mgb4/Kconfig"
source "drivers/media/pci/solo6x10/Kconfig"
source "drivers/media/pci/sta2x11/Kconfig"
source "drivers/media/pci/tw5864/Kconfig"
......
......@@ -32,6 +32,7 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821/
obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_DT3155) += dt3155/
obj-$(CONFIG_VIDEO_IVTV) += ivtv/
obj-$(CONFIG_VIDEO_MGB4) += mgb4/
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/
......
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_MGB4
tristate "Digiteq Automotive MGB4 support"
depends on VIDEO_DEV && PCI && I2C && DMADEVICES && SPI && MTD && IIO
select VIDEOBUF2_DMA_SG
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select I2C_XILINX
select SPI_XILINX
select MTD_SPI_NOR
select XILINX_XDMA
help
This is a video4linux driver for Digiteq Automotive MGB4 grabber
cards.
To compile this driver as a module, choose M here: the
module will be called mgb4.
# SPDX-License-Identifier: GPL-2.0
mgb4-objs := mgb4_regs.o mgb4_core.o mgb4_vin.o mgb4_vout.o \
mgb4_sysfs_pci.o mgb4_sysfs_in.o mgb4_sysfs_out.o \
mgb4_i2c.o mgb4_cmt.o mgb4_trigger.o mgb4_dma.o
obj-$(CONFIG_VIDEO_MGB4) += mgb4.o
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2023 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_CMT_H__
#define __MGB4_CMT_H__
#include "mgb4_vout.h"
#include "mgb4_vin.h"
u32 mgb4_cmt_set_vout_freq(struct mgb4_vout_dev *voutdev, unsigned int freq);
void mgb4_cmt_set_vin_freq_range(struct mgb4_vin_dev *vindev,
unsigned int freq_range);
#endif
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2023 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_CORE_H__
#define __MGB4_CORE_H__
#include <linux/spi/flash.h>
#include <linux/mtd/partitions.h>
#include <linux/mutex.h>
#include <linux/dmaengine.h>
#include "mgb4_regs.h"
#define MGB4_VIN_DEVICES 2
#define MGB4_VOUT_DEVICES 2
#define MGB4_MGB4_BAR_ID 0
#define MGB4_XDMA_BAR_ID 1
#define MGB4_IS_GMSL(mgbdev) \
((mgbdev)->module_version >> 4 == 2)
#define MGB4_IS_FPDL3(mgbdev) \
((mgbdev)->module_version >> 4 == 1)
struct mgb4_dma_channel {
struct dma_chan *chan;
struct completion req_compl;
};
struct mgb4_dev {
struct pci_dev *pdev;
struct platform_device *xdev;
struct mgb4_vin_dev *vin[MGB4_VIN_DEVICES];
struct mgb4_vout_dev *vout[MGB4_VOUT_DEVICES];
struct mgb4_dma_channel c2h_chan[MGB4_VIN_DEVICES];
struct mgb4_dma_channel h2c_chan[MGB4_VOUT_DEVICES];
struct dma_slave_map slave_map[MGB4_VIN_DEVICES + MGB4_VOUT_DEVICES];
struct mgb4_regs video;
struct mgb4_regs cmt;
struct clk_hw *i2c_clk;
struct clk_lookup *i2c_cl;
struct platform_device *i2c_pdev;
struct i2c_adapter *i2c_adap;
struct mutex i2c_lock; /* I2C bus access lock */
struct platform_device *spi_pdev;
struct flash_platform_data flash_data;
struct mtd_partition partitions[2];
char flash_name[16];
char fw_part_name[16];
char data_part_name[16];
char channel_names[MGB4_VIN_DEVICES + MGB4_VOUT_DEVICES][16];
struct iio_dev *indio_dev;
#if IS_REACHABLE(CONFIG_HWMON)
struct device *hwmon_dev;
#endif
unsigned long io_reconfig;
u8 module_version;
u32 serial_number;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
};
#endif
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021-2022 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*
* This module handles the DMA transfers. A standard dmaengine API as provided
* by the XDMA module is used.
*/
#include <linux/pci.h>
#include <linux/dma-direction.h>
#include "mgb4_core.h"
#include "mgb4_dma.h"
static void chan_irq(void *param)
{
struct mgb4_dma_channel *chan = param;
complete(&chan->req_compl);
}
int mgb4_dma_transfer(struct mgb4_dev *mgbdev, u32 channel, bool write,
u64 paddr, struct sg_table *sgt)
{
struct dma_slave_config cfg;
struct mgb4_dma_channel *chan;
struct dma_async_tx_descriptor *tx;
struct pci_dev *pdev = mgbdev->pdev;
int ret;
memset(&cfg, 0, sizeof(cfg));
if (write) {
cfg.direction = DMA_MEM_TO_DEV;
cfg.dst_addr = paddr;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
chan = &mgbdev->h2c_chan[channel];
} else {
cfg.direction = DMA_DEV_TO_MEM;
cfg.src_addr = paddr;
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
chan = &mgbdev->c2h_chan[channel];
}
ret = dmaengine_slave_config(chan->chan, &cfg);
if (ret) {
dev_err(&pdev->dev, "failed to config dma: %d\n", ret);
return ret;
}
tx = dmaengine_prep_slave_sg(chan->chan, sgt->sgl, sgt->nents,
cfg.direction, 0);
if (!tx) {
dev_err(&pdev->dev, "failed to prep slave sg\n");
return -EIO;
}
tx->callback = chan_irq;
tx->callback_param = chan;
ret = dma_submit_error(dmaengine_submit(tx));
if (ret) {
dev_err(&pdev->dev, "failed to submit sg\n");
return -EIO;
}
dma_async_issue_pending(chan->chan);
if (!wait_for_completion_timeout(&chan->req_compl,
msecs_to_jiffies(10000))) {
dev_err(&pdev->dev, "dma timeout\n");
dmaengine_terminate_sync(chan->chan);
return -EIO;
}
return 0;
}
int mgb4_dma_channel_init(struct mgb4_dev *mgbdev)
{
int i, ret;
char name[16];
struct pci_dev *pdev = mgbdev->pdev;
for (i = 0; i < MGB4_VIN_DEVICES; i++) {
sprintf(name, "c2h%d", i);
mgbdev->c2h_chan[i].chan = dma_request_chan(&pdev->dev, name);
if (IS_ERR(mgbdev->c2h_chan[i].chan)) {
dev_err(&pdev->dev, "failed to initialize %s", name);
ret = PTR_ERR(mgbdev->c2h_chan[i].chan);
mgbdev->c2h_chan[i].chan = NULL;
return ret;
}
init_completion(&mgbdev->c2h_chan[i].req_compl);
}
for (i = 0; i < MGB4_VOUT_DEVICES; i++) {
sprintf(name, "h2c%d", i);
mgbdev->h2c_chan[i].chan = dma_request_chan(&pdev->dev, name);
if (IS_ERR(mgbdev->h2c_chan[i].chan)) {
dev_err(&pdev->dev, "failed to initialize %s", name);
ret = PTR_ERR(mgbdev->h2c_chan[i].chan);
mgbdev->h2c_chan[i].chan = NULL;
return ret;
}
init_completion(&mgbdev->h2c_chan[i].req_compl);
}
return 0;
}
void mgb4_dma_channel_free(struct mgb4_dev *mgbdev)
{
int i;
for (i = 0; i < MGB4_VIN_DEVICES; i++) {
if (mgbdev->c2h_chan[i].chan)
dma_release_channel(mgbdev->c2h_chan[i].chan);
}
for (i = 0; i < MGB4_VOUT_DEVICES; i++) {
if (mgbdev->h2c_chan[i].chan)
dma_release_channel(mgbdev->h2c_chan[i].chan);
}
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2023 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_DMA_H__
#define __MGB4_DMA_H__
#include "mgb4_core.h"
int mgb4_dma_channel_init(struct mgb4_dev *mgbdev);
void mgb4_dma_channel_free(struct mgb4_dev *mgbdev);
int mgb4_dma_transfer(struct mgb4_dev *mgbdev, u32 channel, bool write,
u64 paddr, struct sg_table *sgt);
#endif
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021-2023 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*
* The i2c module unifies the I2C access to the serializes/deserializes. The I2C
* chips on the GMSL module use 16b addressing, the FPDL3 chips use standard
* 8b addressing.
*/
#include "mgb4_i2c.h"
static int read_r16(struct i2c_client *client, u16 reg, u8 *val, int len)
{
int ret;
u8 buf[2];
struct i2c_msg msg[2] = {
{
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = buf,
}, {
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = val,
}
};
buf[0] = (reg >> 8) & 0xff;
buf[1] = (reg >> 0) & 0xff;
ret = i2c_transfer(client->adapter, msg, 2);
if (ret < 0)
return ret;
else if (ret != 2)
return -EREMOTEIO;
else
return 0;
}
static int write_r16(struct i2c_client *client, u16 reg, const u8 *val, int len)
{
int ret;
u8 buf[4];
struct i2c_msg msg[1] = {
{
.addr = client->addr,
.flags = 0,
.len = 2 + len,
.buf = buf,
}
};
if (2 + len > sizeof(buf))
return -EINVAL;
buf[0] = (reg >> 8) & 0xff;
buf[1] = (reg >> 0) & 0xff;
memcpy(&buf[2], val, len);
ret = i2c_transfer(client->adapter, msg, 1);
if (ret < 0)
return ret;
else if (ret != 1)
return -EREMOTEIO;
else
return 0;
}
int mgb4_i2c_init(struct mgb4_i2c_client *client, struct i2c_adapter *adap,
struct i2c_board_info const *info, int addr_size)
{
client->client = i2c_new_client_device(adap, info);
if (IS_ERR(client->client))
return PTR_ERR(client->client);
client->addr_size = addr_size;
return 0;
}
void mgb4_i2c_free(struct mgb4_i2c_client *client)
{
i2c_unregister_device(client->client);
}
s32 mgb4_i2c_read_byte(struct mgb4_i2c_client *client, u16 reg)
{
int ret;
u8 b;
if (client->addr_size == 8)
return i2c_smbus_read_byte_data(client->client, reg);
ret = read_r16(client->client, reg, &b, 1);
if (ret < 0)
return ret;
return (s32)b;
}
s32 mgb4_i2c_write_byte(struct mgb4_i2c_client *client, u16 reg, u8 val)
{
if (client->addr_size == 8)
return i2c_smbus_write_byte_data(client->client, reg, val);
else
return write_r16(client->client, reg, &val, 1);
}
s32 mgb4_i2c_mask_byte(struct mgb4_i2c_client *client, u16 reg, u8 mask, u8 val)
{
s32 ret;
if (mask != 0xFF) {
ret = mgb4_i2c_read_byte(client, reg);
if (ret < 0)
return ret;
val |= (u8)ret & ~mask;
}
return mgb4_i2c_write_byte(client, reg, val);
}
int mgb4_i2c_configure(struct mgb4_i2c_client *client,
const struct mgb4_i2c_kv *values, size_t count)
{
size_t i;
s32 res;
for (i = 0; i < count; i++) {
res = mgb4_i2c_mask_byte(client, values[i].reg, values[i].mask,
values[i].val);
if (res < 0)
return res;
}
return 0;
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2023 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_I2C_H__
#define __MGB4_I2C_H__
#include <linux/i2c.h>
struct mgb4_i2c_client {
struct i2c_client *client;
int addr_size;
};
struct mgb4_i2c_kv {
u16 reg;
u8 mask;
u8 val;
};
int mgb4_i2c_init(struct mgb4_i2c_client *client, struct i2c_adapter *adap,
struct i2c_board_info const *info, int addr_size);
void mgb4_i2c_free(struct mgb4_i2c_client *client);
s32 mgb4_i2c_read_byte(struct mgb4_i2c_client *client, u16 reg);
s32 mgb4_i2c_write_byte(struct mgb4_i2c_client *client, u16 reg, u8 val);
s32 mgb4_i2c_mask_byte(struct mgb4_i2c_client *client, u16 reg, u8 mask,
u8 val);
int mgb4_i2c_configure(struct mgb4_i2c_client *client,
const struct mgb4_i2c_kv *values, size_t count);
#endif
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2022 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_IO_H__
#define __MGB4_IO_H__
#include <media/v4l2-dev.h>
#define MGB4_DEFAULT_WIDTH 1280
#define MGB4_DEFAULT_HEIGHT 640
#define MGB4_DEFAULT_PERIOD (125000000 / 60)
/* Register access error indication */
#define MGB4_ERR_NO_REG 0xFFFFFFFE
/* Frame buffer addresses greater than 0xFFFFFFFA indicate HW errors */
#define MGB4_ERR_QUEUE_TIMEOUT 0xFFFFFFFD
#define MGB4_ERR_QUEUE_EMPTY 0xFFFFFFFC
#define MGB4_ERR_QUEUE_FULL 0xFFFFFFFB
struct mgb4_frame_buffer {
struct vb2_v4l2_buffer vb;
struct list_head list;
};
static inline struct mgb4_frame_buffer *to_frame_buffer(struct vb2_v4l2_buffer *vbuf)
{
return container_of(vbuf, struct mgb4_frame_buffer, vb);
}
#endif
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021-2022 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#include <linux/ioport.h>
#include "mgb4_regs.h"
int mgb4_regs_map(struct resource *res, struct mgb4_regs *regs)
{
regs->mapbase = res->start;
regs->mapsize = res->end - res->start;
if (!request_mem_region(regs->mapbase, regs->mapsize, res->name))
return -EINVAL;
regs->membase = ioremap(regs->mapbase, regs->mapsize);
if (!regs->membase) {
release_mem_region(regs->mapbase, regs->mapsize);
return -EINVAL;
}
return 0;
}
void mgb4_regs_free(struct mgb4_regs *regs)
{
iounmap(regs->membase);
release_mem_region(regs->mapbase, regs->mapsize);
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2022 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_REGS_H__
#define __MGB4_REGS_H__
#include <linux/io.h>
struct mgb4_regs {
resource_size_t mapbase;
resource_size_t mapsize;
void __iomem *membase;
};
#define mgb4_write_reg(regs, offset, val) \
iowrite32(val, (regs)->membase + (offset))
#define mgb4_read_reg(regs, offset) \
ioread32((regs)->membase + (offset))
static inline void mgb4_mask_reg(struct mgb4_regs *regs, u32 reg, u32 mask,
u32 val)
{
u32 ret = mgb4_read_reg(regs, reg);
val |= ret & ~mask;
mgb4_write_reg(regs, reg, val);
}
int mgb4_regs_map(struct resource *res, struct mgb4_regs *regs);
void mgb4_regs_free(struct mgb4_regs *regs);
#endif
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2022 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_SYSFS_H__
#define __MGB4_SYSFS_H__
#include <linux/sysfs.h>
extern struct attribute *mgb4_pci_attrs[];
extern struct attribute *mgb4_fpdl3_in_attrs[];
extern struct attribute *mgb4_gmsl_in_attrs[];
extern struct attribute *mgb4_fpdl3_out_attrs[];
extern struct attribute *mgb4_gmsl_out_attrs[];
#endif
This diff is collapsed.
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021-2022 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*
* This module handles all the sysfs info/configuration that is related to the
* PCI card device.
*/
#include <linux/device.h>
#include "mgb4_core.h"
#include "mgb4_sysfs.h"
static ssize_t module_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mgb4_dev *mgbdev = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", mgbdev->module_version & 0x0F);
}
static ssize_t module_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mgb4_dev *mgbdev = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", mgbdev->module_version >> 4);
}
static ssize_t fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mgb4_dev *mgbdev = dev_get_drvdata(dev);
u32 config = mgb4_read_reg(&mgbdev->video, 0xC4);
return sprintf(buf, "%u\n", config & 0xFFFF);
}
static ssize_t fw_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mgb4_dev *mgbdev = dev_get_drvdata(dev);
u32 config = mgb4_read_reg(&mgbdev->video, 0xC4);
return sprintf(buf, "%u\n", config >> 24);
}
static ssize_t serial_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mgb4_dev *mgbdev = dev_get_drvdata(dev);
u32 sn = mgbdev->serial_number;
return sprintf(buf, "%03d-%03d-%03d-%03d\n", sn >> 24, (sn >> 16) & 0xFF,
(sn >> 8) & 0xFF, sn & 0xFF);
}
static DEVICE_ATTR_RO(module_version);
static DEVICE_ATTR_RO(module_type);
static DEVICE_ATTR_RO(fw_version);
static DEVICE_ATTR_RO(fw_type);
static DEVICE_ATTR_RO(serial_number);
struct attribute *mgb4_pci_attrs[] = {
&dev_attr_module_type.attr,
&dev_attr_module_version.attr,
&dev_attr_fw_type.attr,
&dev_attr_fw_version.attr,
&dev_attr_serial_number.attr,
NULL
};
// 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);
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2022 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
struct iio_dev *mgb4_trigger_create(struct mgb4_dev *mgbdev);
void mgb4_trigger_free(struct iio_dev *indio_dev);
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2023 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_VIN_H__
#define __MGB4_VIN_H__
#include <media/v4l2-device.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ctrls.h>
#include <media/videobuf2-core.h>
#include <linux/debugfs.h>
#include "mgb4_i2c.h"
struct mgb4_vin_regs {
u32 address;
u32 config;
u32 status;
u32 resolution;
u32 frame_period;
u32 sync;
u32 pclk;
u32 signal;
u32 signal2;
u32 padding;
};
struct mgb4_vin_config {
int id;
int dma_channel;
int vin_irq;
int err_irq;
struct mgb4_vin_regs regs;
};
struct mgb4_vin_dev {
struct mgb4_dev *mgbdev;
struct v4l2_device v4l2dev;
struct video_device vdev;
struct vb2_queue queue;
struct mutex lock; /* vdev lock */
spinlock_t qlock; /* video buffer queue lock */
struct list_head buf_list;
struct work_struct dma_work, err_work;
unsigned int sequence;
struct v4l2_dv_timings timings;
u32 freq_range;
u32 padding;
struct mgb4_i2c_client deser;
const struct mgb4_vin_config *config;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
struct debugfs_regset32 regset;
struct debugfs_reg32 regs[9];
#endif
};
struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id);
void mgb4_vin_free(struct mgb4_vin_dev *vindev);
#endif
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021-2023 Digiteq Automotive
* author: Martin Tuma <martin.tuma@digiteqautomotive.com>
*/
#ifndef __MGB4_VOUT_H__
#define __MGB4_VOUT_H__
#include <media/v4l2-device.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-ctrls.h>
#include <media/videobuf2-core.h>
#include <linux/debugfs.h>
#include "mgb4_i2c.h"
struct mgb4_vout_regs {
u32 address;
u32 config;
u32 status;
u32 resolution;
u32 frame_period;
u32 hsync;
u32 vsync;
u32 padding;
};
struct mgb4_vout_config {
int id;
int dma_channel;
int irq;
struct mgb4_vout_regs regs;
};
struct mgb4_vout_dev {
struct mgb4_dev *mgbdev;
struct v4l2_device v4l2dev;
struct video_device vdev;
struct vb2_queue queue;
struct mutex lock; /* vdev lock */
spinlock_t qlock; /* buffer queue lock */
struct list_head buf_list;
struct work_struct dma_work;
u32 width;
u32 height;
u32 freq;
u32 padding;
struct mgb4_i2c_client ser;
const struct mgb4_vout_config *config;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
struct debugfs_regset32 regset;
struct debugfs_reg32 regs[7];
#endif
};
struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id);
void mgb4_vout_free(struct mgb4_vout_dev *voutdev);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment