Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Martin Tůma <martin.tuma@digiteqautomotive.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

authored by

Martin Tůma and committed by
Hans Verkuil
0ab13674 fd6627cb

+4956
+7
MAINTAINERS
··· 6087 6087 S: Maintained 6088 6088 F: drivers/gpio/gpio-gpio-mm.c 6089 6089 6090 + DIGITEQ AUTOMOTIVE MGB4 V4L2 DRIVER 6091 + M: Martin Tuma <martin.tuma@digiteqautomotive.com> 6092 + L: linux-media@vger.kernel.org 6093 + S: Maintained 6094 + F: Documentation/admin-guide/media/mgb4.rst 6095 + F: drivers/media/pci/mgb4/ 6096 + 6090 6097 DIOLAN U2C-12 I2C DRIVER 6091 6098 M: Guenter Roeck <linux@roeck-us.net> 6092 6099 L: linux-i2c@vger.kernel.org
+1
drivers/media/pci/Kconfig
··· 13 13 if MEDIA_CAMERA_SUPPORT 14 14 comment "Media capture support" 15 15 16 + source "drivers/media/pci/mgb4/Kconfig" 16 17 source "drivers/media/pci/solo6x10/Kconfig" 17 18 source "drivers/media/pci/sta2x11/Kconfig" 18 19 source "drivers/media/pci/tw5864/Kconfig"
+1
drivers/media/pci/Makefile
··· 32 32 obj-$(CONFIG_VIDEO_CX88) += cx88/ 33 33 obj-$(CONFIG_VIDEO_DT3155) += dt3155/ 34 34 obj-$(CONFIG_VIDEO_IVTV) += ivtv/ 35 + obj-$(CONFIG_VIDEO_MGB4) += mgb4/ 35 36 obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ 36 37 obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ 37 38 obj-$(CONFIG_VIDEO_SOLO6X10) += solo6x10/
+17
drivers/media/pci/mgb4/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + config VIDEO_MGB4 3 + tristate "Digiteq Automotive MGB4 support" 4 + depends on VIDEO_DEV && PCI && I2C && DMADEVICES && SPI && MTD && IIO 5 + select VIDEOBUF2_DMA_SG 6 + select IIO_BUFFER 7 + select IIO_TRIGGERED_BUFFER 8 + select I2C_XILINX 9 + select SPI_XILINX 10 + select MTD_SPI_NOR 11 + select XILINX_XDMA 12 + help 13 + This is a video4linux driver for Digiteq Automotive MGB4 grabber 14 + cards. 15 + 16 + To compile this driver as a module, choose M here: the 17 + module will be called mgb4.
+6
drivers/media/pci/mgb4/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + mgb4-objs := mgb4_regs.o mgb4_core.o mgb4_vin.o mgb4_vout.o \ 3 + mgb4_sysfs_pci.o mgb4_sysfs_in.o mgb4_sysfs_out.o \ 4 + mgb4_i2c.o mgb4_cmt.o mgb4_trigger.o mgb4_dma.o 5 + 6 + obj-$(CONFIG_VIDEO_MGB4) += mgb4.o
+244
drivers/media/pci/mgb4/mgb4_cmt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * The CMT module configures the FPGA Clock Management Tile (CMT) registers. For 7 + * different video signal frequencies (FPGA input signal frequencies), the FPGA 8 + * CMT registers need to be adjusted for the FPGA to work properly. The values 9 + * are precomputed based on formulas given by Xilinx in their FPGA documentation 10 + * (which are in turn full of some magic values/tables...). 11 + */ 12 + 13 + #include <linux/types.h> 14 + #include <linux/kernel.h> 15 + #include "mgb4_core.h" 16 + #include "mgb4_cmt.h" 17 + 18 + static const u16 cmt_vals_out[][15] = { 19 + {0x1208, 0x0000, 0x171C, 0x0000, 0x1E38, 0x0000, 0x11C7, 0x0000, 0x1041, 0x01BC, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x8100, }, 20 + {0x11C7, 0x0000, 0x1619, 0x0080, 0x1C71, 0x0000, 0x130D, 0x0080, 0x0041, 0x0090, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x9000, }, 21 + {0x11C7, 0x0000, 0x1619, 0x0080, 0x1C71, 0x0000, 0x165A, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 22 + {0x11C7, 0x0000, 0x1619, 0x0080, 0x1C71, 0x0000, 0x1187, 0x0080, 0x1041, 0x01EE, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x8100, }, 23 + {0x1186, 0x0000, 0x1555, 0x0000, 0x1AAA, 0x0000, 0x1451, 0x0000, 0x0042, 0x0013, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 24 + {0x11C7, 0x0000, 0x1619, 0x0080, 0x1C71, 0x0000, 0x134E, 0x0080, 0x0041, 0x005E, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 25 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x1619, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 26 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x179E, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 27 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x179F, 0x0080, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 28 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x17DF, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x8800, }, 29 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x128B, 0x0080, 0x0041, 0x00DB, 0x7C01, 0x7DE9, 0xFFFF, 0x9000, 0x0100, }, 30 + {0x1186, 0x0000, 0x1555, 0x0000, 0x1AAA, 0x0000, 0x1820, 0x0000, 0x0083, 0x00FA, 0x7DE9, 0x7DE8, 0xFFFF, 0x0900, 0x9000, }, 31 + {0x1186, 0x0000, 0x1555, 0x0000, 0x1AAA, 0x0000, 0x1187, 0x0080, 0x1041, 0x01EE, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x8100, }, 32 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x169B, 0x0080, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1800, 0x0100, }, 33 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x171C, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 34 + {0x1186, 0x0000, 0x1555, 0x0000, 0x1AAA, 0x0000, 0x1515, 0x0080, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 35 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x1493, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 36 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x15D8, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1900, 0x0100, }, 37 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x124A, 0x0080, 0x0041, 0x010D, 0x7C01, 0x7DE9, 0xFFFF, 0x9000, 0x0100, }, 38 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x175D, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 39 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x1619, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 40 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x17DF, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x8800, }, 41 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x17E0, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x9000, }, 42 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x1820, 0x0000, 0x0083, 0x00FA, 0x7DE9, 0x7DE8, 0xFFFF, 0x0900, 0x9000, }, 43 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x13D0, 0x0080, 0x0042, 0x002C, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 44 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x128B, 0x0080, 0x0041, 0x00DB, 0x7C01, 0x7DE9, 0xFFFF, 0x9000, 0x0100, }, 45 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x1820, 0x0000, 0x00C3, 0x00FA, 0x7DE9, 0x7DE8, 0xFFFF, 0x0900, 0x9000, }, 46 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x134E, 0x0080, 0x0041, 0x005E, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 47 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x1515, 0x0080, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 48 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x175D, 0x0000, 0x00C4, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 49 + {0x1145, 0x0000, 0x1452, 0x0080, 0x18E3, 0x0000, 0x11C7, 0x0000, 0x1041, 0x01BC, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x8100, }, 50 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1209, 0x0080, 0x0041, 0x013F, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x1100, }, 51 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1556, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x8000, }, 52 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x179F, 0x0080, 0x00C4, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 53 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x15D8, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1900, 0x0100, }, 54 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1105, 0x0080, 0x1041, 0x01E8, 0x6401, 0x65E9, 0xFFFF, 0x9800, 0x1100, }, 55 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1820, 0x0000, 0x00C4, 0x00FA, 0x7DE9, 0x7DE8, 0xFFFF, 0x0900, 0x9000, }, 56 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x1493, 0x0080, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 57 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x138E, 0x0000, 0x0042, 0x005E, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 58 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x17E0, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x9000, }, 59 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x165A, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 60 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x175D, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 61 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x1187, 0x0080, 0x1041, 0x01EE, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x8100, }, 62 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x175E, 0x0080, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 63 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x179E, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 64 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x134E, 0x0080, 0x0041, 0x005E, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 65 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x165A, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 66 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x16DC, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 67 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x169A, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 68 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x11C7, 0x0000, 0x1041, 0x01BC, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x8100, }, 69 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x169B, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1800, 0x0100, }, 70 + {0x1104, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x171D, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 71 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x16DB, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1800, 0x0100, }, 72 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1146, 0x0080, 0x1041, 0x0184, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x8100, }, 73 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x171C, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 74 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1451, 0x0000, 0x0042, 0x0013, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 75 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x171D, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 76 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x175D, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 77 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1452, 0x0080, 0x0042, 0x0013, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 78 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x15D8, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1900, 0x0100, }, 79 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1104, 0x0000, 0x1041, 0x01E8, 0x5801, 0x59E9, 0xFFFF, 0x9900, 0x0900, }, 80 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x179F, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 81 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1515, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 82 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x17DF, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x8800, }, 83 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1659, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 84 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1555, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x8000, }, 85 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x14D3, 0x0000, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 86 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1820, 0x0000, 0x0083, 0x00FA, 0x7DE9, 0x7DE8, 0xFFFF, 0x0900, 0x9000, }, 87 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1556, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x8000, }, 88 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1187, 0x0080, 0x1041, 0x01EE, 0x7C01, 0x7DE9, 0xFFFF, 0x9900, 0x8100, }, 89 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1452, 0x0080, 0x0082, 0x0013, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 90 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x169B, 0x0080, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1800, 0x0100, }, 91 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1514, 0x0000, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 92 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x17E0, 0x0080, 0x00C4, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x9000, }, 93 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x1515, 0x0080, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 94 + {0x10C3, 0x0000, 0x128B, 0x0080, 0x1555, 0x0000, 0x16DC, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 95 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1493, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 96 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 97 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x15D8, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1900, 0x0100, }, 98 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x171D, 0x0080, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 99 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1618, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1900, 0x0100, }, 100 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x175D, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 101 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x14D4, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 102 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1619, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 103 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x179E, 0x0000, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 104 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x179F, 0x0080, 0x00C3, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 105 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1515, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 106 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x13D0, 0x0080, 0x0042, 0x002C, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 107 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x169A, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 108 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x128B, 0x0080, 0x0041, 0x00DB, 0x7C01, 0x7DE9, 0xFFFF, 0x9000, 0x0100, }, 109 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x169B, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1800, 0x0100, }, 110 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1820, 0x0000, 0x00C3, 0x00FA, 0x7DE9, 0x7DE8, 0xFFFF, 0x0900, 0x9000, }, 111 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1556, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x8000, }, 112 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x16DB, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1800, 0x0100, }, 113 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1411, 0x0080, 0x0042, 0x002C, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 114 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x171C, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 115 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1597, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x8000, }, 116 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1451, 0x0000, 0x0042, 0x0013, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 117 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x171D, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x1800, }, 118 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x12CC, 0x0080, 0x0041, 0x00A9, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x9000, }, 119 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x175D, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 120 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1452, 0x0080, 0x0042, 0x0013, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 121 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x15D8, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1900, 0x0100, }, 122 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x175E, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 123 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1492, 0x0000, 0x0042, 0x0013, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 124 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x179F, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0800, 0x1000, }, 125 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1619, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 126 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1493, 0x0080, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 127 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x17DF, 0x0000, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x8800, }, 128 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x130D, 0x0080, 0x0041, 0x0090, 0x7C01, 0x7DE9, 0xFFFF, 0x1100, 0x9000, }, 129 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x17E0, 0x0080, 0x0083, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x9000, }, 130 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x14D3, 0x0000, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 131 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x165A, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1000, 0x9000, }, 132 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x1820, 0x0000, 0x0083, 0x00FA, 0x7DE9, 0x7DE8, 0xFFFF, 0x0900, 0x9000, }, 133 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x14D4, 0x0080, 0x0042, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x0900, 0x1000, }, 134 + {0x1082, 0x0000, 0x11C7, 0x0000, 0x138E, 0x0000, 0x169B, 0x0080, 0x0082, 0x00FA, 0x7C01, 0x7DE9, 0xFFFF, 0x1800, 0x0100, }, 135 + }; 136 + 137 + static const u16 cmt_vals_in[][13] = { 138 + {0x1082, 0x0000, 0x5104, 0x0000, 0x11C7, 0x0000, 0x1041, 0x02BC, 0x7C01, 0xFFE9, 0x9900, 0x9908, 0x8100}, 139 + {0x1104, 0x0000, 0x9208, 0x0000, 0x138E, 0x0000, 0x1041, 0x015E, 0x7C01, 0xFFE9, 0x0100, 0x0908, 0x1000}, 140 + }; 141 + 142 + static const u32 cmt_addrs_out[][15] = { 143 + {0x420, 0x424, 0x428, 0x42C, 0x430, 0x434, 0x450, 0x454, 0x458, 0x460, 0x464, 0x468, 0x4A0, 0x538, 0x53C}, 144 + {0x620, 0x624, 0x628, 0x62C, 0x630, 0x634, 0x650, 0x654, 0x658, 0x660, 0x664, 0x668, 0x6A0, 0x738, 0x73C}, 145 + }; 146 + 147 + static const u32 cmt_addrs_in[][13] = { 148 + {0x020, 0x024, 0x028, 0x02C, 0x050, 0x054, 0x058, 0x060, 0x064, 0x068, 0x0A0, 0x138, 0x13C}, 149 + {0x220, 0x224, 0x228, 0x22C, 0x250, 0x254, 0x258, 0x260, 0x264, 0x268, 0x2A0, 0x338, 0x33C}, 150 + }; 151 + 152 + static const u32 cmt_freq[] = { 153 + 25000, 25510, 26020, 26530, 26983, 27551, 28000, 28570, 154 + 29046, 29522, 30000, 30476, 30952, 31546, 32000, 32539, 155 + 33035, 33571, 33928, 34522, 35000, 35428, 36000, 36571, 156 + 36904, 37500, 38093, 38571, 39047, 39453, 40000, 40476, 157 + 40952, 41494, 41964, 42857, 43535, 44047, 44444, 45000, 158 + 45535, 46029, 46428, 46823, 47617, 48214, 48571, 49107, 159 + 49523, 50000, 50476, 50892, 51428, 52380, 53333, 53967, 160 + 54285, 55238, 55555, 55952, 57142, 58095, 58571, 59047, 161 + 59521, 60000, 60316, 60952, 61428, 61904, 62500, 63092, 162 + 63491, 64282, 65078, 65476, 66071, 66664, 67142, 67854, 163 + 68571, 69044, 69642, 70000, 71425, 72616, 73214, 73808, 164 + 74285, 75000, 75714, 76187, 76785, 77142, 78570, 80000, 165 + 80357, 80951, 81428, 82142, 82857, 83332, 83928, 84285, 166 + 85713, 87142, 87500, 88094, 88571, 89285, 90000, 90475, 167 + 91071, 91428, 92856, 94642, 168 + }; 169 + 170 + static size_t freq_srch(u32 key, const u32 *array, size_t size) 171 + { 172 + int l = 0; 173 + int r = size - 1; 174 + int m = 0; 175 + 176 + while (l <= r) { 177 + m = (l + r) / 2; 178 + if (array[m] < key) 179 + l = m + 1; 180 + else if (array[m] > key) 181 + r = m - 1; 182 + else 183 + return m; 184 + } 185 + 186 + if (r < 0 || l > size - 1) 187 + return m; 188 + else 189 + return (abs(key - array[l]) < abs(key - array[r])) ? l : r; 190 + } 191 + 192 + u32 mgb4_cmt_set_vout_freq(struct mgb4_vout_dev *voutdev, unsigned int freq) 193 + { 194 + struct mgb4_regs *video = &voutdev->mgbdev->video; 195 + const struct mgb4_vout_regs *regs = &voutdev->config->regs; 196 + const u16 *reg_set; 197 + const u32 *addr; 198 + u32 config; 199 + size_t i, index; 200 + 201 + index = freq_srch(freq, cmt_freq, ARRAY_SIZE(cmt_freq)); 202 + addr = cmt_addrs_out[voutdev->config->id]; 203 + reg_set = cmt_vals_out[index]; 204 + 205 + config = mgb4_read_reg(video, regs->config); 206 + 207 + mgb4_write_reg(video, regs->config, 0x1 | (config & ~0x3)); 208 + 209 + for (i = 0; i < ARRAY_SIZE(cmt_addrs_out[0]); i++) 210 + mgb4_write_reg(&voutdev->mgbdev->cmt, addr[i], reg_set[i]); 211 + 212 + mgb4_mask_reg(video, regs->config, 0x100, 0x100); 213 + mgb4_mask_reg(video, regs->config, 0x100, 0x0); 214 + 215 + mgb4_write_reg(video, regs->config, config & ~0x1); 216 + 217 + return cmt_freq[index]; 218 + } 219 + 220 + void mgb4_cmt_set_vin_freq_range(struct mgb4_vin_dev *vindev, 221 + unsigned int freq_range) 222 + { 223 + struct mgb4_regs *video = &vindev->mgbdev->video; 224 + const struct mgb4_vin_regs *regs = &vindev->config->regs; 225 + const u16 *reg_set; 226 + const u32 *addr; 227 + u32 config; 228 + size_t i; 229 + 230 + addr = cmt_addrs_in[vindev->config->id]; 231 + reg_set = cmt_vals_in[freq_range]; 232 + 233 + config = mgb4_read_reg(video, regs->config); 234 + 235 + mgb4_write_reg(video, regs->config, 0x1 | (config & ~0x3)); 236 + 237 + for (i = 0; i < ARRAY_SIZE(cmt_addrs_in[0]); i++) 238 + mgb4_write_reg(&vindev->mgbdev->cmt, addr[i], reg_set[i]); 239 + 240 + mgb4_mask_reg(video, regs->config, 0x1000, 0x1000); 241 + mgb4_mask_reg(video, regs->config, 0x1000, 0x0); 242 + 243 + mgb4_write_reg(video, regs->config, config & ~0x1); 244 + }
+17
drivers/media/pci/mgb4/mgb4_cmt.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_CMT_H__ 8 + #define __MGB4_CMT_H__ 9 + 10 + #include "mgb4_vout.h" 11 + #include "mgb4_vin.h" 12 + 13 + u32 mgb4_cmt_set_vout_freq(struct mgb4_vout_dev *voutdev, unsigned int freq); 14 + void mgb4_cmt_set_vin_freq_range(struct mgb4_vin_dev *vindev, 15 + unsigned int freq_range); 16 + 17 + #endif
+686
drivers/media/pci/mgb4/mgb4_core.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * This is the driver for the MGB4 video grabber card by Digiteq Automotive. 4 + * 5 + * Copyright (C) 2021-2023 Digiteq Automotive 6 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 7 + * 8 + * This is the main driver module. The DMA, I2C and SPI sub-drivers are 9 + * initialized here and the input/output v4l2 devices are created. 10 + * 11 + * The mgb4 card uses different expansion modules for different video sources 12 + * (GMSL and FPDL3 for now) so in probe() we detect the module type based on 13 + * what we see on the I2C bus and check if it matches the FPGA bitstream (there 14 + * are different bitstreams for different expansion modules). When no expansion 15 + * module is present, we still let the driver initialize to allow flashing of 16 + * the FPGA firmware using the SPI FLASH device. No v4l2 video devices are 17 + * created in this case. 18 + */ 19 + 20 + #include <linux/types.h> 21 + #include <linux/module.h> 22 + #include <linux/pci.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/clk.h> 25 + #include <linux/clk-provider.h> 26 + #include <linux/clkdev.h> 27 + #include <linux/i2c.h> 28 + #include <linux/delay.h> 29 + #include <linux/dma/amd_xdma.h> 30 + #include <linux/platform_data/amd_xdma.h> 31 + #include <linux/spi/xilinx_spi.h> 32 + #include <linux/mtd/mtd.h> 33 + #include <linux/hwmon.h> 34 + #include <linux/debugfs.h> 35 + #include "mgb4_dma.h" 36 + #include "mgb4_i2c.h" 37 + #include "mgb4_sysfs.h" 38 + #include "mgb4_vout.h" 39 + #include "mgb4_vin.h" 40 + #include "mgb4_trigger.h" 41 + #include "mgb4_core.h" 42 + 43 + #define MGB4_USER_IRQS 16 44 + 45 + ATTRIBUTE_GROUPS(mgb4_pci); 46 + 47 + static int flashid; 48 + 49 + static struct xdma_chan_info h2c_chan_info = { 50 + .dir = DMA_MEM_TO_DEV, 51 + }; 52 + 53 + static struct xdma_chan_info c2h_chan_info = { 54 + .dir = DMA_DEV_TO_MEM, 55 + }; 56 + 57 + static struct xspi_platform_data spi_platform_data = { 58 + .num_chipselect = 1, 59 + .bits_per_word = 8 60 + }; 61 + 62 + static const struct i2c_board_info extender_info = { 63 + I2C_BOARD_INFO("extender", 0x21) 64 + }; 65 + 66 + #if IS_REACHABLE(CONFIG_HWMON) 67 + static umode_t temp_is_visible(const void *data, enum hwmon_sensor_types type, 68 + u32 attr, int channel) 69 + { 70 + if (type == hwmon_temp && 71 + (attr == hwmon_temp_input || attr == hwmon_temp_label)) 72 + return 0444; 73 + else 74 + return 0; 75 + } 76 + 77 + static int temp_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, 78 + int channel, long *val) 79 + { 80 + struct mgb4_dev *mgbdev = dev_get_drvdata(dev); 81 + u32 val10, raw; 82 + 83 + if (type != hwmon_temp || attr != hwmon_temp_input) 84 + return -EOPNOTSUPP; 85 + 86 + raw = mgb4_read_reg(&mgbdev->video, 0xD0); 87 + /* register value -> Celsius degrees formula given by Xilinx */ 88 + val10 = ((((raw >> 20) & 0xFFF) * 503975) - 1118822400) / 409600; 89 + *val = val10 * 100; 90 + 91 + return 0; 92 + } 93 + 94 + static int temp_read_string(struct device *dev, enum hwmon_sensor_types type, 95 + u32 attr, int channel, const char **str) 96 + { 97 + if (type != hwmon_temp || attr != hwmon_temp_label) 98 + return -EOPNOTSUPP; 99 + 100 + *str = "FPGA Temperature"; 101 + 102 + return 0; 103 + } 104 + 105 + static const struct hwmon_ops temp_ops = { 106 + .is_visible = temp_is_visible, 107 + .read = temp_read, 108 + .read_string = temp_read_string 109 + }; 110 + 111 + static const struct hwmon_channel_info *temp_channel_info[] = { 112 + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), 113 + NULL 114 + }; 115 + 116 + static const struct hwmon_chip_info temp_chip_info = { 117 + .ops = &temp_ops, 118 + .info = temp_channel_info, 119 + }; 120 + #endif 121 + 122 + static int match_i2c_adap(struct device *dev, void *data) 123 + { 124 + return i2c_verify_adapter(dev) ? 1 : 0; 125 + } 126 + 127 + static struct i2c_adapter *get_i2c_adap(struct platform_device *pdev) 128 + { 129 + struct device *dev; 130 + 131 + mutex_lock(&pdev->dev.mutex); 132 + dev = device_find_child(&pdev->dev, NULL, match_i2c_adap); 133 + mutex_unlock(&pdev->dev.mutex); 134 + 135 + return dev ? to_i2c_adapter(dev) : NULL; 136 + } 137 + 138 + static int match_spi_adap(struct device *dev, void *data) 139 + { 140 + return to_spi_device(dev) ? 1 : 0; 141 + } 142 + 143 + static struct spi_master *get_spi_adap(struct platform_device *pdev) 144 + { 145 + struct device *dev; 146 + 147 + mutex_lock(&pdev->dev.mutex); 148 + dev = device_find_child(&pdev->dev, NULL, match_spi_adap); 149 + mutex_unlock(&pdev->dev.mutex); 150 + 151 + return dev ? container_of(dev, struct spi_master, dev) : NULL; 152 + } 153 + 154 + static int init_spi(struct mgb4_dev *mgbdev) 155 + { 156 + struct resource spi_resources[] = { 157 + { 158 + .start = 0x400, 159 + .end = 0x47f, 160 + .flags = IORESOURCE_MEM, 161 + .name = "io-memory", 162 + }, 163 + { 164 + .start = 14, 165 + .end = 14, 166 + .flags = IORESOURCE_IRQ, 167 + .name = "irq", 168 + }, 169 + }; 170 + struct spi_board_info spi_info = { 171 + .max_speed_hz = 10000000, 172 + .modalias = "m25p80", 173 + .chip_select = 0, 174 + .mode = SPI_MODE_3, 175 + }; 176 + struct pci_dev *pdev = mgbdev->pdev; 177 + struct device *dev = &pdev->dev; 178 + struct spi_master *master; 179 + struct spi_device *spi_dev; 180 + u32 irq; 181 + int rv, id; 182 + resource_size_t mapbase = pci_resource_start(pdev, MGB4_MGB4_BAR_ID); 183 + 184 + request_module("platform:xilinx_spi"); 185 + 186 + irq = xdma_get_user_irq(mgbdev->xdev, 14); 187 + xdma_enable_user_irq(mgbdev->xdev, irq); 188 + 189 + spi_resources[0].parent = &pdev->resource[MGB4_MGB4_BAR_ID]; 190 + spi_resources[0].start += mapbase; 191 + spi_resources[0].end += mapbase; 192 + spi_resources[1].start = irq; 193 + spi_resources[1].end = irq; 194 + 195 + id = pci_dev_id(pdev); 196 + mgbdev->spi_pdev = platform_device_register_resndata(dev, "xilinx_spi", 197 + id, spi_resources, 198 + ARRAY_SIZE(spi_resources), 199 + &spi_platform_data, 200 + sizeof(spi_platform_data)); 201 + if (IS_ERR(mgbdev->spi_pdev)) { 202 + dev_err(dev, "failed to register SPI device\n"); 203 + return PTR_ERR(mgbdev->spi_pdev); 204 + } 205 + 206 + master = get_spi_adap(mgbdev->spi_pdev); 207 + if (!master) { 208 + dev_err(dev, "failed to get SPI adapter\n"); 209 + rv = -EINVAL; 210 + goto err_pdev; 211 + } 212 + 213 + snprintf(mgbdev->fw_part_name, sizeof(mgbdev->fw_part_name), 214 + "mgb4-fw.%d", flashid); 215 + mgbdev->partitions[0].name = mgbdev->fw_part_name; 216 + mgbdev->partitions[0].size = 0x400000; 217 + mgbdev->partitions[0].offset = 0x400000; 218 + mgbdev->partitions[0].mask_flags = 0; 219 + 220 + snprintf(mgbdev->data_part_name, sizeof(mgbdev->data_part_name), 221 + "mgb4-data.%d", flashid); 222 + mgbdev->partitions[1].name = mgbdev->data_part_name; 223 + mgbdev->partitions[1].size = 0x10000; 224 + mgbdev->partitions[1].offset = 0xFF0000; 225 + mgbdev->partitions[1].mask_flags = MTD_CAP_NORFLASH; 226 + 227 + snprintf(mgbdev->flash_name, sizeof(mgbdev->flash_name), 228 + "mgb4-flash.%d", flashid); 229 + mgbdev->flash_data.name = mgbdev->flash_name; 230 + mgbdev->flash_data.parts = mgbdev->partitions; 231 + mgbdev->flash_data.nr_parts = ARRAY_SIZE(mgbdev->partitions); 232 + mgbdev->flash_data.type = "spi-nor"; 233 + 234 + spi_info.platform_data = &mgbdev->flash_data; 235 + 236 + spi_dev = spi_new_device(master, &spi_info); 237 + put_device(&master->dev); 238 + if (!spi_dev) { 239 + dev_err(dev, "failed to create MTD device\n"); 240 + rv = -EINVAL; 241 + goto err_pdev; 242 + } 243 + 244 + return 0; 245 + 246 + err_pdev: 247 + platform_device_unregister(mgbdev->spi_pdev); 248 + 249 + return rv; 250 + } 251 + 252 + static void free_spi(struct mgb4_dev *mgbdev) 253 + { 254 + platform_device_unregister(mgbdev->spi_pdev); 255 + } 256 + 257 + static int init_i2c(struct mgb4_dev *mgbdev) 258 + { 259 + struct resource i2c_resources[] = { 260 + { 261 + .start = 0x200, 262 + .end = 0x3ff, 263 + .flags = IORESOURCE_MEM, 264 + .name = "io-memory", 265 + }, 266 + { 267 + .start = 15, 268 + .end = 15, 269 + .flags = IORESOURCE_IRQ, 270 + .name = "irq", 271 + }, 272 + }; 273 + struct pci_dev *pdev = mgbdev->pdev; 274 + struct device *dev = &pdev->dev; 275 + char clk_name[16]; 276 + u32 irq; 277 + int rv, id; 278 + resource_size_t mapbase = pci_resource_start(pdev, MGB4_MGB4_BAR_ID); 279 + 280 + request_module("platform:xiic-i2c"); 281 + 282 + irq = xdma_get_user_irq(mgbdev->xdev, 15); 283 + xdma_enable_user_irq(mgbdev->xdev, irq); 284 + 285 + i2c_resources[0].parent = &pdev->resource[MGB4_MGB4_BAR_ID]; 286 + i2c_resources[0].start += mapbase; 287 + i2c_resources[0].end += mapbase; 288 + i2c_resources[1].start = irq; 289 + i2c_resources[1].end = irq; 290 + 291 + id = pci_dev_id(pdev); 292 + 293 + /* create dummy clock required by the xiic-i2c adapter */ 294 + snprintf(clk_name, sizeof(clk_name), "xiic-i2c.%d", id); 295 + mgbdev->i2c_clk = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 296 + 0, 125000000); 297 + if (IS_ERR(mgbdev->i2c_clk)) { 298 + dev_err(dev, "failed to register I2C clock\n"); 299 + return PTR_ERR(mgbdev->i2c_clk); 300 + } 301 + mgbdev->i2c_cl = clkdev_hw_create(mgbdev->i2c_clk, NULL, "xiic-i2c.%d", 302 + id); 303 + if (!mgbdev->i2c_cl) { 304 + dev_err(dev, "failed to register I2C clockdev\n"); 305 + rv = -ENOMEM; 306 + goto err_clk; 307 + } 308 + 309 + mgbdev->i2c_pdev = platform_device_register_resndata(dev, "xiic-i2c", 310 + id, i2c_resources, 311 + ARRAY_SIZE(i2c_resources), 312 + NULL, 0); 313 + if (IS_ERR(mgbdev->i2c_pdev)) { 314 + dev_err(dev, "failed to register I2C device\n"); 315 + rv = PTR_ERR(mgbdev->i2c_pdev); 316 + goto err_clkdev; 317 + } 318 + 319 + mgbdev->i2c_adap = get_i2c_adap(mgbdev->i2c_pdev); 320 + if (!mgbdev->i2c_adap) { 321 + dev_err(dev, "failed to get I2C adapter\n"); 322 + rv = -EINVAL; 323 + goto err_pdev; 324 + } 325 + 326 + mutex_init(&mgbdev->i2c_lock); 327 + 328 + return 0; 329 + 330 + err_pdev: 331 + platform_device_unregister(mgbdev->i2c_pdev); 332 + err_clkdev: 333 + clkdev_drop(mgbdev->i2c_cl); 334 + err_clk: 335 + clk_hw_unregister(mgbdev->i2c_clk); 336 + 337 + return rv; 338 + } 339 + 340 + static void free_i2c(struct mgb4_dev *mgbdev) 341 + { 342 + put_device(&mgbdev->i2c_adap->dev); 343 + platform_device_unregister(mgbdev->i2c_pdev); 344 + clkdev_drop(mgbdev->i2c_cl); 345 + clk_hw_unregister(mgbdev->i2c_clk); 346 + } 347 + 348 + static int get_serial_number(struct mgb4_dev *mgbdev) 349 + { 350 + struct device *dev = &mgbdev->pdev->dev; 351 + struct mtd_info *mtd; 352 + size_t rs; 353 + int rv; 354 + 355 + mgbdev->serial_number = 0; 356 + 357 + mtd = get_mtd_device_nm(mgbdev->data_part_name); 358 + if (IS_ERR(mtd)) { 359 + dev_warn(dev, "failed to get data MTD device\n"); 360 + return -ENOENT; 361 + } 362 + rv = mtd_read(mtd, 0, sizeof(mgbdev->serial_number), &rs, 363 + (u_char *)&mgbdev->serial_number); 364 + put_mtd_device(mtd); 365 + if (rv < 0 || rs != sizeof(mgbdev->serial_number)) { 366 + dev_warn(dev, "error reading MTD device\n"); 367 + return -EIO; 368 + } 369 + 370 + return 0; 371 + } 372 + 373 + static int get_module_version(struct mgb4_dev *mgbdev) 374 + { 375 + struct device *dev = &mgbdev->pdev->dev; 376 + struct mgb4_i2c_client extender; 377 + s32 version; 378 + u32 fw_version; 379 + int rv; 380 + 381 + rv = mgb4_i2c_init(&extender, mgbdev->i2c_adap, &extender_info, 8); 382 + if (rv < 0) { 383 + dev_err(dev, "failed to create extender I2C device\n"); 384 + return rv; 385 + } 386 + version = mgb4_i2c_read_byte(&extender, 0x00); 387 + mgb4_i2c_free(&extender); 388 + if (version < 0) { 389 + dev_err(dev, "error reading module version\n"); 390 + return -EIO; 391 + } 392 + 393 + mgbdev->module_version = ~((u32)version) & 0xff; 394 + if (!(MGB4_IS_FPDL3(mgbdev) || MGB4_IS_GMSL(mgbdev))) { 395 + dev_err(dev, "unknown module type\n"); 396 + return -EINVAL; 397 + } 398 + fw_version = mgb4_read_reg(&mgbdev->video, 0xC4); 399 + if (fw_version >> 24 != mgbdev->module_version >> 4) { 400 + dev_err(dev, "module/firmware type mismatch\n"); 401 + return -EINVAL; 402 + } 403 + 404 + dev_info(dev, "%s module detected\n", 405 + MGB4_IS_FPDL3(mgbdev) ? "FPDL3" : "GMSL"); 406 + 407 + return 0; 408 + } 409 + 410 + static int map_regs(struct pci_dev *pdev, struct resource *res, 411 + struct mgb4_regs *regs) 412 + { 413 + int rv; 414 + resource_size_t mapbase = pci_resource_start(pdev, MGB4_MGB4_BAR_ID); 415 + 416 + res->start += mapbase; 417 + res->end += mapbase; 418 + 419 + rv = mgb4_regs_map(res, regs); 420 + if (rv < 0) { 421 + dev_err(&pdev->dev, "failed to map %s registers\n", res->name); 422 + return rv; 423 + } 424 + 425 + return 0; 426 + } 427 + 428 + static int init_xdma(struct mgb4_dev *mgbdev) 429 + { 430 + struct xdma_platdata data; 431 + struct resource res[2] = { 0 }; 432 + struct dma_slave_map *map; 433 + struct pci_dev *pdev = mgbdev->pdev; 434 + struct device *dev = &pdev->dev; 435 + int i; 436 + 437 + res[0].start = pci_resource_start(pdev, MGB4_XDMA_BAR_ID); 438 + res[0].end = pci_resource_end(pdev, MGB4_XDMA_BAR_ID); 439 + res[0].flags = IORESOURCE_MEM; 440 + res[0].parent = &pdev->resource[MGB4_XDMA_BAR_ID]; 441 + res[1].start = pci_irq_vector(pdev, 0); 442 + res[1].end = res[1].start + MGB4_VIN_DEVICES + MGB4_VOUT_DEVICES 443 + + MGB4_USER_IRQS - 1; 444 + res[1].flags = IORESOURCE_IRQ; 445 + 446 + data.max_dma_channels = MGB4_VIN_DEVICES + MGB4_VOUT_DEVICES; 447 + data.device_map = mgbdev->slave_map; 448 + data.device_map_cnt = MGB4_VIN_DEVICES + MGB4_VOUT_DEVICES; 449 + 450 + for (i = 0; i < MGB4_VIN_DEVICES; i++) { 451 + sprintf(mgbdev->channel_names[i], "c2h%d", i); 452 + map = &data.device_map[i]; 453 + map->slave = mgbdev->channel_names[i]; 454 + map->devname = dev_name(dev); 455 + map->param = XDMA_FILTER_PARAM(&c2h_chan_info); 456 + } 457 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) { 458 + sprintf(mgbdev->channel_names[i + MGB4_VIN_DEVICES], "h2c%d", i); 459 + map = &data.device_map[i + MGB4_VIN_DEVICES]; 460 + map->slave = mgbdev->channel_names[i + MGB4_VIN_DEVICES]; 461 + map->devname = dev_name(dev); 462 + map->param = XDMA_FILTER_PARAM(&h2c_chan_info); 463 + } 464 + 465 + mgbdev->xdev = platform_device_register_resndata(dev, "xdma", 466 + PLATFORM_DEVID_AUTO, res, 467 + 2, &data, sizeof(data)); 468 + if (IS_ERR(mgbdev->xdev)) { 469 + dev_err(dev, "failed to register XDMA device\n"); 470 + return PTR_ERR(mgbdev->xdev); 471 + } 472 + 473 + return 0; 474 + } 475 + 476 + static void free_xdma(struct mgb4_dev *mgbdev) 477 + { 478 + platform_device_unregister(mgbdev->xdev); 479 + } 480 + 481 + static int mgb4_probe(struct pci_dev *pdev, const struct pci_device_id *id) 482 + { 483 + int i, rv; 484 + struct mgb4_dev *mgbdev; 485 + struct resource video = { 486 + .start = 0x0, 487 + .end = 0x100, 488 + .flags = IORESOURCE_MEM, 489 + .name = "mgb4-video", 490 + }; 491 + struct resource cmt = { 492 + .start = 0x1000, 493 + .end = 0x1800, 494 + .flags = IORESOURCE_MEM, 495 + .name = "mgb4-cmt", 496 + }; 497 + int irqs = pci_msix_vec_count(pdev); 498 + 499 + mgbdev = kzalloc(sizeof(*mgbdev), GFP_KERNEL); 500 + if (!mgbdev) 501 + return -ENOMEM; 502 + 503 + mgbdev->pdev = pdev; 504 + pci_set_drvdata(pdev, mgbdev); 505 + 506 + /* PCIe related stuff */ 507 + rv = pci_enable_device(pdev); 508 + if (rv) { 509 + dev_err(&pdev->dev, "error enabling PCI device\n"); 510 + goto err_mgbdev; 511 + } 512 + 513 + rv = pcie_capability_set_word(pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); 514 + if (rv) 515 + dev_warn(&pdev->dev, "error enabling PCIe relaxed ordering\n"); 516 + rv = pcie_capability_set_word(pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_EXT_TAG); 517 + if (rv) 518 + dev_warn(&pdev->dev, "error enabling PCIe extended tag field\n"); 519 + rv = pcie_set_readrq(pdev, 512); 520 + if (rv) 521 + dev_warn(&pdev->dev, "error setting PCIe max. memory read size\n"); 522 + pci_set_master(pdev); 523 + 524 + rv = pci_alloc_irq_vectors(pdev, irqs, irqs, PCI_IRQ_MSIX); 525 + if (rv < 0) { 526 + dev_err(&pdev->dev, "error allocating MSI-X IRQs\n"); 527 + goto err_enable_pci; 528 + } 529 + 530 + rv = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 531 + if (rv) { 532 + dev_err(&pdev->dev, "error setting DMA mask\n"); 533 + goto err_enable_pci; 534 + } 535 + 536 + /* DMA + IRQ engine */ 537 + rv = init_xdma(mgbdev); 538 + if (rv) 539 + goto err_alloc_irq; 540 + rv = mgb4_dma_channel_init(mgbdev); 541 + if (rv) 542 + goto err_dma_chan; 543 + 544 + /* mgb4 video registers */ 545 + rv = map_regs(pdev, &video, &mgbdev->video); 546 + if (rv < 0) 547 + goto err_dma_chan; 548 + /* mgb4 cmt registers */ 549 + rv = map_regs(pdev, &cmt, &mgbdev->cmt); 550 + if (rv < 0) 551 + goto err_video_regs; 552 + 553 + /* SPI FLASH */ 554 + rv = init_spi(mgbdev); 555 + if (rv < 0) 556 + goto err_cmt_regs; 557 + 558 + /* I2C controller */ 559 + rv = init_i2c(mgbdev); 560 + if (rv < 0) 561 + goto err_spi; 562 + 563 + /* PCI card related sysfs attributes */ 564 + rv = device_add_groups(&pdev->dev, mgb4_pci_groups); 565 + if (rv < 0) 566 + goto err_i2c; 567 + 568 + #if IS_REACHABLE(CONFIG_HWMON) 569 + /* HWmon (card temperature) */ 570 + mgbdev->hwmon_dev = hwmon_device_register_with_info(&pdev->dev, "mgb4", 571 + mgbdev, 572 + &temp_chip_info, 573 + NULL); 574 + #endif 575 + 576 + #ifdef CONFIG_DEBUG_FS 577 + mgbdev->debugfs = debugfs_create_dir(dev_name(&pdev->dev), NULL); 578 + #endif 579 + 580 + /* Get card serial number. On systems without MTD flash support we may 581 + * get an error thus ignore the return value. An invalid serial number 582 + * should not break anything... 583 + */ 584 + if (get_serial_number(mgbdev) < 0) 585 + dev_warn(&pdev->dev, "error reading card serial number\n"); 586 + 587 + /* Get module type. If no valid module is found, skip the video device 588 + * creation part but do not exit with error to allow flashing the card. 589 + */ 590 + rv = get_module_version(mgbdev); 591 + if (rv < 0) 592 + goto exit; 593 + 594 + /* Video input v4l2 devices */ 595 + for (i = 0; i < MGB4_VIN_DEVICES; i++) 596 + mgbdev->vin[i] = mgb4_vin_create(mgbdev, i); 597 + 598 + /* Video output v4l2 devices */ 599 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) 600 + mgbdev->vout[i] = mgb4_vout_create(mgbdev, i); 601 + 602 + /* Triggers */ 603 + mgbdev->indio_dev = mgb4_trigger_create(mgbdev); 604 + 605 + exit: 606 + flashid++; 607 + 608 + return 0; 609 + 610 + err_i2c: 611 + free_i2c(mgbdev); 612 + err_spi: 613 + free_spi(mgbdev); 614 + err_cmt_regs: 615 + mgb4_regs_free(&mgbdev->cmt); 616 + err_video_regs: 617 + mgb4_regs_free(&mgbdev->video); 618 + err_dma_chan: 619 + mgb4_dma_channel_free(mgbdev); 620 + free_xdma(mgbdev); 621 + err_alloc_irq: 622 + pci_disable_msix(pdev); 623 + err_enable_pci: 624 + pci_disable_device(pdev); 625 + err_mgbdev: 626 + kfree(mgbdev); 627 + 628 + return rv; 629 + } 630 + 631 + static void mgb4_remove(struct pci_dev *pdev) 632 + { 633 + struct mgb4_dev *mgbdev = pci_get_drvdata(pdev); 634 + int i; 635 + 636 + #ifdef CONFIG_DEBUG_FS 637 + debugfs_remove_recursive(mgbdev->debugfs); 638 + #endif 639 + #if IS_REACHABLE(CONFIG_HWMON) 640 + hwmon_device_unregister(mgbdev->hwmon_dev); 641 + #endif 642 + 643 + if (mgbdev->indio_dev) 644 + mgb4_trigger_free(mgbdev->indio_dev); 645 + 646 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) 647 + if (mgbdev->vout[i]) 648 + mgb4_vout_free(mgbdev->vout[i]); 649 + for (i = 0; i < MGB4_VIN_DEVICES; i++) 650 + if (mgbdev->vin[i]) 651 + mgb4_vin_free(mgbdev->vin[i]); 652 + 653 + device_remove_groups(&mgbdev->pdev->dev, mgb4_pci_groups); 654 + free_spi(mgbdev); 655 + free_i2c(mgbdev); 656 + mgb4_regs_free(&mgbdev->video); 657 + mgb4_regs_free(&mgbdev->cmt); 658 + 659 + mgb4_dma_channel_free(mgbdev); 660 + free_xdma(mgbdev); 661 + 662 + pci_disable_msix(mgbdev->pdev); 663 + pci_disable_device(mgbdev->pdev); 664 + 665 + kfree(mgbdev); 666 + } 667 + 668 + static const struct pci_device_id mgb4_pci_ids[] = { 669 + { PCI_DEVICE(0x1ed8, 0x0101), }, 670 + { 0, } 671 + }; 672 + MODULE_DEVICE_TABLE(pci, mgb4_pci_ids); 673 + 674 + static struct pci_driver mgb4_pci_driver = { 675 + .name = KBUILD_MODNAME, 676 + .id_table = mgb4_pci_ids, 677 + .probe = mgb4_probe, 678 + .remove = mgb4_remove, 679 + }; 680 + 681 + module_pci_driver(mgb4_pci_driver); 682 + 683 + MODULE_AUTHOR("Digiteq Automotive s.r.o."); 684 + MODULE_DESCRIPTION("Digiteq Automotive MGB4 Driver"); 685 + MODULE_LICENSE("GPL"); 686 + MODULE_SOFTDEP("pre: platform:xiic-i2c platform:xilinx_spi spi-nor");
+74
drivers/media/pci/mgb4/mgb4_core.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_CORE_H__ 8 + #define __MGB4_CORE_H__ 9 + 10 + #include <linux/spi/flash.h> 11 + #include <linux/mtd/partitions.h> 12 + #include <linux/mutex.h> 13 + #include <linux/dmaengine.h> 14 + #include "mgb4_regs.h" 15 + 16 + #define MGB4_VIN_DEVICES 2 17 + #define MGB4_VOUT_DEVICES 2 18 + 19 + #define MGB4_MGB4_BAR_ID 0 20 + #define MGB4_XDMA_BAR_ID 1 21 + 22 + #define MGB4_IS_GMSL(mgbdev) \ 23 + ((mgbdev)->module_version >> 4 == 2) 24 + #define MGB4_IS_FPDL3(mgbdev) \ 25 + ((mgbdev)->module_version >> 4 == 1) 26 + 27 + struct mgb4_dma_channel { 28 + struct dma_chan *chan; 29 + struct completion req_compl; 30 + }; 31 + 32 + struct mgb4_dev { 33 + struct pci_dev *pdev; 34 + struct platform_device *xdev; 35 + struct mgb4_vin_dev *vin[MGB4_VIN_DEVICES]; 36 + struct mgb4_vout_dev *vout[MGB4_VOUT_DEVICES]; 37 + 38 + struct mgb4_dma_channel c2h_chan[MGB4_VIN_DEVICES]; 39 + struct mgb4_dma_channel h2c_chan[MGB4_VOUT_DEVICES]; 40 + struct dma_slave_map slave_map[MGB4_VIN_DEVICES + MGB4_VOUT_DEVICES]; 41 + 42 + struct mgb4_regs video; 43 + struct mgb4_regs cmt; 44 + 45 + struct clk_hw *i2c_clk; 46 + struct clk_lookup *i2c_cl; 47 + struct platform_device *i2c_pdev; 48 + struct i2c_adapter *i2c_adap; 49 + struct mutex i2c_lock; /* I2C bus access lock */ 50 + 51 + struct platform_device *spi_pdev; 52 + struct flash_platform_data flash_data; 53 + struct mtd_partition partitions[2]; 54 + char flash_name[16]; 55 + char fw_part_name[16]; 56 + char data_part_name[16]; 57 + char channel_names[MGB4_VIN_DEVICES + MGB4_VOUT_DEVICES][16]; 58 + 59 + struct iio_dev *indio_dev; 60 + #if IS_REACHABLE(CONFIG_HWMON) 61 + struct device *hwmon_dev; 62 + #endif 63 + 64 + unsigned long io_reconfig; 65 + 66 + u8 module_version; 67 + u32 serial_number; 68 + 69 + #ifdef CONFIG_DEBUG_FS 70 + struct dentry *debugfs; 71 + #endif 72 + }; 73 + 74 + #endif
+123
drivers/media/pci/mgb4/mgb4_dma.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2022 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * This module handles the DMA transfers. A standard dmaengine API as provided 7 + * by the XDMA module is used. 8 + */ 9 + 10 + #include <linux/pci.h> 11 + #include <linux/dma-direction.h> 12 + #include "mgb4_core.h" 13 + #include "mgb4_dma.h" 14 + 15 + static void chan_irq(void *param) 16 + { 17 + struct mgb4_dma_channel *chan = param; 18 + 19 + complete(&chan->req_compl); 20 + } 21 + 22 + int mgb4_dma_transfer(struct mgb4_dev *mgbdev, u32 channel, bool write, 23 + u64 paddr, struct sg_table *sgt) 24 + { 25 + struct dma_slave_config cfg; 26 + struct mgb4_dma_channel *chan; 27 + struct dma_async_tx_descriptor *tx; 28 + struct pci_dev *pdev = mgbdev->pdev; 29 + int ret; 30 + 31 + memset(&cfg, 0, sizeof(cfg)); 32 + 33 + if (write) { 34 + cfg.direction = DMA_MEM_TO_DEV; 35 + cfg.dst_addr = paddr; 36 + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 37 + chan = &mgbdev->h2c_chan[channel]; 38 + } else { 39 + cfg.direction = DMA_DEV_TO_MEM; 40 + cfg.src_addr = paddr; 41 + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 42 + chan = &mgbdev->c2h_chan[channel]; 43 + } 44 + 45 + ret = dmaengine_slave_config(chan->chan, &cfg); 46 + if (ret) { 47 + dev_err(&pdev->dev, "failed to config dma: %d\n", ret); 48 + return ret; 49 + } 50 + 51 + tx = dmaengine_prep_slave_sg(chan->chan, sgt->sgl, sgt->nents, 52 + cfg.direction, 0); 53 + if (!tx) { 54 + dev_err(&pdev->dev, "failed to prep slave sg\n"); 55 + return -EIO; 56 + } 57 + 58 + tx->callback = chan_irq; 59 + tx->callback_param = chan; 60 + 61 + ret = dma_submit_error(dmaengine_submit(tx)); 62 + if (ret) { 63 + dev_err(&pdev->dev, "failed to submit sg\n"); 64 + return -EIO; 65 + } 66 + 67 + dma_async_issue_pending(chan->chan); 68 + 69 + if (!wait_for_completion_timeout(&chan->req_compl, 70 + msecs_to_jiffies(10000))) { 71 + dev_err(&pdev->dev, "dma timeout\n"); 72 + dmaengine_terminate_sync(chan->chan); 73 + return -EIO; 74 + } 75 + 76 + return 0; 77 + } 78 + 79 + int mgb4_dma_channel_init(struct mgb4_dev *mgbdev) 80 + { 81 + int i, ret; 82 + char name[16]; 83 + struct pci_dev *pdev = mgbdev->pdev; 84 + 85 + for (i = 0; i < MGB4_VIN_DEVICES; i++) { 86 + sprintf(name, "c2h%d", i); 87 + mgbdev->c2h_chan[i].chan = dma_request_chan(&pdev->dev, name); 88 + if (IS_ERR(mgbdev->c2h_chan[i].chan)) { 89 + dev_err(&pdev->dev, "failed to initialize %s", name); 90 + ret = PTR_ERR(mgbdev->c2h_chan[i].chan); 91 + mgbdev->c2h_chan[i].chan = NULL; 92 + return ret; 93 + } 94 + init_completion(&mgbdev->c2h_chan[i].req_compl); 95 + } 96 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) { 97 + sprintf(name, "h2c%d", i); 98 + mgbdev->h2c_chan[i].chan = dma_request_chan(&pdev->dev, name); 99 + if (IS_ERR(mgbdev->h2c_chan[i].chan)) { 100 + dev_err(&pdev->dev, "failed to initialize %s", name); 101 + ret = PTR_ERR(mgbdev->h2c_chan[i].chan); 102 + mgbdev->h2c_chan[i].chan = NULL; 103 + return ret; 104 + } 105 + init_completion(&mgbdev->h2c_chan[i].req_compl); 106 + } 107 + 108 + return 0; 109 + } 110 + 111 + void mgb4_dma_channel_free(struct mgb4_dev *mgbdev) 112 + { 113 + int i; 114 + 115 + for (i = 0; i < MGB4_VIN_DEVICES; i++) { 116 + if (mgbdev->c2h_chan[i].chan) 117 + dma_release_channel(mgbdev->c2h_chan[i].chan); 118 + } 119 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) { 120 + if (mgbdev->h2c_chan[i].chan) 121 + dma_release_channel(mgbdev->h2c_chan[i].chan); 122 + } 123 + }
+18
drivers/media/pci/mgb4/mgb4_dma.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_DMA_H__ 8 + #define __MGB4_DMA_H__ 9 + 10 + #include "mgb4_core.h" 11 + 12 + int mgb4_dma_channel_init(struct mgb4_dev *mgbdev); 13 + void mgb4_dma_channel_free(struct mgb4_dev *mgbdev); 14 + 15 + int mgb4_dma_transfer(struct mgb4_dev *mgbdev, u32 channel, bool write, 16 + u64 paddr, struct sg_table *sgt); 17 + 18 + #endif
+140
drivers/media/pci/mgb4/mgb4_i2c.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * The i2c module unifies the I2C access to the serializes/deserializes. The I2C 7 + * chips on the GMSL module use 16b addressing, the FPDL3 chips use standard 8 + * 8b addressing. 9 + */ 10 + 11 + #include "mgb4_i2c.h" 12 + 13 + static int read_r16(struct i2c_client *client, u16 reg, u8 *val, int len) 14 + { 15 + int ret; 16 + u8 buf[2]; 17 + struct i2c_msg msg[2] = { 18 + { 19 + .addr = client->addr, 20 + .flags = 0, 21 + .len = 2, 22 + .buf = buf, 23 + }, { 24 + .addr = client->addr, 25 + .flags = I2C_M_RD, 26 + .len = len, 27 + .buf = val, 28 + } 29 + }; 30 + 31 + buf[0] = (reg >> 8) & 0xff; 32 + buf[1] = (reg >> 0) & 0xff; 33 + 34 + ret = i2c_transfer(client->adapter, msg, 2); 35 + if (ret < 0) 36 + return ret; 37 + else if (ret != 2) 38 + return -EREMOTEIO; 39 + else 40 + return 0; 41 + } 42 + 43 + static int write_r16(struct i2c_client *client, u16 reg, const u8 *val, int len) 44 + { 45 + int ret; 46 + u8 buf[4]; 47 + struct i2c_msg msg[1] = { 48 + { 49 + .addr = client->addr, 50 + .flags = 0, 51 + .len = 2 + len, 52 + .buf = buf, 53 + } 54 + }; 55 + 56 + if (2 + len > sizeof(buf)) 57 + return -EINVAL; 58 + 59 + buf[0] = (reg >> 8) & 0xff; 60 + buf[1] = (reg >> 0) & 0xff; 61 + memcpy(&buf[2], val, len); 62 + 63 + ret = i2c_transfer(client->adapter, msg, 1); 64 + if (ret < 0) 65 + return ret; 66 + else if (ret != 1) 67 + return -EREMOTEIO; 68 + else 69 + return 0; 70 + } 71 + 72 + int mgb4_i2c_init(struct mgb4_i2c_client *client, struct i2c_adapter *adap, 73 + struct i2c_board_info const *info, int addr_size) 74 + { 75 + client->client = i2c_new_client_device(adap, info); 76 + if (IS_ERR(client->client)) 77 + return PTR_ERR(client->client); 78 + 79 + client->addr_size = addr_size; 80 + 81 + return 0; 82 + } 83 + 84 + void mgb4_i2c_free(struct mgb4_i2c_client *client) 85 + { 86 + i2c_unregister_device(client->client); 87 + } 88 + 89 + s32 mgb4_i2c_read_byte(struct mgb4_i2c_client *client, u16 reg) 90 + { 91 + int ret; 92 + u8 b; 93 + 94 + if (client->addr_size == 8) 95 + return i2c_smbus_read_byte_data(client->client, reg); 96 + 97 + ret = read_r16(client->client, reg, &b, 1); 98 + if (ret < 0) 99 + return ret; 100 + 101 + return (s32)b; 102 + } 103 + 104 + s32 mgb4_i2c_write_byte(struct mgb4_i2c_client *client, u16 reg, u8 val) 105 + { 106 + if (client->addr_size == 8) 107 + return i2c_smbus_write_byte_data(client->client, reg, val); 108 + else 109 + return write_r16(client->client, reg, &val, 1); 110 + } 111 + 112 + s32 mgb4_i2c_mask_byte(struct mgb4_i2c_client *client, u16 reg, u8 mask, u8 val) 113 + { 114 + s32 ret; 115 + 116 + if (mask != 0xFF) { 117 + ret = mgb4_i2c_read_byte(client, reg); 118 + if (ret < 0) 119 + return ret; 120 + val |= (u8)ret & ~mask; 121 + } 122 + 123 + return mgb4_i2c_write_byte(client, reg, val); 124 + } 125 + 126 + int mgb4_i2c_configure(struct mgb4_i2c_client *client, 127 + const struct mgb4_i2c_kv *values, size_t count) 128 + { 129 + size_t i; 130 + s32 res; 131 + 132 + for (i = 0; i < count; i++) { 133 + res = mgb4_i2c_mask_byte(client, values[i].reg, values[i].mask, 134 + values[i].val); 135 + if (res < 0) 136 + return res; 137 + } 138 + 139 + return 0; 140 + }
+35
drivers/media/pci/mgb4/mgb4_i2c.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_I2C_H__ 8 + #define __MGB4_I2C_H__ 9 + 10 + #include <linux/i2c.h> 11 + 12 + struct mgb4_i2c_client { 13 + struct i2c_client *client; 14 + int addr_size; 15 + }; 16 + 17 + struct mgb4_i2c_kv { 18 + u16 reg; 19 + u8 mask; 20 + u8 val; 21 + }; 22 + 23 + int mgb4_i2c_init(struct mgb4_i2c_client *client, struct i2c_adapter *adap, 24 + struct i2c_board_info const *info, int addr_size); 25 + void mgb4_i2c_free(struct mgb4_i2c_client *client); 26 + 27 + s32 mgb4_i2c_read_byte(struct mgb4_i2c_client *client, u16 reg); 28 + s32 mgb4_i2c_write_byte(struct mgb4_i2c_client *client, u16 reg, u8 val); 29 + s32 mgb4_i2c_mask_byte(struct mgb4_i2c_client *client, u16 reg, u8 mask, 30 + u8 val); 31 + 32 + int mgb4_i2c_configure(struct mgb4_i2c_client *client, 33 + const struct mgb4_i2c_kv *values, size_t count); 34 + 35 + #endif
+33
drivers/media/pci/mgb4/mgb4_io.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2022 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_IO_H__ 8 + #define __MGB4_IO_H__ 9 + 10 + #include <media/v4l2-dev.h> 11 + 12 + #define MGB4_DEFAULT_WIDTH 1280 13 + #define MGB4_DEFAULT_HEIGHT 640 14 + #define MGB4_DEFAULT_PERIOD (125000000 / 60) 15 + 16 + /* Register access error indication */ 17 + #define MGB4_ERR_NO_REG 0xFFFFFFFE 18 + /* Frame buffer addresses greater than 0xFFFFFFFA indicate HW errors */ 19 + #define MGB4_ERR_QUEUE_TIMEOUT 0xFFFFFFFD 20 + #define MGB4_ERR_QUEUE_EMPTY 0xFFFFFFFC 21 + #define MGB4_ERR_QUEUE_FULL 0xFFFFFFFB 22 + 23 + struct mgb4_frame_buffer { 24 + struct vb2_v4l2_buffer vb; 25 + struct list_head list; 26 + }; 27 + 28 + static inline struct mgb4_frame_buffer *to_frame_buffer(struct vb2_v4l2_buffer *vbuf) 29 + { 30 + return container_of(vbuf, struct mgb4_frame_buffer, vb); 31 + } 32 + 33 + #endif
+30
drivers/media/pci/mgb4/mgb4_regs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2022 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #include <linux/ioport.h> 8 + #include "mgb4_regs.h" 9 + 10 + int mgb4_regs_map(struct resource *res, struct mgb4_regs *regs) 11 + { 12 + regs->mapbase = res->start; 13 + regs->mapsize = res->end - res->start; 14 + 15 + if (!request_mem_region(regs->mapbase, regs->mapsize, res->name)) 16 + return -EINVAL; 17 + regs->membase = ioremap(regs->mapbase, regs->mapsize); 18 + if (!regs->membase) { 19 + release_mem_region(regs->mapbase, regs->mapsize); 20 + return -EINVAL; 21 + } 22 + 23 + return 0; 24 + } 25 + 26 + void mgb4_regs_free(struct mgb4_regs *regs) 27 + { 28 + iounmap(regs->membase); 29 + release_mem_region(regs->mapbase, regs->mapsize); 30 + }
+35
drivers/media/pci/mgb4/mgb4_regs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2022 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_REGS_H__ 8 + #define __MGB4_REGS_H__ 9 + 10 + #include <linux/io.h> 11 + 12 + struct mgb4_regs { 13 + resource_size_t mapbase; 14 + resource_size_t mapsize; 15 + void __iomem *membase; 16 + }; 17 + 18 + #define mgb4_write_reg(regs, offset, val) \ 19 + iowrite32(val, (regs)->membase + (offset)) 20 + #define mgb4_read_reg(regs, offset) \ 21 + ioread32((regs)->membase + (offset)) 22 + 23 + static inline void mgb4_mask_reg(struct mgb4_regs *regs, u32 reg, u32 mask, 24 + u32 val) 25 + { 26 + u32 ret = mgb4_read_reg(regs, reg); 27 + 28 + val |= ret & ~mask; 29 + mgb4_write_reg(regs, reg, val); 30 + } 31 + 32 + int mgb4_regs_map(struct resource *res, struct mgb4_regs *regs); 33 + void mgb4_regs_free(struct mgb4_regs *regs); 34 + 35 + #endif
+18
drivers/media/pci/mgb4/mgb4_sysfs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2022 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_SYSFS_H__ 8 + #define __MGB4_SYSFS_H__ 9 + 10 + #include <linux/sysfs.h> 11 + 12 + extern struct attribute *mgb4_pci_attrs[]; 13 + extern struct attribute *mgb4_fpdl3_in_attrs[]; 14 + extern struct attribute *mgb4_gmsl_in_attrs[]; 15 + extern struct attribute *mgb4_fpdl3_out_attrs[]; 16 + extern struct attribute *mgb4_gmsl_out_attrs[]; 17 + 18 + #endif
+772
drivers/media/pci/mgb4/mgb4_sysfs_in.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * This module handles all the sysfs info/configuration that is related to the 7 + * v4l2 input devices. 8 + */ 9 + 10 + #include <linux/device.h> 11 + #include "mgb4_core.h" 12 + #include "mgb4_i2c.h" 13 + #include "mgb4_vin.h" 14 + #include "mgb4_cmt.h" 15 + #include "mgb4_sysfs.h" 16 + 17 + /* Common for both FPDL3 and GMSL */ 18 + 19 + static ssize_t input_id_show(struct device *dev, 20 + struct device_attribute *attr, char *buf) 21 + { 22 + struct video_device *vdev = to_video_device(dev); 23 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 24 + 25 + return sprintf(buf, "%d\n", vindev->config->id); 26 + } 27 + 28 + static ssize_t oldi_lane_width_show(struct device *dev, 29 + struct device_attribute *attr, char *buf) 30 + { 31 + struct video_device *vdev = to_video_device(dev); 32 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 33 + struct mgb4_dev *mgbdev = vindev->mgbdev; 34 + u16 i2c_reg; 35 + u8 i2c_mask, i2c_single_val, i2c_dual_val; 36 + u32 config; 37 + int ret; 38 + 39 + i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49; 40 + i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03; 41 + i2c_single_val = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02; 42 + i2c_dual_val = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00; 43 + 44 + mutex_lock(&mgbdev->i2c_lock); 45 + ret = mgb4_i2c_read_byte(&vindev->deser, i2c_reg); 46 + mutex_unlock(&mgbdev->i2c_lock); 47 + if (ret < 0) 48 + return -EIO; 49 + 50 + config = mgb4_read_reg(&mgbdev->video, vindev->config->regs.config); 51 + 52 + if (((config & (1U << 9)) && ((ret & i2c_mask) != i2c_dual_val)) || 53 + (!(config & (1U << 9)) && ((ret & i2c_mask) != i2c_single_val))) { 54 + dev_err(dev, "I2C/FPGA register value mismatch\n"); 55 + return -EINVAL; 56 + } 57 + 58 + return sprintf(buf, "%s\n", config & (1U << 9) ? "1" : "0"); 59 + } 60 + 61 + /* 62 + * OLDI lane width change is expected to be called on live streams. Video device 63 + * locking/queue check is not needed. 64 + */ 65 + static ssize_t oldi_lane_width_store(struct device *dev, 66 + struct device_attribute *attr, 67 + const char *buf, size_t count) 68 + { 69 + struct video_device *vdev = to_video_device(dev); 70 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 71 + struct mgb4_dev *mgbdev = vindev->mgbdev; 72 + u32 fpga_data; 73 + u16 i2c_reg; 74 + u8 i2c_mask, i2c_data; 75 + unsigned long val; 76 + int ret; 77 + 78 + ret = kstrtoul(buf, 10, &val); 79 + if (ret) 80 + return ret; 81 + 82 + switch (val) { 83 + case 0: /* single */ 84 + fpga_data = 0; 85 + i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02; 86 + break; 87 + case 1: /* dual */ 88 + fpga_data = 1U << 9; 89 + i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00; 90 + break; 91 + default: 92 + return -EINVAL; 93 + } 94 + 95 + i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49; 96 + i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03; 97 + 98 + mutex_lock(&mgbdev->i2c_lock); 99 + ret = mgb4_i2c_mask_byte(&vindev->deser, i2c_reg, i2c_mask, i2c_data); 100 + mutex_unlock(&mgbdev->i2c_lock); 101 + if (ret < 0) 102 + return -EIO; 103 + mgb4_mask_reg(&mgbdev->video, vindev->config->regs.config, 1U << 9, 104 + fpga_data); 105 + if (MGB4_IS_GMSL(mgbdev)) { 106 + /* reset input link */ 107 + mutex_lock(&mgbdev->i2c_lock); 108 + ret = mgb4_i2c_mask_byte(&vindev->deser, 0x10, 1U << 5, 1U << 5); 109 + mutex_unlock(&mgbdev->i2c_lock); 110 + if (ret < 0) 111 + return -EIO; 112 + } 113 + 114 + return count; 115 + } 116 + 117 + static ssize_t color_mapping_show(struct device *dev, 118 + struct device_attribute *attr, char *buf) 119 + { 120 + struct video_device *vdev = to_video_device(dev); 121 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 122 + u32 config = mgb4_read_reg(&vindev->mgbdev->video, 123 + vindev->config->regs.config); 124 + 125 + return sprintf(buf, "%s\n", config & (1U << 8) ? "0" : "1"); 126 + } 127 + 128 + /* 129 + * Color mapping change is expected to be called on live streams. Video device 130 + * locking/queue check is not needed. 131 + */ 132 + static ssize_t color_mapping_store(struct device *dev, 133 + struct device_attribute *attr, 134 + const char *buf, size_t count) 135 + { 136 + struct video_device *vdev = to_video_device(dev); 137 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 138 + u32 fpga_data; 139 + unsigned long val; 140 + int ret; 141 + 142 + ret = kstrtoul(buf, 10, &val); 143 + if (ret) 144 + return ret; 145 + 146 + switch (val) { 147 + case 0: /* OLDI/JEIDA */ 148 + fpga_data = (1U << 8); 149 + break; 150 + case 1: /* SPWG/VESA */ 151 + fpga_data = 0; 152 + break; 153 + default: 154 + return -EINVAL; 155 + } 156 + 157 + mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.config, 158 + 1U << 8, fpga_data); 159 + 160 + return count; 161 + } 162 + 163 + static ssize_t link_status_show(struct device *dev, 164 + struct device_attribute *attr, char *buf) 165 + { 166 + struct video_device *vdev = to_video_device(dev); 167 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 168 + u32 status = mgb4_read_reg(&vindev->mgbdev->video, 169 + vindev->config->regs.status); 170 + 171 + return sprintf(buf, "%s\n", status & (1U << 2) ? "1" : "0"); 172 + } 173 + 174 + static ssize_t stream_status_show(struct device *dev, 175 + struct device_attribute *attr, char *buf) 176 + { 177 + struct video_device *vdev = to_video_device(dev); 178 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 179 + u32 status = mgb4_read_reg(&vindev->mgbdev->video, 180 + vindev->config->regs.status); 181 + 182 + return sprintf(buf, "%s\n", ((status & (1 << 14)) && 183 + (status & (1 << 2)) && (status & (3 << 9))) ? "1" : "0"); 184 + } 185 + 186 + static ssize_t video_width_show(struct device *dev, 187 + struct device_attribute *attr, char *buf) 188 + { 189 + struct video_device *vdev = to_video_device(dev); 190 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 191 + u32 config = mgb4_read_reg(&vindev->mgbdev->video, 192 + vindev->config->regs.resolution); 193 + 194 + return sprintf(buf, "%u\n", config >> 16); 195 + } 196 + 197 + static ssize_t video_height_show(struct device *dev, 198 + struct device_attribute *attr, char *buf) 199 + { 200 + struct video_device *vdev = to_video_device(dev); 201 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 202 + u32 config = mgb4_read_reg(&vindev->mgbdev->video, 203 + vindev->config->regs.resolution); 204 + 205 + return sprintf(buf, "%u\n", config & 0xFFFF); 206 + } 207 + 208 + static ssize_t hsync_status_show(struct device *dev, 209 + struct device_attribute *attr, char *buf) 210 + { 211 + struct video_device *vdev = to_video_device(dev); 212 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 213 + u32 status = mgb4_read_reg(&vindev->mgbdev->video, 214 + vindev->config->regs.status); 215 + u32 res; 216 + 217 + if (!(status & (1U << 11))) 218 + res = 0x02; // not available 219 + else if (status & (1U << 12)) 220 + res = 0x01; // active high 221 + else 222 + res = 0x00; // active low 223 + 224 + return sprintf(buf, "%u\n", res); 225 + } 226 + 227 + static ssize_t vsync_status_show(struct device *dev, 228 + struct device_attribute *attr, char *buf) 229 + { 230 + struct video_device *vdev = to_video_device(dev); 231 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 232 + u32 status = mgb4_read_reg(&vindev->mgbdev->video, 233 + vindev->config->regs.status); 234 + u32 res; 235 + 236 + if (!(status & (1U << 11))) 237 + res = 0x02; // not available 238 + else if (status & (1U << 13)) 239 + res = 0x01; // active high 240 + else 241 + res = 0x00; // active low 242 + 243 + return sprintf(buf, "%u\n", res); 244 + } 245 + 246 + static ssize_t hsync_gap_length_show(struct device *dev, 247 + struct device_attribute *attr, 248 + char *buf) 249 + { 250 + struct video_device *vdev = to_video_device(dev); 251 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 252 + u32 sync = mgb4_read_reg(&vindev->mgbdev->video, 253 + vindev->config->regs.sync); 254 + 255 + return sprintf(buf, "%u\n", sync >> 16); 256 + } 257 + 258 + /* 259 + * HSYNC gap length change is expected to be called on live streams. Video 260 + * device locking/queue check is not needed. 261 + */ 262 + static ssize_t hsync_gap_length_store(struct device *dev, 263 + struct device_attribute *attr, 264 + const char *buf, size_t count) 265 + { 266 + struct video_device *vdev = to_video_device(dev); 267 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 268 + unsigned long val; 269 + int ret; 270 + 271 + ret = kstrtoul(buf, 10, &val); 272 + if (ret) 273 + return ret; 274 + if (val > 0xFFFF) 275 + return -EINVAL; 276 + 277 + mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.sync, 278 + 0xFFFF0000, val << 16); 279 + 280 + return count; 281 + } 282 + 283 + static ssize_t vsync_gap_length_show(struct device *dev, 284 + struct device_attribute *attr, char *buf) 285 + { 286 + struct video_device *vdev = to_video_device(dev); 287 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 288 + u32 sync = mgb4_read_reg(&vindev->mgbdev->video, 289 + vindev->config->regs.sync); 290 + 291 + return sprintf(buf, "%u\n", sync & 0xFFFF); 292 + } 293 + 294 + /* 295 + * VSYNC gap length change is expected to be called on live streams. Video 296 + * device locking/queue check is not needed. 297 + */ 298 + static ssize_t vsync_gap_length_store(struct device *dev, 299 + struct device_attribute *attr, 300 + const char *buf, size_t count) 301 + { 302 + struct video_device *vdev = to_video_device(dev); 303 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 304 + unsigned long val; 305 + int ret; 306 + 307 + ret = kstrtoul(buf, 10, &val); 308 + if (ret) 309 + return ret; 310 + if (val > 0xFFFF) 311 + return -EINVAL; 312 + 313 + mgb4_mask_reg(&vindev->mgbdev->video, vindev->config->regs.sync, 0xFFFF, 314 + val); 315 + 316 + return count; 317 + } 318 + 319 + static ssize_t pclk_frequency_show(struct device *dev, 320 + struct device_attribute *attr, char *buf) 321 + { 322 + struct video_device *vdev = to_video_device(dev); 323 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 324 + u32 freq = mgb4_read_reg(&vindev->mgbdev->video, 325 + vindev->config->regs.pclk); 326 + 327 + return sprintf(buf, "%u\n", freq); 328 + } 329 + 330 + static ssize_t hsync_width_show(struct device *dev, 331 + struct device_attribute *attr, char *buf) 332 + { 333 + struct video_device *vdev = to_video_device(dev); 334 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 335 + u32 sig = mgb4_read_reg(&vindev->mgbdev->video, 336 + vindev->config->regs.signal); 337 + 338 + return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16); 339 + } 340 + 341 + static ssize_t vsync_width_show(struct device *dev, 342 + struct device_attribute *attr, char *buf) 343 + { 344 + struct video_device *vdev = to_video_device(dev); 345 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 346 + u32 sig = mgb4_read_reg(&vindev->mgbdev->video, 347 + vindev->config->regs.signal2); 348 + 349 + return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16); 350 + } 351 + 352 + static ssize_t hback_porch_show(struct device *dev, 353 + struct device_attribute *attr, char *buf) 354 + { 355 + struct video_device *vdev = to_video_device(dev); 356 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 357 + u32 sig = mgb4_read_reg(&vindev->mgbdev->video, 358 + vindev->config->regs.signal); 359 + 360 + return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8); 361 + } 362 + 363 + static ssize_t hfront_porch_show(struct device *dev, 364 + struct device_attribute *attr, char *buf) 365 + { 366 + struct video_device *vdev = to_video_device(dev); 367 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 368 + u32 sig = mgb4_read_reg(&vindev->mgbdev->video, 369 + vindev->config->regs.signal); 370 + 371 + return sprintf(buf, "%u\n", (sig & 0x000000FF)); 372 + } 373 + 374 + static ssize_t vback_porch_show(struct device *dev, 375 + struct device_attribute *attr, char *buf) 376 + { 377 + struct video_device *vdev = to_video_device(dev); 378 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 379 + u32 sig = mgb4_read_reg(&vindev->mgbdev->video, 380 + vindev->config->regs.signal2); 381 + 382 + return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8); 383 + } 384 + 385 + static ssize_t vfront_porch_show(struct device *dev, 386 + struct device_attribute *attr, char *buf) 387 + { 388 + struct video_device *vdev = to_video_device(dev); 389 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 390 + u32 sig = mgb4_read_reg(&vindev->mgbdev->video, 391 + vindev->config->regs.signal2); 392 + 393 + return sprintf(buf, "%u\n", (sig & 0x000000FF)); 394 + } 395 + 396 + static ssize_t frequency_range_show(struct device *dev, 397 + struct device_attribute *attr, char *buf) 398 + { 399 + struct video_device *vdev = to_video_device(dev); 400 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 401 + 402 + return sprintf(buf, "%d\n", vindev->freq_range); 403 + } 404 + 405 + static ssize_t frequency_range_store(struct device *dev, 406 + struct device_attribute *attr, 407 + const char *buf, size_t count) 408 + { 409 + struct video_device *vdev = to_video_device(dev); 410 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 411 + unsigned long val; 412 + int ret; 413 + 414 + ret = kstrtoul(buf, 10, &val); 415 + if (ret) 416 + return ret; 417 + if (val > 1) 418 + return -EINVAL; 419 + 420 + mutex_lock(vindev->vdev.lock); 421 + if (vb2_is_busy(vindev->vdev.queue)) { 422 + mutex_unlock(vindev->vdev.lock); 423 + return -EBUSY; 424 + } 425 + 426 + mgb4_cmt_set_vin_freq_range(vindev, val); 427 + vindev->freq_range = val; 428 + 429 + mutex_unlock(vindev->vdev.lock); 430 + 431 + return count; 432 + } 433 + 434 + /* FPDL3 only */ 435 + 436 + static ssize_t fpdl3_input_width_show(struct device *dev, 437 + struct device_attribute *attr, char *buf) 438 + { 439 + struct video_device *vdev = to_video_device(dev); 440 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 441 + s32 ret; 442 + 443 + mutex_lock(&vindev->mgbdev->i2c_lock); 444 + ret = mgb4_i2c_read_byte(&vindev->deser, 0x34); 445 + mutex_unlock(&vindev->mgbdev->i2c_lock); 446 + if (ret < 0) 447 + return -EIO; 448 + 449 + switch ((u8)ret & 0x18) { 450 + case 0: 451 + return sprintf(buf, "0\n"); 452 + case 0x10: 453 + return sprintf(buf, "1\n"); 454 + case 0x08: 455 + return sprintf(buf, "2\n"); 456 + default: 457 + return -EINVAL; 458 + } 459 + } 460 + 461 + /* 462 + * FPD-Link width change is expected to be called on live streams. Video device 463 + * locking/queue check is not needed. 464 + */ 465 + static ssize_t fpdl3_input_width_store(struct device *dev, 466 + struct device_attribute *attr, 467 + const char *buf, size_t count) 468 + { 469 + struct video_device *vdev = to_video_device(dev); 470 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 471 + u8 i2c_data; 472 + unsigned long val; 473 + int ret; 474 + 475 + ret = kstrtoul(buf, 10, &val); 476 + if (ret) 477 + return ret; 478 + 479 + switch (val) { 480 + case 0: /* auto */ 481 + i2c_data = 0x00; 482 + break; 483 + case 1: /* single */ 484 + i2c_data = 0x10; 485 + break; 486 + case 2: /* dual */ 487 + i2c_data = 0x08; 488 + break; 489 + default: 490 + return -EINVAL; 491 + } 492 + 493 + mutex_lock(&vindev->mgbdev->i2c_lock); 494 + ret = mgb4_i2c_mask_byte(&vindev->deser, 0x34, 0x18, i2c_data); 495 + mutex_unlock(&vindev->mgbdev->i2c_lock); 496 + if (ret < 0) 497 + return -EIO; 498 + 499 + return count; 500 + } 501 + 502 + /* GMSL only */ 503 + 504 + static ssize_t gmsl_mode_show(struct device *dev, 505 + struct device_attribute *attr, char *buf) 506 + { 507 + struct video_device *vdev = to_video_device(dev); 508 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 509 + s32 r1, r300, r3; 510 + 511 + mutex_lock(&vindev->mgbdev->i2c_lock); 512 + r1 = mgb4_i2c_read_byte(&vindev->deser, 0x01); 513 + r300 = mgb4_i2c_read_byte(&vindev->deser, 0x300); 514 + r3 = mgb4_i2c_read_byte(&vindev->deser, 0x03); 515 + mutex_unlock(&vindev->mgbdev->i2c_lock); 516 + if (r1 < 0 || r300 < 0 || r3 < 0) 517 + return -EIO; 518 + 519 + if ((r1 & 0x03) == 0x03 && (r300 & 0x0C) == 0x0C && (r3 & 0xC0) == 0xC0) 520 + return sprintf(buf, "0\n"); 521 + else if ((r1 & 0x03) == 0x02 && (r300 & 0x0C) == 0x08 && (r3 & 0xC0) == 0x00) 522 + return sprintf(buf, "1\n"); 523 + else if ((r1 & 0x03) == 0x01 && (r300 & 0x0C) == 0x04 && (r3 & 0xC0) == 0x00) 524 + return sprintf(buf, "2\n"); 525 + else if ((r1 & 0x03) == 0x00 && (r300 & 0x0C) == 0x00 && (r3 & 0xC0) == 0x00) 526 + return sprintf(buf, "3\n"); 527 + else 528 + return -EINVAL; 529 + } 530 + 531 + /* 532 + * GMSL mode change is expected to be called on live streams. Video device 533 + * locking/queue check is not needed. 534 + */ 535 + static ssize_t gmsl_mode_store(struct device *dev, 536 + struct device_attribute *attr, const char *buf, 537 + size_t count) 538 + { 539 + static const struct mgb4_i2c_kv G12[] = { 540 + {0x01, 0x03, 0x03}, {0x300, 0x0C, 0x0C}, {0x03, 0xC0, 0xC0}}; 541 + static const struct mgb4_i2c_kv G6[] = { 542 + {0x01, 0x03, 0x02}, {0x300, 0x0C, 0x08}, {0x03, 0xC0, 0x00}}; 543 + static const struct mgb4_i2c_kv G3[] = { 544 + {0x01, 0x03, 0x01}, {0x300, 0x0C, 0x04}, {0x03, 0xC0, 0x00}}; 545 + static const struct mgb4_i2c_kv G1[] = { 546 + {0x01, 0x03, 0x00}, {0x300, 0x0C, 0x00}, {0x03, 0xC0, 0x00}}; 547 + static const struct mgb4_i2c_kv reset[] = { 548 + {0x10, 1U << 5, 1U << 5}, {0x300, 1U << 6, 1U << 6}}; 549 + struct video_device *vdev = to_video_device(dev); 550 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 551 + const struct mgb4_i2c_kv *values; 552 + unsigned long val; 553 + int ret; 554 + 555 + ret = kstrtoul(buf, 10, &val); 556 + if (ret) 557 + return ret; 558 + 559 + switch (val) { 560 + case 0: /* 12Gb/s */ 561 + values = G12; 562 + break; 563 + case 1: /* 6Gb/s */ 564 + values = G6; 565 + break; 566 + case 2: /* 3Gb/s */ 567 + values = G3; 568 + break; 569 + case 3: /* 1.5Gb/s */ 570 + values = G1; 571 + break; 572 + default: 573 + return -EINVAL; 574 + } 575 + 576 + mutex_lock(&vindev->mgbdev->i2c_lock); 577 + ret = mgb4_i2c_configure(&vindev->deser, values, 3); 578 + ret |= mgb4_i2c_configure(&vindev->deser, reset, 2); 579 + mutex_unlock(&vindev->mgbdev->i2c_lock); 580 + if (ret < 0) 581 + return -EIO; 582 + 583 + return count; 584 + } 585 + 586 + static ssize_t gmsl_stream_id_show(struct device *dev, 587 + struct device_attribute *attr, char *buf) 588 + { 589 + struct video_device *vdev = to_video_device(dev); 590 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 591 + s32 ret; 592 + 593 + mutex_lock(&vindev->mgbdev->i2c_lock); 594 + ret = mgb4_i2c_read_byte(&vindev->deser, 0xA0); 595 + mutex_unlock(&vindev->mgbdev->i2c_lock); 596 + if (ret < 0) 597 + return -EIO; 598 + 599 + return sprintf(buf, "%d\n", ret & 0x03); 600 + } 601 + 602 + static ssize_t gmsl_stream_id_store(struct device *dev, 603 + struct device_attribute *attr, 604 + const char *buf, size_t count) 605 + { 606 + struct video_device *vdev = to_video_device(dev); 607 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 608 + unsigned long val; 609 + int ret; 610 + 611 + ret = kstrtoul(buf, 10, &val); 612 + if (ret) 613 + return ret; 614 + if (val > 3) 615 + return -EINVAL; 616 + 617 + mutex_lock(vindev->vdev.lock); 618 + if (vb2_is_busy(vindev->vdev.queue)) { 619 + mutex_unlock(vindev->vdev.lock); 620 + return -EBUSY; 621 + } 622 + 623 + mutex_lock(&vindev->mgbdev->i2c_lock); 624 + ret = mgb4_i2c_mask_byte(&vindev->deser, 0xA0, 0x03, (u8)val); 625 + mutex_unlock(&vindev->mgbdev->i2c_lock); 626 + 627 + mutex_unlock(vindev->vdev.lock); 628 + 629 + return (ret < 0) ? -EIO : count; 630 + } 631 + 632 + static ssize_t gmsl_fec_show(struct device *dev, struct device_attribute *attr, 633 + char *buf) 634 + { 635 + struct video_device *vdev = to_video_device(dev); 636 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 637 + s32 r3e0, r308; 638 + 639 + mutex_lock(&vindev->mgbdev->i2c_lock); 640 + r3e0 = mgb4_i2c_read_byte(&vindev->deser, 0x3E0); 641 + r308 = mgb4_i2c_read_byte(&vindev->deser, 0x308); 642 + mutex_unlock(&vindev->mgbdev->i2c_lock); 643 + if (r3e0 < 0 || r308 < 0) 644 + return -EIO; 645 + 646 + if ((r3e0 & 0x07) == 0x00 && (r308 & 0x01) == 0x00) 647 + return sprintf(buf, "0\n"); 648 + else if ((r3e0 & 0x07) == 0x07 && (r308 & 0x01) == 0x01) 649 + return sprintf(buf, "1\n"); 650 + else 651 + return -EINVAL; 652 + } 653 + 654 + /* 655 + * GMSL FEC change is expected to be called on live streams. Video device 656 + * locking/queue check is not needed. 657 + */ 658 + static ssize_t gmsl_fec_store(struct device *dev, struct device_attribute *attr, 659 + const char *buf, size_t count) 660 + { 661 + struct video_device *vdev = to_video_device(dev); 662 + struct mgb4_vin_dev *vindev = video_get_drvdata(vdev); 663 + static const struct mgb4_i2c_kv enable[] = { 664 + {0x3E0, 0x07, 0x07}, {0x308, 0x01, 0x01}}; 665 + static const struct mgb4_i2c_kv disable[] = { 666 + {0x3E0, 0x07, 0x00}, {0x308, 0x01, 0x00}}; 667 + static const struct mgb4_i2c_kv reset[] = { 668 + {0x10, 1U << 5, 1U << 5}, {0x300, 1U << 6, 1U << 6}}; 669 + const struct mgb4_i2c_kv *values; 670 + unsigned long val; 671 + int ret; 672 + 673 + ret = kstrtoul(buf, 10, &val); 674 + if (ret) 675 + return ret; 676 + 677 + switch (val) { 678 + case 0: /* disabled */ 679 + values = disable; 680 + break; 681 + case 1: /* enabled */ 682 + values = enable; 683 + break; 684 + default: 685 + return -EINVAL; 686 + } 687 + 688 + mutex_lock(&vindev->mgbdev->i2c_lock); 689 + ret = mgb4_i2c_configure(&vindev->deser, values, 2); 690 + ret |= mgb4_i2c_configure(&vindev->deser, reset, 2); 691 + mutex_unlock(&vindev->mgbdev->i2c_lock); 692 + if (ret < 0) 693 + return -EIO; 694 + 695 + return count; 696 + } 697 + 698 + static DEVICE_ATTR_RO(input_id); 699 + static DEVICE_ATTR_RW(oldi_lane_width); 700 + static DEVICE_ATTR_RW(color_mapping); 701 + static DEVICE_ATTR_RO(link_status); 702 + static DEVICE_ATTR_RO(stream_status); 703 + static DEVICE_ATTR_RO(video_width); 704 + static DEVICE_ATTR_RO(video_height); 705 + static DEVICE_ATTR_RO(hsync_status); 706 + static DEVICE_ATTR_RO(vsync_status); 707 + static DEVICE_ATTR_RW(hsync_gap_length); 708 + static DEVICE_ATTR_RW(vsync_gap_length); 709 + static DEVICE_ATTR_RO(pclk_frequency); 710 + static DEVICE_ATTR_RO(hsync_width); 711 + static DEVICE_ATTR_RO(vsync_width); 712 + static DEVICE_ATTR_RO(hback_porch); 713 + static DEVICE_ATTR_RO(hfront_porch); 714 + static DEVICE_ATTR_RO(vback_porch); 715 + static DEVICE_ATTR_RO(vfront_porch); 716 + static DEVICE_ATTR_RW(frequency_range); 717 + 718 + static DEVICE_ATTR_RW(fpdl3_input_width); 719 + 720 + static DEVICE_ATTR_RW(gmsl_mode); 721 + static DEVICE_ATTR_RW(gmsl_stream_id); 722 + static DEVICE_ATTR_RW(gmsl_fec); 723 + 724 + struct attribute *mgb4_fpdl3_in_attrs[] = { 725 + &dev_attr_input_id.attr, 726 + &dev_attr_link_status.attr, 727 + &dev_attr_stream_status.attr, 728 + &dev_attr_video_width.attr, 729 + &dev_attr_video_height.attr, 730 + &dev_attr_hsync_status.attr, 731 + &dev_attr_vsync_status.attr, 732 + &dev_attr_oldi_lane_width.attr, 733 + &dev_attr_color_mapping.attr, 734 + &dev_attr_hsync_gap_length.attr, 735 + &dev_attr_vsync_gap_length.attr, 736 + &dev_attr_pclk_frequency.attr, 737 + &dev_attr_hsync_width.attr, 738 + &dev_attr_vsync_width.attr, 739 + &dev_attr_hback_porch.attr, 740 + &dev_attr_hfront_porch.attr, 741 + &dev_attr_vback_porch.attr, 742 + &dev_attr_vfront_porch.attr, 743 + &dev_attr_frequency_range.attr, 744 + &dev_attr_fpdl3_input_width.attr, 745 + NULL 746 + }; 747 + 748 + struct attribute *mgb4_gmsl_in_attrs[] = { 749 + &dev_attr_input_id.attr, 750 + &dev_attr_link_status.attr, 751 + &dev_attr_stream_status.attr, 752 + &dev_attr_video_width.attr, 753 + &dev_attr_video_height.attr, 754 + &dev_attr_hsync_status.attr, 755 + &dev_attr_vsync_status.attr, 756 + &dev_attr_oldi_lane_width.attr, 757 + &dev_attr_color_mapping.attr, 758 + &dev_attr_hsync_gap_length.attr, 759 + &dev_attr_vsync_gap_length.attr, 760 + &dev_attr_pclk_frequency.attr, 761 + &dev_attr_hsync_width.attr, 762 + &dev_attr_vsync_width.attr, 763 + &dev_attr_hback_porch.attr, 764 + &dev_attr_hfront_porch.attr, 765 + &dev_attr_vback_porch.attr, 766 + &dev_attr_vfront_porch.attr, 767 + &dev_attr_frequency_range.attr, 768 + &dev_attr_gmsl_mode.attr, 769 + &dev_attr_gmsl_stream_id.attr, 770 + &dev_attr_gmsl_fec.attr, 771 + NULL 772 + };
+737
drivers/media/pci/mgb4/mgb4_sysfs_out.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * This module handles all the sysfs info/configuration that is related to the 7 + * v4l2 output devices. 8 + */ 9 + 10 + #include <linux/device.h> 11 + #include "mgb4_core.h" 12 + #include "mgb4_i2c.h" 13 + #include "mgb4_vout.h" 14 + #include "mgb4_vin.h" 15 + #include "mgb4_cmt.h" 16 + #include "mgb4_sysfs.h" 17 + 18 + static int loopin_cnt(struct mgb4_vin_dev *vindev) 19 + { 20 + struct mgb4_vout_dev *voutdev; 21 + u32 config; 22 + int i, cnt = 0; 23 + 24 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) { 25 + voutdev = vindev->mgbdev->vout[i]; 26 + if (!voutdev) 27 + continue; 28 + 29 + config = mgb4_read_reg(&voutdev->mgbdev->video, 30 + voutdev->config->regs.config); 31 + if ((config & 0xc) >> 2 == vindev->config->id) 32 + cnt++; 33 + } 34 + 35 + return cnt; 36 + } 37 + 38 + static bool is_busy(struct video_device *dev) 39 + { 40 + bool ret; 41 + 42 + mutex_lock(dev->lock); 43 + ret = vb2_is_busy(dev->queue); 44 + mutex_unlock(dev->lock); 45 + 46 + return ret; 47 + } 48 + 49 + /* Common for both FPDL3 and GMSL */ 50 + 51 + static ssize_t output_id_show(struct device *dev, 52 + struct device_attribute *attr, char *buf) 53 + { 54 + struct video_device *vdev = to_video_device(dev); 55 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 56 + 57 + return sprintf(buf, "%d\n", voutdev->config->id); 58 + } 59 + 60 + static ssize_t video_source_show(struct device *dev, 61 + struct device_attribute *attr, char *buf) 62 + { 63 + struct video_device *vdev = to_video_device(dev); 64 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 65 + u32 config = mgb4_read_reg(&voutdev->mgbdev->video, 66 + voutdev->config->regs.config); 67 + 68 + return sprintf(buf, "%u\n", (config & 0xc) >> 2); 69 + } 70 + 71 + /* 72 + * Video source change may affect the buffer queue of ANY video input/output on 73 + * the card thus if any of the inputs/outputs is in use, we do not allow 74 + * the change. 75 + * 76 + * As we do not want to lock all the video devices at the same time, a two-stage 77 + * locking strategy is used. In addition to the video device locking there is 78 + * a global (PCI device) variable "io_reconfig" atomically checked/set when 79 + * the reconfiguration is running. All the video devices check the variable in 80 + * their queue_setup() functions and do not allow to start the queue when 81 + * the reconfiguration has started. 82 + */ 83 + static ssize_t video_source_store(struct device *dev, 84 + struct device_attribute *attr, 85 + const char *buf, size_t count) 86 + { 87 + struct video_device *vdev = to_video_device(dev); 88 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 89 + struct mgb4_dev *mgbdev = voutdev->mgbdev; 90 + struct mgb4_vin_dev *loopin_new = NULL, *loopin_old = NULL; 91 + unsigned long val; 92 + ssize_t ret; 93 + u32 config; 94 + int i; 95 + 96 + ret = kstrtoul(buf, 10, &val); 97 + if (ret) 98 + return ret; 99 + if (val > 3) 100 + return -EINVAL; 101 + 102 + if (test_and_set_bit(0, &mgbdev->io_reconfig)) 103 + return -EBUSY; 104 + 105 + ret = -EBUSY; 106 + for (i = 0; i < MGB4_VIN_DEVICES; i++) 107 + if (mgbdev->vin[i] && is_busy(&mgbdev->vin[i]->vdev)) 108 + goto end; 109 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) 110 + if (mgbdev->vout[i] && is_busy(&mgbdev->vout[i]->vdev)) 111 + goto end; 112 + 113 + config = mgb4_read_reg(&mgbdev->video, voutdev->config->regs.config); 114 + 115 + if (((config & 0xc) >> 2) < MGB4_VIN_DEVICES) 116 + loopin_old = mgbdev->vin[(config & 0xc) >> 2]; 117 + if (val < MGB4_VIN_DEVICES) 118 + loopin_new = mgbdev->vin[val]; 119 + if (loopin_old && loopin_cnt(loopin_old) == 1) 120 + mgb4_mask_reg(&mgbdev->video, loopin_old->config->regs.config, 121 + 0x2, 0x0); 122 + if (loopin_new) 123 + mgb4_mask_reg(&mgbdev->video, loopin_new->config->regs.config, 124 + 0x2, 0x2); 125 + 126 + if (val == voutdev->config->id + MGB4_VIN_DEVICES) 127 + mgb4_write_reg(&mgbdev->video, voutdev->config->regs.config, 128 + config & ~(1 << 1)); 129 + else 130 + mgb4_write_reg(&mgbdev->video, voutdev->config->regs.config, 131 + config | (1U << 1)); 132 + 133 + mgb4_mask_reg(&mgbdev->video, voutdev->config->regs.config, 0xc, 134 + val << 2); 135 + 136 + ret = count; 137 + end: 138 + clear_bit(0, &mgbdev->io_reconfig); 139 + 140 + return ret; 141 + } 142 + 143 + static ssize_t display_width_show(struct device *dev, 144 + struct device_attribute *attr, char *buf) 145 + { 146 + struct video_device *vdev = to_video_device(dev); 147 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 148 + u32 config = mgb4_read_reg(&voutdev->mgbdev->video, 149 + voutdev->config->regs.resolution); 150 + 151 + return sprintf(buf, "%u\n", config >> 16); 152 + } 153 + 154 + static ssize_t display_width_store(struct device *dev, 155 + struct device_attribute *attr, 156 + const char *buf, size_t count) 157 + { 158 + struct video_device *vdev = to_video_device(dev); 159 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 160 + unsigned long val; 161 + int ret; 162 + 163 + ret = kstrtoul(buf, 10, &val); 164 + if (ret) 165 + return ret; 166 + if (val > 0xFFFF) 167 + return -EINVAL; 168 + 169 + mutex_lock(voutdev->vdev.lock); 170 + if (vb2_is_busy(voutdev->vdev.queue)) { 171 + mutex_unlock(voutdev->vdev.lock); 172 + return -EBUSY; 173 + } 174 + 175 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.resolution, 176 + 0xFFFF0000, val << 16); 177 + 178 + mutex_unlock(voutdev->vdev.lock); 179 + 180 + return count; 181 + } 182 + 183 + static ssize_t display_height_show(struct device *dev, 184 + struct device_attribute *attr, char *buf) 185 + { 186 + struct video_device *vdev = to_video_device(dev); 187 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 188 + u32 config = mgb4_read_reg(&voutdev->mgbdev->video, 189 + voutdev->config->regs.resolution); 190 + 191 + return sprintf(buf, "%u\n", config & 0xFFFF); 192 + } 193 + 194 + static ssize_t display_height_store(struct device *dev, 195 + struct device_attribute *attr, 196 + const char *buf, size_t count) 197 + { 198 + struct video_device *vdev = to_video_device(dev); 199 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 200 + unsigned long val; 201 + int ret; 202 + 203 + ret = kstrtoul(buf, 10, &val); 204 + if (ret) 205 + return ret; 206 + if (val > 0xFFFF) 207 + return -EINVAL; 208 + 209 + mutex_lock(voutdev->vdev.lock); 210 + if (vb2_is_busy(voutdev->vdev.queue)) { 211 + mutex_unlock(voutdev->vdev.lock); 212 + return -EBUSY; 213 + } 214 + 215 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.resolution, 216 + 0xFFFF, val); 217 + 218 + mutex_unlock(voutdev->vdev.lock); 219 + 220 + return count; 221 + } 222 + 223 + static ssize_t frame_rate_show(struct device *dev, 224 + struct device_attribute *attr, char *buf) 225 + { 226 + struct video_device *vdev = to_video_device(dev); 227 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 228 + u32 period = mgb4_read_reg(&voutdev->mgbdev->video, 229 + voutdev->config->regs.frame_period); 230 + 231 + return sprintf(buf, "%u\n", 125000000 / period); 232 + } 233 + 234 + /* 235 + * Frame rate change is expected to be called on live streams. Video device 236 + * locking/queue check is not needed. 237 + */ 238 + static ssize_t frame_rate_store(struct device *dev, 239 + struct device_attribute *attr, const char *buf, 240 + size_t count) 241 + { 242 + struct video_device *vdev = to_video_device(dev); 243 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 244 + unsigned long val; 245 + int ret; 246 + 247 + ret = kstrtoul(buf, 10, &val); 248 + if (ret) 249 + return ret; 250 + 251 + mgb4_write_reg(&voutdev->mgbdev->video, 252 + voutdev->config->regs.frame_period, 125000000 / val); 253 + 254 + return count; 255 + } 256 + 257 + static ssize_t hsync_width_show(struct device *dev, 258 + struct device_attribute *attr, char *buf) 259 + { 260 + struct video_device *vdev = to_video_device(dev); 261 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 262 + u32 sig = mgb4_read_reg(&voutdev->mgbdev->video, 263 + voutdev->config->regs.hsync); 264 + 265 + return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16); 266 + } 267 + 268 + /* 269 + * HSYNC width change is expected to be called on live streams. Video device 270 + * locking/queue check is not needed. 271 + */ 272 + static ssize_t hsync_width_store(struct device *dev, 273 + struct device_attribute *attr, const char *buf, 274 + size_t count) 275 + { 276 + struct video_device *vdev = to_video_device(dev); 277 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 278 + unsigned long val; 279 + int ret; 280 + 281 + ret = kstrtoul(buf, 10, &val); 282 + if (ret) 283 + return ret; 284 + if (val > 0xFF) 285 + return -EINVAL; 286 + 287 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync, 288 + 0x00FF0000, val << 16); 289 + 290 + return count; 291 + } 292 + 293 + static ssize_t vsync_width_show(struct device *dev, 294 + struct device_attribute *attr, char *buf) 295 + { 296 + struct video_device *vdev = to_video_device(dev); 297 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 298 + u32 sig = mgb4_read_reg(&voutdev->mgbdev->video, 299 + voutdev->config->regs.vsync); 300 + 301 + return sprintf(buf, "%u\n", (sig & 0x00FF0000) >> 16); 302 + } 303 + 304 + /* 305 + * VSYNC vidth change is expected to be called on live streams. Video device 306 + * locking/queue check is not needed. 307 + */ 308 + static ssize_t vsync_width_store(struct device *dev, 309 + struct device_attribute *attr, const char *buf, 310 + size_t count) 311 + { 312 + struct video_device *vdev = to_video_device(dev); 313 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 314 + unsigned long val; 315 + int ret; 316 + 317 + ret = kstrtoul(buf, 10, &val); 318 + if (ret) 319 + return ret; 320 + if (val > 0xFF) 321 + return -EINVAL; 322 + 323 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync, 324 + 0x00FF0000, val << 16); 325 + 326 + return count; 327 + } 328 + 329 + static ssize_t hback_porch_show(struct device *dev, 330 + struct device_attribute *attr, char *buf) 331 + { 332 + struct video_device *vdev = to_video_device(dev); 333 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 334 + u32 sig = mgb4_read_reg(&voutdev->mgbdev->video, 335 + voutdev->config->regs.hsync); 336 + 337 + return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8); 338 + } 339 + 340 + /* 341 + * hback porch change is expected to be called on live streams. Video device 342 + * locking/queue check is not needed. 343 + */ 344 + static ssize_t hback_porch_store(struct device *dev, 345 + struct device_attribute *attr, const char *buf, 346 + size_t count) 347 + { 348 + struct video_device *vdev = to_video_device(dev); 349 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 350 + unsigned long val; 351 + int ret; 352 + 353 + ret = kstrtoul(buf, 10, &val); 354 + if (ret) 355 + return ret; 356 + if (val > 0xFF) 357 + return -EINVAL; 358 + 359 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync, 360 + 0x0000FF00, val << 8); 361 + 362 + return count; 363 + } 364 + 365 + static ssize_t vback_porch_show(struct device *dev, 366 + struct device_attribute *attr, char *buf) 367 + { 368 + struct video_device *vdev = to_video_device(dev); 369 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 370 + u32 sig = mgb4_read_reg(&voutdev->mgbdev->video, 371 + voutdev->config->regs.vsync); 372 + 373 + return sprintf(buf, "%u\n", (sig & 0x0000FF00) >> 8); 374 + } 375 + 376 + /* 377 + * vback porch change is expected to be called on live streams. Video device 378 + * locking/queue check is not needed. 379 + */ 380 + static ssize_t vback_porch_store(struct device *dev, 381 + struct device_attribute *attr, const char *buf, 382 + size_t count) 383 + { 384 + struct video_device *vdev = to_video_device(dev); 385 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 386 + unsigned long val; 387 + int ret; 388 + 389 + ret = kstrtoul(buf, 10, &val); 390 + if (ret) 391 + return ret; 392 + if (val > 0xFF) 393 + return -EINVAL; 394 + 395 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync, 396 + 0x0000FF00, val << 8); 397 + 398 + return count; 399 + } 400 + 401 + static ssize_t hfront_porch_show(struct device *dev, 402 + struct device_attribute *attr, char *buf) 403 + { 404 + struct video_device *vdev = to_video_device(dev); 405 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 406 + u32 sig = mgb4_read_reg(&voutdev->mgbdev->video, 407 + voutdev->config->regs.hsync); 408 + 409 + return sprintf(buf, "%u\n", (sig & 0x000000FF)); 410 + } 411 + 412 + /* 413 + * hfront porch change is expected to be called on live streams. Video device 414 + * locking/queue check is not needed. 415 + */ 416 + static ssize_t hfront_porch_store(struct device *dev, 417 + struct device_attribute *attr, 418 + const char *buf, size_t count) 419 + { 420 + struct video_device *vdev = to_video_device(dev); 421 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 422 + unsigned long val; 423 + int ret; 424 + 425 + ret = kstrtoul(buf, 10, &val); 426 + if (ret) 427 + return ret; 428 + if (val > 0xFF) 429 + return -EINVAL; 430 + 431 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync, 432 + 0x000000FF, val); 433 + 434 + return count; 435 + } 436 + 437 + static ssize_t vfront_porch_show(struct device *dev, 438 + struct device_attribute *attr, char *buf) 439 + { 440 + struct video_device *vdev = to_video_device(dev); 441 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 442 + u32 sig = mgb4_read_reg(&voutdev->mgbdev->video, 443 + voutdev->config->regs.vsync); 444 + 445 + return sprintf(buf, "%u\n", (sig & 0x000000FF)); 446 + } 447 + 448 + /* 449 + * vfront porch change is expected to be called on live streams. Video device 450 + * locking/queue check is not needed. 451 + */ 452 + static ssize_t vfront_porch_store(struct device *dev, 453 + struct device_attribute *attr, const char *buf, 454 + size_t count) 455 + { 456 + struct video_device *vdev = to_video_device(dev); 457 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 458 + unsigned long val; 459 + int ret; 460 + 461 + ret = kstrtoul(buf, 10, &val); 462 + if (ret) 463 + return ret; 464 + if (val > 0xFF) 465 + return -EINVAL; 466 + 467 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync, 468 + 0x000000FF, val); 469 + 470 + return count; 471 + } 472 + 473 + /* FPDL3 only */ 474 + 475 + static ssize_t hsync_polarity_show(struct device *dev, 476 + struct device_attribute *attr, char *buf) 477 + { 478 + struct video_device *vdev = to_video_device(dev); 479 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 480 + u32 config = mgb4_read_reg(&voutdev->mgbdev->video, 481 + voutdev->config->regs.hsync); 482 + 483 + return sprintf(buf, "%u\n", (config & (1U << 31)) >> 31); 484 + } 485 + 486 + /* 487 + * HSYNC polarity change is expected to be called on live streams. Video device 488 + * locking/queue check is not needed. 489 + */ 490 + static ssize_t hsync_polarity_store(struct device *dev, 491 + struct device_attribute *attr, 492 + const char *buf, size_t count) 493 + { 494 + struct video_device *vdev = to_video_device(dev); 495 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 496 + unsigned long val; 497 + int ret; 498 + 499 + ret = kstrtoul(buf, 10, &val); 500 + if (ret) 501 + return ret; 502 + if (val > 1) 503 + return -EINVAL; 504 + 505 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.hsync, 506 + (1U << 31), val << 31); 507 + 508 + return count; 509 + } 510 + 511 + static ssize_t vsync_polarity_show(struct device *dev, 512 + struct device_attribute *attr, char *buf) 513 + { 514 + struct video_device *vdev = to_video_device(dev); 515 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 516 + u32 config = mgb4_read_reg(&voutdev->mgbdev->video, 517 + voutdev->config->regs.vsync); 518 + 519 + return sprintf(buf, "%u\n", (config & (1U << 31)) >> 31); 520 + } 521 + 522 + /* 523 + * VSYNC polarity change is expected to be called on live streams. Video device 524 + * locking/queue check is not needed. 525 + */ 526 + static ssize_t vsync_polarity_store(struct device *dev, 527 + struct device_attribute *attr, 528 + const char *buf, size_t count) 529 + { 530 + struct video_device *vdev = to_video_device(dev); 531 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 532 + unsigned long val; 533 + int ret; 534 + 535 + ret = kstrtoul(buf, 10, &val); 536 + if (ret) 537 + return ret; 538 + if (val > 1) 539 + return -EINVAL; 540 + 541 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync, 542 + (1U << 31), val << 31); 543 + 544 + return count; 545 + } 546 + 547 + static ssize_t de_polarity_show(struct device *dev, 548 + struct device_attribute *attr, char *buf) 549 + { 550 + struct video_device *vdev = to_video_device(dev); 551 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 552 + u32 config = mgb4_read_reg(&voutdev->mgbdev->video, 553 + voutdev->config->regs.vsync); 554 + 555 + return sprintf(buf, "%u\n", (config & (1U << 30)) >> 30); 556 + } 557 + 558 + /* 559 + * DE polarity change is expected to be called on live streams. Video device 560 + * locking/queue check is not needed. 561 + */ 562 + static ssize_t de_polarity_store(struct device *dev, 563 + struct device_attribute *attr, const char *buf, 564 + size_t count) 565 + { 566 + struct video_device *vdev = to_video_device(dev); 567 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 568 + unsigned long val; 569 + int ret; 570 + 571 + ret = kstrtoul(buf, 10, &val); 572 + if (ret) 573 + return ret; 574 + if (val > 1) 575 + return -EINVAL; 576 + 577 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.vsync, 578 + (1U << 30), val << 30); 579 + 580 + return count; 581 + } 582 + 583 + static ssize_t fpdl3_output_width_show(struct device *dev, 584 + struct device_attribute *attr, char *buf) 585 + { 586 + struct video_device *vdev = to_video_device(dev); 587 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 588 + s32 ret; 589 + 590 + mutex_lock(&voutdev->mgbdev->i2c_lock); 591 + ret = mgb4_i2c_read_byte(&voutdev->ser, 0x5B); 592 + mutex_unlock(&voutdev->mgbdev->i2c_lock); 593 + if (ret < 0) 594 + return -EIO; 595 + 596 + switch ((u8)ret & 0x03) { 597 + case 0: 598 + return sprintf(buf, "0\n"); 599 + case 1: 600 + return sprintf(buf, "1\n"); 601 + case 3: 602 + return sprintf(buf, "2\n"); 603 + default: 604 + return -EINVAL; 605 + } 606 + } 607 + 608 + /* 609 + * FPD-Link width change is expected to be called on live streams. Video device 610 + * locking/queue check is not needed. 611 + */ 612 + static ssize_t fpdl3_output_width_store(struct device *dev, 613 + struct device_attribute *attr, 614 + const char *buf, size_t count) 615 + { 616 + struct video_device *vdev = to_video_device(dev); 617 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 618 + u8 i2c_data; 619 + unsigned long val; 620 + int ret; 621 + 622 + ret = kstrtoul(buf, 10, &val); 623 + if (ret) 624 + return ret; 625 + 626 + switch (val) { 627 + case 0: /* auto */ 628 + i2c_data = 0x00; 629 + break; 630 + case 1: /* single */ 631 + i2c_data = 0x01; 632 + break; 633 + case 2: /* dual */ 634 + i2c_data = 0x03; 635 + break; 636 + default: 637 + return -EINVAL; 638 + } 639 + 640 + mutex_lock(&voutdev->mgbdev->i2c_lock); 641 + ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x5B, 0x03, i2c_data); 642 + mutex_unlock(&voutdev->mgbdev->i2c_lock); 643 + if (ret < 0) 644 + return -EIO; 645 + 646 + return count; 647 + } 648 + 649 + static ssize_t pclk_frequency_show(struct device *dev, 650 + struct device_attribute *attr, char *buf) 651 + { 652 + struct video_device *vdev = to_video_device(dev); 653 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 654 + 655 + return sprintf(buf, "%u\n", voutdev->freq); 656 + } 657 + 658 + static ssize_t pclk_frequency_store(struct device *dev, 659 + struct device_attribute *attr, 660 + const char *buf, size_t count) 661 + { 662 + struct video_device *vdev = to_video_device(dev); 663 + struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); 664 + unsigned long val; 665 + int ret; 666 + unsigned int dp; 667 + 668 + ret = kstrtoul(buf, 10, &val); 669 + if (ret) 670 + return ret; 671 + 672 + mutex_lock(voutdev->vdev.lock); 673 + if (vb2_is_busy(voutdev->vdev.queue)) { 674 + mutex_unlock(voutdev->vdev.lock); 675 + return -EBUSY; 676 + } 677 + 678 + dp = (val > 50000) ? 1 : 0; 679 + voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, val >> dp) << dp; 680 + 681 + mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.config, 682 + 0x10, dp << 4); 683 + mutex_lock(&voutdev->mgbdev->i2c_lock); 684 + ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x4F, 1 << 6, ((~dp) & 1) << 6); 685 + mutex_unlock(&voutdev->mgbdev->i2c_lock); 686 + 687 + mutex_unlock(voutdev->vdev.lock); 688 + 689 + return (ret < 0) ? -EIO : count; 690 + } 691 + 692 + static DEVICE_ATTR_RO(output_id); 693 + static DEVICE_ATTR_RW(video_source); 694 + static DEVICE_ATTR_RW(display_width); 695 + static DEVICE_ATTR_RW(display_height); 696 + static DEVICE_ATTR_RW(frame_rate); 697 + static DEVICE_ATTR_RW(hsync_polarity); 698 + static DEVICE_ATTR_RW(vsync_polarity); 699 + static DEVICE_ATTR_RW(de_polarity); 700 + static DEVICE_ATTR_RW(pclk_frequency); 701 + static DEVICE_ATTR_RW(hsync_width); 702 + static DEVICE_ATTR_RW(vsync_width); 703 + static DEVICE_ATTR_RW(hback_porch); 704 + static DEVICE_ATTR_RW(hfront_porch); 705 + static DEVICE_ATTR_RW(vback_porch); 706 + static DEVICE_ATTR_RW(vfront_porch); 707 + 708 + static DEVICE_ATTR_RW(fpdl3_output_width); 709 + 710 + struct attribute *mgb4_fpdl3_out_attrs[] = { 711 + &dev_attr_output_id.attr, 712 + &dev_attr_video_source.attr, 713 + &dev_attr_display_width.attr, 714 + &dev_attr_display_height.attr, 715 + &dev_attr_frame_rate.attr, 716 + &dev_attr_hsync_polarity.attr, 717 + &dev_attr_vsync_polarity.attr, 718 + &dev_attr_de_polarity.attr, 719 + &dev_attr_pclk_frequency.attr, 720 + &dev_attr_hsync_width.attr, 721 + &dev_attr_vsync_width.attr, 722 + &dev_attr_hback_porch.attr, 723 + &dev_attr_hfront_porch.attr, 724 + &dev_attr_vback_porch.attr, 725 + &dev_attr_vfront_porch.attr, 726 + &dev_attr_fpdl3_output_width.attr, 727 + NULL 728 + }; 729 + 730 + struct attribute *mgb4_gmsl_out_attrs[] = { 731 + &dev_attr_output_id.attr, 732 + &dev_attr_video_source.attr, 733 + &dev_attr_display_width.attr, 734 + &dev_attr_display_height.attr, 735 + &dev_attr_frame_rate.attr, 736 + NULL 737 + };
+71
drivers/media/pci/mgb4/mgb4_sysfs_pci.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2022 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * This module handles all the sysfs info/configuration that is related to the 7 + * PCI card device. 8 + */ 9 + 10 + #include <linux/device.h> 11 + #include "mgb4_core.h" 12 + #include "mgb4_sysfs.h" 13 + 14 + static ssize_t module_version_show(struct device *dev, 15 + struct device_attribute *attr, char *buf) 16 + { 17 + struct mgb4_dev *mgbdev = dev_get_drvdata(dev); 18 + 19 + return sprintf(buf, "%u\n", mgbdev->module_version & 0x0F); 20 + } 21 + 22 + static ssize_t module_type_show(struct device *dev, 23 + struct device_attribute *attr, char *buf) 24 + { 25 + struct mgb4_dev *mgbdev = dev_get_drvdata(dev); 26 + 27 + return sprintf(buf, "%u\n", mgbdev->module_version >> 4); 28 + } 29 + 30 + static ssize_t fw_version_show(struct device *dev, 31 + struct device_attribute *attr, char *buf) 32 + { 33 + struct mgb4_dev *mgbdev = dev_get_drvdata(dev); 34 + u32 config = mgb4_read_reg(&mgbdev->video, 0xC4); 35 + 36 + return sprintf(buf, "%u\n", config & 0xFFFF); 37 + } 38 + 39 + static ssize_t fw_type_show(struct device *dev, 40 + struct device_attribute *attr, char *buf) 41 + { 42 + struct mgb4_dev *mgbdev = dev_get_drvdata(dev); 43 + u32 config = mgb4_read_reg(&mgbdev->video, 0xC4); 44 + 45 + return sprintf(buf, "%u\n", config >> 24); 46 + } 47 + 48 + static ssize_t serial_number_show(struct device *dev, 49 + struct device_attribute *attr, char *buf) 50 + { 51 + struct mgb4_dev *mgbdev = dev_get_drvdata(dev); 52 + u32 sn = mgbdev->serial_number; 53 + 54 + return sprintf(buf, "%03d-%03d-%03d-%03d\n", sn >> 24, (sn >> 16) & 0xFF, 55 + (sn >> 8) & 0xFF, sn & 0xFF); 56 + } 57 + 58 + static DEVICE_ATTR_RO(module_version); 59 + static DEVICE_ATTR_RO(module_type); 60 + static DEVICE_ATTR_RO(fw_version); 61 + static DEVICE_ATTR_RO(fw_type); 62 + static DEVICE_ATTR_RO(serial_number); 63 + 64 + struct attribute *mgb4_pci_attrs[] = { 65 + &dev_attr_module_type.attr, 66 + &dev_attr_module_version.attr, 67 + &dev_attr_fw_type.attr, 68 + &dev_attr_fw_version.attr, 69 + &dev_attr_serial_number.attr, 70 + NULL 71 + };
+208
drivers/media/pci/mgb4/mgb4_trigger.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * This module handles the IIO trigger device. The card has two signal inputs 7 + * for event triggers that can be used to record events related to the video 8 + * stream. A standard linux IIO device with triggered buffer capability is 9 + * created and configured that can be used to fetch the events with the same 10 + * clock source as the video frames. 11 + */ 12 + 13 + #include <linux/iio/iio.h> 14 + #include <linux/iio/buffer.h> 15 + #include <linux/iio/trigger.h> 16 + #include <linux/iio/trigger_consumer.h> 17 + #include <linux/iio/triggered_buffer.h> 18 + #include <linux/pci.h> 19 + #include <linux/dma/amd_xdma.h> 20 + #include "mgb4_core.h" 21 + #include "mgb4_trigger.h" 22 + 23 + struct trigger_data { 24 + struct mgb4_dev *mgbdev; 25 + struct iio_trigger *trig; 26 + }; 27 + 28 + static int trigger_read_raw(struct iio_dev *indio_dev, 29 + struct iio_chan_spec const *chan, int *val, 30 + int *val2, long mask) 31 + { 32 + struct trigger_data *st = iio_priv(indio_dev); 33 + 34 + switch (mask) { 35 + case IIO_CHAN_INFO_RAW: 36 + if (iio_buffer_enabled(indio_dev)) 37 + return -EBUSY; 38 + *val = mgb4_read_reg(&st->mgbdev->video, 0xA0); 39 + 40 + return IIO_VAL_INT; 41 + } 42 + 43 + return -EINVAL; 44 + } 45 + 46 + static int trigger_set_state(struct iio_trigger *trig, bool state) 47 + { 48 + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); 49 + struct trigger_data *st = iio_priv(indio_dev); 50 + int irq = xdma_get_user_irq(st->mgbdev->xdev, 11); 51 + 52 + if (state) 53 + xdma_enable_user_irq(st->mgbdev->xdev, irq); 54 + else 55 + xdma_disable_user_irq(st->mgbdev->xdev, irq); 56 + 57 + return 0; 58 + } 59 + 60 + static const struct iio_trigger_ops trigger_ops = { 61 + .set_trigger_state = &trigger_set_state, 62 + }; 63 + 64 + static const struct iio_info trigger_info = { 65 + .read_raw = trigger_read_raw, 66 + }; 67 + 68 + #define TRIGGER_CHANNEL(_si) { \ 69 + .type = IIO_ACTIVITY, \ 70 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 71 + .scan_index = _si, \ 72 + .scan_type = { \ 73 + .sign = 'u', \ 74 + .realbits = 32, \ 75 + .storagebits = 32, \ 76 + .shift = 0, \ 77 + .endianness = IIO_CPU \ 78 + }, \ 79 + } 80 + 81 + static const struct iio_chan_spec trigger_channels[] = { 82 + TRIGGER_CHANNEL(0), 83 + IIO_CHAN_SOFT_TIMESTAMP(1), 84 + }; 85 + 86 + static irqreturn_t trigger_handler(int irq, void *p) 87 + { 88 + struct iio_poll_func *pf = p; 89 + struct iio_dev *indio_dev = pf->indio_dev; 90 + struct trigger_data *st = iio_priv(indio_dev); 91 + struct { 92 + u32 data; 93 + s64 ts __aligned(8); 94 + } scan; 95 + 96 + scan.data = mgb4_read_reg(&st->mgbdev->video, 0xA0); 97 + mgb4_write_reg(&st->mgbdev->video, 0xA0, scan.data); 98 + 99 + iio_push_to_buffers_with_timestamp(indio_dev, &scan, pf->timestamp); 100 + iio_trigger_notify_done(indio_dev->trig); 101 + 102 + mgb4_write_reg(&st->mgbdev->video, 0xB4, 1U << 11); 103 + 104 + return IRQ_HANDLED; 105 + } 106 + 107 + static int probe_trigger(struct iio_dev *indio_dev, int irq) 108 + { 109 + int ret; 110 + struct trigger_data *st = iio_priv(indio_dev); 111 + 112 + st->trig = iio_trigger_alloc(&st->mgbdev->pdev->dev, "%s-dev%d", 113 + indio_dev->name, iio_device_id(indio_dev)); 114 + if (!st->trig) 115 + return -ENOMEM; 116 + 117 + ret = request_irq(irq, &iio_trigger_generic_data_rdy_poll, 0, 118 + "mgb4-trigger", st->trig); 119 + if (ret) 120 + goto error_free_trig; 121 + 122 + st->trig->ops = &trigger_ops; 123 + iio_trigger_set_drvdata(st->trig, indio_dev); 124 + ret = iio_trigger_register(st->trig); 125 + if (ret) 126 + goto error_free_irq; 127 + 128 + indio_dev->trig = iio_trigger_get(st->trig); 129 + 130 + return 0; 131 + 132 + error_free_irq: 133 + free_irq(irq, st->trig); 134 + error_free_trig: 135 + iio_trigger_free(st->trig); 136 + 137 + return ret; 138 + } 139 + 140 + static void remove_trigger(struct iio_dev *indio_dev, int irq) 141 + { 142 + struct trigger_data *st = iio_priv(indio_dev); 143 + 144 + iio_trigger_unregister(st->trig); 145 + free_irq(irq, st->trig); 146 + iio_trigger_free(st->trig); 147 + } 148 + 149 + struct iio_dev *mgb4_trigger_create(struct mgb4_dev *mgbdev) 150 + { 151 + struct iio_dev *indio_dev; 152 + struct trigger_data *data; 153 + struct pci_dev *pdev = mgbdev->pdev; 154 + struct device *dev = &pdev->dev; 155 + int rv, irq; 156 + 157 + indio_dev = iio_device_alloc(dev, sizeof(*data)); 158 + if (!indio_dev) 159 + return NULL; 160 + 161 + indio_dev->info = &trigger_info; 162 + indio_dev->name = "mgb4"; 163 + indio_dev->modes = INDIO_DIRECT_MODE; 164 + indio_dev->channels = trigger_channels; 165 + indio_dev->num_channels = ARRAY_SIZE(trigger_channels); 166 + 167 + data = iio_priv(indio_dev); 168 + data->mgbdev = mgbdev; 169 + 170 + irq = xdma_get_user_irq(mgbdev->xdev, 11); 171 + rv = probe_trigger(indio_dev, irq); 172 + if (rv < 0) { 173 + dev_err(dev, "iio triggered setup failed\n"); 174 + goto error_alloc; 175 + } 176 + rv = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, 177 + trigger_handler, NULL); 178 + if (rv < 0) { 179 + dev_err(dev, "iio triggered buffer setup failed\n"); 180 + goto error_trigger; 181 + } 182 + rv = iio_device_register(indio_dev); 183 + if (rv < 0) { 184 + dev_err(dev, "iio device register failed\n"); 185 + goto error_buffer; 186 + } 187 + 188 + return indio_dev; 189 + 190 + error_buffer: 191 + iio_triggered_buffer_cleanup(indio_dev); 192 + error_trigger: 193 + remove_trigger(indio_dev, irq); 194 + error_alloc: 195 + iio_device_free(indio_dev); 196 + 197 + return NULL; 198 + } 199 + 200 + void mgb4_trigger_free(struct iio_dev *indio_dev) 201 + { 202 + struct trigger_data *st = iio_priv(indio_dev); 203 + 204 + iio_device_unregister(indio_dev); 205 + iio_triggered_buffer_cleanup(indio_dev); 206 + remove_trigger(indio_dev, xdma_get_user_irq(st->mgbdev->xdev, 11)); 207 + iio_device_free(indio_dev); 208 + }
+8
drivers/media/pci/mgb4/mgb4_trigger.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2022 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + struct iio_dev *mgb4_trigger_create(struct mgb4_dev *mgbdev); 8 + void mgb4_trigger_free(struct iio_dev *indio_dev);
+939
drivers/media/pci/mgb4/mgb4_vin.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * This is the v4l2 input device module. It initializes the signal deserializers 7 + * and creates the v4l2 video devices. The input signal can change at any time 8 + * which is handled by the "timings" callbacks and an IRQ based watcher, that 9 + * emits the V4L2_EVENT_SOURCE_CHANGE event in case of a signal source change. 10 + * 11 + * When the device is in loopback mode (a direct, in HW, in->out frame passing 12 + * mode) the card's frame queue must be running regardless of whether a v4l2 13 + * stream is running and the output parameters like frame buffers padding must 14 + * be in sync with the input parameters. 15 + */ 16 + 17 + #include <linux/pci.h> 18 + #include <linux/workqueue.h> 19 + #include <linux/align.h> 20 + #include <linux/dma/amd_xdma.h> 21 + #include <media/v4l2-ioctl.h> 22 + #include <media/videobuf2-v4l2.h> 23 + #include <media/videobuf2-dma-sg.h> 24 + #include <media/v4l2-dv-timings.h> 25 + #include <media/v4l2-event.h> 26 + #include "mgb4_core.h" 27 + #include "mgb4_dma.h" 28 + #include "mgb4_sysfs.h" 29 + #include "mgb4_io.h" 30 + #include "mgb4_vout.h" 31 + #include "mgb4_vin.h" 32 + 33 + ATTRIBUTE_GROUPS(mgb4_fpdl3_in); 34 + ATTRIBUTE_GROUPS(mgb4_gmsl_in); 35 + 36 + static const struct mgb4_vin_config vin_cfg[] = { 37 + {0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28}}, 38 + {1, 1, 1, 7, {0x40, 0x30, 0x34, 0x38, 0x4C, 0x44, 0x48, 0x50, 0x54, 0x58}} 39 + }; 40 + 41 + static const struct i2c_board_info fpdl3_deser_info[] = { 42 + {I2C_BOARD_INFO("deserializer1", 0x38)}, 43 + {I2C_BOARD_INFO("deserializer2", 0x36)}, 44 + }; 45 + 46 + static const struct i2c_board_info gmsl_deser_info[] = { 47 + {I2C_BOARD_INFO("deserializer1", 0x4C)}, 48 + {I2C_BOARD_INFO("deserializer2", 0x2A)}, 49 + }; 50 + 51 + static const struct mgb4_i2c_kv fpdl3_i2c[] = { 52 + {0x06, 0xFF, 0x04}, {0x07, 0xFF, 0x01}, {0x45, 0xFF, 0xE8}, 53 + {0x49, 0xFF, 0x00}, {0x34, 0xFF, 0x00}, {0x23, 0xFF, 0x00} 54 + }; 55 + 56 + static const struct mgb4_i2c_kv gmsl_i2c[] = { 57 + {0x01, 0x03, 0x03}, {0x300, 0x0C, 0x0C}, {0x03, 0xC0, 0xC0}, 58 + {0x1CE, 0x0E, 0x0E}, {0x11, 0x05, 0x00}, {0x05, 0xC0, 0x40}, 59 + {0x307, 0x0F, 0x00}, {0xA0, 0x03, 0x00}, {0x3E0, 0x07, 0x07}, 60 + {0x308, 0x01, 0x01}, {0x10, 0x20, 0x20}, {0x300, 0x40, 0x40} 61 + }; 62 + 63 + static const struct v4l2_dv_timings_cap video_timings_cap = { 64 + .type = V4L2_DV_BT_656_1120, 65 + .bt = { 66 + .min_width = 320, 67 + .max_width = 4096, 68 + .min_height = 240, 69 + .max_height = 2160, 70 + .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ 71 + .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ 72 + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | 73 + V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, 74 + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | 75 + V4L2_DV_BT_CAP_CUSTOM, 76 + }, 77 + }; 78 + 79 + /* 80 + * Returns the video output connected with the given video input if the input 81 + * is in loopback mode. 82 + */ 83 + static struct mgb4_vout_dev *loopback_dev(struct mgb4_vin_dev *vindev, int i) 84 + { 85 + struct mgb4_vout_dev *voutdev; 86 + u32 config; 87 + 88 + voutdev = vindev->mgbdev->vout[i]; 89 + if (!voutdev) 90 + return NULL; 91 + 92 + config = mgb4_read_reg(&voutdev->mgbdev->video, 93 + voutdev->config->regs.config); 94 + if ((config & 0xc) >> 2 == vindev->config->id) 95 + return voutdev; 96 + 97 + return NULL; 98 + } 99 + 100 + /* 101 + * Check, whether the loopback mode - a HW INPUT->OUTPUT transmission - is 102 + * enabled on the given input. 103 + */ 104 + static int loopback_active(struct mgb4_vin_dev *vindev) 105 + { 106 + int i; 107 + 108 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) 109 + if (loopback_dev(vindev, i)) 110 + return 1; 111 + 112 + return 0; 113 + } 114 + 115 + /* 116 + * Set the output frame buffer padding of all outputs connected with the given 117 + * input when the video input is set to loopback mode. The paddings must be 118 + * the same for the loopback to work properly. 119 + */ 120 + static void set_loopback_padding(struct mgb4_vin_dev *vindev, u32 padding) 121 + { 122 + struct mgb4_regs *video = &vindev->mgbdev->video; 123 + struct mgb4_vout_dev *voutdev; 124 + int i; 125 + 126 + for (i = 0; i < MGB4_VOUT_DEVICES; i++) { 127 + voutdev = loopback_dev(vindev, i); 128 + if (voutdev) 129 + mgb4_write_reg(video, voutdev->config->regs.padding, 130 + padding); 131 + } 132 + } 133 + 134 + static int get_timings(struct mgb4_vin_dev *vindev, 135 + struct v4l2_dv_timings *timings) 136 + { 137 + struct mgb4_regs *video = &vindev->mgbdev->video; 138 + const struct mgb4_vin_regs *regs = &vindev->config->regs; 139 + 140 + u32 status = mgb4_read_reg(video, regs->status); 141 + u32 pclk = mgb4_read_reg(video, regs->pclk); 142 + u32 signal = mgb4_read_reg(video, regs->signal); 143 + u32 signal2 = mgb4_read_reg(video, regs->signal2); 144 + u32 resolution = mgb4_read_reg(video, regs->resolution); 145 + 146 + if (!(status & (1U << 2))) 147 + return -ENOLCK; 148 + if (!(status & (3 << 9))) 149 + return -ENOLINK; 150 + 151 + memset(timings, 0, sizeof(*timings)); 152 + timings->type = V4L2_DV_BT_656_1120; 153 + timings->bt.width = resolution >> 16; 154 + timings->bt.height = resolution & 0xFFFF; 155 + if (status & (1U << 12)) 156 + timings->bt.polarities |= V4L2_DV_HSYNC_POS_POL; 157 + if (status & (1U << 13)) 158 + timings->bt.polarities |= V4L2_DV_VSYNC_POS_POL; 159 + timings->bt.pixelclock = pclk * 1000; 160 + timings->bt.hsync = (signal & 0x00FF0000) >> 16; 161 + timings->bt.vsync = (signal2 & 0x00FF0000) >> 16; 162 + timings->bt.hbackporch = (signal & 0x0000FF00) >> 8; 163 + timings->bt.hfrontporch = signal & 0x000000FF; 164 + timings->bt.vbackporch = (signal2 & 0x0000FF00) >> 8; 165 + timings->bt.vfrontporch = signal2 & 0x000000FF; 166 + 167 + return 0; 168 + } 169 + 170 + static void return_all_buffers(struct mgb4_vin_dev *vindev, 171 + enum vb2_buffer_state state) 172 + { 173 + struct mgb4_frame_buffer *buf, *node; 174 + unsigned long flags; 175 + 176 + spin_lock_irqsave(&vindev->qlock, flags); 177 + list_for_each_entry_safe(buf, node, &vindev->buf_list, list) { 178 + vb2_buffer_done(&buf->vb.vb2_buf, state); 179 + list_del(&buf->list); 180 + } 181 + spin_unlock_irqrestore(&vindev->qlock, flags); 182 + } 183 + 184 + static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, 185 + unsigned int *nplanes, unsigned int sizes[], 186 + struct device *alloc_devs[]) 187 + { 188 + struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q); 189 + unsigned int size = (vindev->timings.bt.width + vindev->padding) 190 + * vindev->timings.bt.height * 4; 191 + 192 + /* 193 + * If I/O reconfiguration is in process, do not allow to start 194 + * the queue. See video_source_store() in mgb4_sysfs_out.c for 195 + * details. 196 + */ 197 + if (test_bit(0, &vindev->mgbdev->io_reconfig)) 198 + return -EBUSY; 199 + 200 + if (!size) 201 + return -EINVAL; 202 + if (*nplanes) 203 + return sizes[0] < size ? -EINVAL : 0; 204 + *nplanes = 1; 205 + sizes[0] = size; 206 + 207 + return 0; 208 + } 209 + 210 + static int buffer_init(struct vb2_buffer *vb) 211 + { 212 + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 213 + struct mgb4_frame_buffer *buf = to_frame_buffer(vbuf); 214 + 215 + INIT_LIST_HEAD(&buf->list); 216 + 217 + return 0; 218 + } 219 + 220 + static int buffer_prepare(struct vb2_buffer *vb) 221 + { 222 + struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vb->vb2_queue); 223 + struct device *dev = &vindev->mgbdev->pdev->dev; 224 + unsigned int size = (vindev->timings.bt.width + vindev->padding) 225 + * vindev->timings.bt.height * 4; 226 + 227 + if (vb2_plane_size(vb, 0) < size) { 228 + dev_err(dev, "buffer too small (%lu < %u)\n", 229 + vb2_plane_size(vb, 0), size); 230 + return -EINVAL; 231 + } 232 + 233 + vb2_set_plane_payload(vb, 0, size); 234 + 235 + return 0; 236 + } 237 + 238 + static void buffer_queue(struct vb2_buffer *vb) 239 + { 240 + struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vb->vb2_queue); 241 + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 242 + struct mgb4_frame_buffer *buf = to_frame_buffer(vbuf); 243 + unsigned long flags; 244 + 245 + spin_lock_irqsave(&vindev->qlock, flags); 246 + list_add_tail(&buf->list, &vindev->buf_list); 247 + spin_unlock_irqrestore(&vindev->qlock, flags); 248 + } 249 + 250 + static void stop_streaming(struct vb2_queue *vq) 251 + { 252 + struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vq); 253 + const struct mgb4_vin_config *config = vindev->config; 254 + int irq = xdma_get_user_irq(vindev->mgbdev->xdev, config->vin_irq); 255 + 256 + xdma_disable_user_irq(vindev->mgbdev->xdev, irq); 257 + 258 + /* 259 + * In loopback mode, the HW frame queue must be left running for 260 + * the IN->OUT transmission to work! 261 + */ 262 + if (!loopback_active(vindev)) 263 + mgb4_mask_reg(&vindev->mgbdev->video, config->regs.config, 0x2, 264 + 0x0); 265 + 266 + cancel_work_sync(&vindev->dma_work); 267 + return_all_buffers(vindev, VB2_BUF_STATE_ERROR); 268 + } 269 + 270 + static int start_streaming(struct vb2_queue *vq, unsigned int count) 271 + { 272 + struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vq); 273 + const struct mgb4_vin_config *config = vindev->config; 274 + int irq = xdma_get_user_irq(vindev->mgbdev->xdev, config->vin_irq); 275 + 276 + vindev->sequence = 0; 277 + 278 + /* 279 + * In loopback mode, the HW frame queue is already running. 280 + */ 281 + if (!loopback_active(vindev)) 282 + mgb4_mask_reg(&vindev->mgbdev->video, config->regs.config, 0x2, 283 + 0x2); 284 + 285 + xdma_enable_user_irq(vindev->mgbdev->xdev, irq); 286 + 287 + return 0; 288 + } 289 + 290 + static const struct vb2_ops queue_ops = { 291 + .queue_setup = queue_setup, 292 + .buf_init = buffer_init, 293 + .buf_prepare = buffer_prepare, 294 + .buf_queue = buffer_queue, 295 + .start_streaming = start_streaming, 296 + .stop_streaming = stop_streaming, 297 + .wait_prepare = vb2_ops_wait_prepare, 298 + .wait_finish = vb2_ops_wait_finish 299 + }; 300 + 301 + static int fh_open(struct file *file) 302 + { 303 + struct mgb4_vin_dev *vindev = video_drvdata(file); 304 + int rv; 305 + 306 + mutex_lock(&vindev->lock); 307 + 308 + rv = v4l2_fh_open(file); 309 + if (rv) 310 + goto out; 311 + 312 + if (!v4l2_fh_is_singular_file(file)) 313 + goto out; 314 + 315 + get_timings(vindev, &vindev->timings); 316 + set_loopback_padding(vindev, vindev->padding); 317 + 318 + out: 319 + mutex_unlock(&vindev->lock); 320 + return rv; 321 + } 322 + 323 + static int fh_release(struct file *file) 324 + { 325 + struct mgb4_vin_dev *vindev = video_drvdata(file); 326 + int rv; 327 + 328 + mutex_lock(&vindev->lock); 329 + 330 + if (v4l2_fh_is_singular_file(file)) 331 + set_loopback_padding(vindev, 0); 332 + 333 + rv = _vb2_fop_release(file, NULL); 334 + 335 + mutex_unlock(&vindev->lock); 336 + 337 + return rv; 338 + } 339 + 340 + static const struct v4l2_file_operations video_fops = { 341 + .owner = THIS_MODULE, 342 + .open = fh_open, 343 + .release = fh_release, 344 + .unlocked_ioctl = video_ioctl2, 345 + .read = vb2_fop_read, 346 + .mmap = vb2_fop_mmap, 347 + .poll = vb2_fop_poll, 348 + }; 349 + 350 + static int vidioc_querycap(struct file *file, void *priv, 351 + struct v4l2_capability *cap) 352 + { 353 + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); 354 + strscpy(cap->card, "MGB4 PCIe Card", sizeof(cap->card)); 355 + 356 + return 0; 357 + } 358 + 359 + static int vidioc_enum_fmt(struct file *file, void *priv, 360 + struct v4l2_fmtdesc *f) 361 + { 362 + if (f->index != 0) 363 + return -EINVAL; 364 + 365 + f->pixelformat = V4L2_PIX_FMT_ABGR32; 366 + 367 + return 0; 368 + } 369 + 370 + static int vidioc_enum_frameintervals(struct file *file, void *priv, 371 + struct v4l2_frmivalenum *ival) 372 + { 373 + struct mgb4_vin_dev *vindev = video_drvdata(file); 374 + 375 + if (ival->index != 0) 376 + return -EINVAL; 377 + if (ival->pixel_format != V4L2_PIX_FMT_ABGR32) 378 + return -EINVAL; 379 + if (ival->width != vindev->timings.bt.width || 380 + ival->height != vindev->timings.bt.height) 381 + return -EINVAL; 382 + 383 + ival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; 384 + ival->stepwise.min.denominator = 60; 385 + ival->stepwise.min.numerator = 1; 386 + ival->stepwise.max.denominator = 1; 387 + ival->stepwise.max.numerator = 1; 388 + ival->stepwise.step = ival->stepwise.max; 389 + 390 + return 0; 391 + } 392 + 393 + static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) 394 + { 395 + struct mgb4_vin_dev *vindev = video_drvdata(file); 396 + 397 + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; 398 + f->fmt.pix.width = vindev->timings.bt.width; 399 + f->fmt.pix.height = vindev->timings.bt.height; 400 + f->fmt.pix.field = V4L2_FIELD_NONE; 401 + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; 402 + f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 4; 403 + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 404 + 405 + return 0; 406 + } 407 + 408 + static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) 409 + { 410 + struct mgb4_vin_dev *vindev = video_drvdata(file); 411 + 412 + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; 413 + f->fmt.pix.width = vindev->timings.bt.width; 414 + f->fmt.pix.height = vindev->timings.bt.height; 415 + f->fmt.pix.field = V4L2_FIELD_NONE; 416 + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; 417 + f->fmt.pix.bytesperline = max(f->fmt.pix.width * 4, 418 + ALIGN_DOWN(f->fmt.pix.bytesperline, 4)); 419 + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 420 + 421 + return 0; 422 + } 423 + 424 + static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) 425 + { 426 + struct mgb4_vin_dev *vindev = video_drvdata(file); 427 + struct mgb4_regs *video = &vindev->mgbdev->video; 428 + 429 + if (vb2_is_busy(&vindev->queue)) 430 + return -EBUSY; 431 + 432 + vidioc_try_fmt(file, priv, f); 433 + 434 + vindev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width * 4)) / 4; 435 + mgb4_write_reg(video, vindev->config->regs.padding, vindev->padding); 436 + set_loopback_padding(vindev, vindev->padding); 437 + 438 + return 0; 439 + } 440 + 441 + static int vidioc_enum_input(struct file *file, void *priv, 442 + struct v4l2_input *i) 443 + { 444 + struct mgb4_vin_dev *vindev = video_drvdata(file); 445 + struct mgb4_regs *video = &vindev->mgbdev->video; 446 + u32 status; 447 + 448 + if (i->index != 0) 449 + return -EINVAL; 450 + 451 + strscpy(i->name, "MGB4", sizeof(i->name)); 452 + i->type = V4L2_INPUT_TYPE_CAMERA; 453 + i->capabilities = V4L2_IN_CAP_DV_TIMINGS; 454 + i->status = 0; 455 + 456 + status = mgb4_read_reg(video, vindev->config->regs.status); 457 + if (!(status & (1U << 2))) 458 + i->status |= V4L2_IN_ST_NO_SYNC; 459 + if (!(status & (3 << 9))) 460 + i->status |= V4L2_IN_ST_NO_SIGNAL; 461 + 462 + return 0; 463 + } 464 + 465 + static int vidioc_enum_framesizes(struct file *file, void *fh, 466 + struct v4l2_frmsizeenum *fsize) 467 + { 468 + struct mgb4_vin_dev *vindev = video_drvdata(file); 469 + 470 + if (fsize->index != 0 || fsize->pixel_format != V4L2_PIX_FMT_ABGR32) 471 + return -EINVAL; 472 + 473 + fsize->discrete.width = vindev->timings.bt.width; 474 + fsize->discrete.height = vindev->timings.bt.height; 475 + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 476 + 477 + return 0; 478 + } 479 + 480 + static int vidioc_s_input(struct file *file, void *priv, unsigned int i) 481 + { 482 + return (i == 0) ? 0 : -EINVAL; 483 + } 484 + 485 + static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) 486 + { 487 + *i = 0; 488 + return 0; 489 + } 490 + 491 + static int vidioc_parm(struct file *file, void *priv, 492 + struct v4l2_streamparm *parm) 493 + { 494 + struct mgb4_vin_dev *vindev = video_drvdata(file); 495 + struct mgb4_regs *video = &vindev->mgbdev->video; 496 + const struct mgb4_vin_regs *regs = &vindev->config->regs; 497 + struct v4l2_fract timeperframe = { 498 + .numerator = mgb4_read_reg(video, regs->frame_period), 499 + .denominator = 125000000, 500 + }; 501 + 502 + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 503 + return -EINVAL; 504 + 505 + parm->parm.capture.readbuffers = 2; 506 + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 507 + parm->parm.capture.timeperframe = timeperframe; 508 + 509 + return 0; 510 + } 511 + 512 + static int vidioc_s_dv_timings(struct file *file, void *fh, 513 + struct v4l2_dv_timings *timings) 514 + { 515 + struct mgb4_vin_dev *vindev = video_drvdata(file); 516 + 517 + if (timings->bt.width < video_timings_cap.bt.min_width || 518 + timings->bt.width > video_timings_cap.bt.max_width || 519 + timings->bt.height < video_timings_cap.bt.min_height || 520 + timings->bt.height > video_timings_cap.bt.max_height) 521 + return -EINVAL; 522 + if (timings->bt.width == vindev->timings.bt.width && 523 + timings->bt.height == vindev->timings.bt.height) 524 + return 0; 525 + if (vb2_is_busy(&vindev->queue)) 526 + return -EBUSY; 527 + 528 + vindev->timings = *timings; 529 + 530 + return 0; 531 + } 532 + 533 + static int vidioc_g_dv_timings(struct file *file, void *fh, 534 + struct v4l2_dv_timings *timings) 535 + { 536 + struct mgb4_vin_dev *vindev = video_drvdata(file); 537 + *timings = vindev->timings; 538 + 539 + return 0; 540 + } 541 + 542 + static int vidioc_query_dv_timings(struct file *file, void *fh, 543 + struct v4l2_dv_timings *timings) 544 + { 545 + struct mgb4_vin_dev *vindev = video_drvdata(file); 546 + 547 + return get_timings(vindev, timings); 548 + } 549 + 550 + static int vidioc_enum_dv_timings(struct file *file, void *fh, 551 + struct v4l2_enum_dv_timings *timings) 552 + { 553 + return v4l2_enum_dv_timings_cap(timings, &video_timings_cap, NULL, NULL); 554 + } 555 + 556 + static int vidioc_dv_timings_cap(struct file *file, void *fh, 557 + struct v4l2_dv_timings_cap *cap) 558 + { 559 + *cap = video_timings_cap; 560 + 561 + return 0; 562 + } 563 + 564 + static int vidioc_subscribe_event(struct v4l2_fh *fh, 565 + const struct v4l2_event_subscription *sub) 566 + { 567 + switch (sub->type) { 568 + case V4L2_EVENT_SOURCE_CHANGE: 569 + return v4l2_src_change_event_subscribe(fh, sub); 570 + } 571 + 572 + return v4l2_ctrl_subscribe_event(fh, sub); 573 + } 574 + 575 + static const struct v4l2_ioctl_ops video_ioctl_ops = { 576 + .vidioc_querycap = vidioc_querycap, 577 + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, 578 + .vidioc_try_fmt_vid_cap = vidioc_try_fmt, 579 + .vidioc_s_fmt_vid_cap = vidioc_s_fmt, 580 + .vidioc_g_fmt_vid_cap = vidioc_g_fmt, 581 + .vidioc_enum_framesizes = vidioc_enum_framesizes, 582 + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, 583 + .vidioc_enum_input = vidioc_enum_input, 584 + .vidioc_g_input = vidioc_g_input, 585 + .vidioc_s_input = vidioc_s_input, 586 + .vidioc_reqbufs = vb2_ioctl_reqbufs, 587 + .vidioc_create_bufs = vb2_ioctl_create_bufs, 588 + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 589 + .vidioc_querybuf = vb2_ioctl_querybuf, 590 + .vidioc_qbuf = vb2_ioctl_qbuf, 591 + .vidioc_dqbuf = vb2_ioctl_dqbuf, 592 + .vidioc_expbuf = vb2_ioctl_expbuf, 593 + .vidioc_streamon = vb2_ioctl_streamon, 594 + .vidioc_streamoff = vb2_ioctl_streamoff, 595 + .vidioc_g_parm = vidioc_parm, 596 + .vidioc_s_parm = vidioc_parm, 597 + .vidioc_dv_timings_cap = vidioc_dv_timings_cap, 598 + .vidioc_enum_dv_timings = vidioc_enum_dv_timings, 599 + .vidioc_g_dv_timings = vidioc_g_dv_timings, 600 + .vidioc_s_dv_timings = vidioc_s_dv_timings, 601 + .vidioc_query_dv_timings = vidioc_query_dv_timings, 602 + .vidioc_subscribe_event = vidioc_subscribe_event, 603 + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 604 + }; 605 + 606 + static void dma_transfer(struct work_struct *work) 607 + { 608 + struct mgb4_vin_dev *vindev = container_of(work, struct mgb4_vin_dev, 609 + dma_work); 610 + struct mgb4_regs *video = &vindev->mgbdev->video; 611 + struct device *dev = &vindev->mgbdev->pdev->dev; 612 + struct mgb4_frame_buffer *buf = NULL; 613 + unsigned long flags; 614 + u32 addr; 615 + int rv; 616 + 617 + spin_lock_irqsave(&vindev->qlock, flags); 618 + if (!list_empty(&vindev->buf_list)) { 619 + buf = list_first_entry(&vindev->buf_list, 620 + struct mgb4_frame_buffer, list); 621 + list_del_init(vindev->buf_list.next); 622 + } 623 + spin_unlock_irqrestore(&vindev->qlock, flags); 624 + 625 + if (!buf) 626 + return; 627 + 628 + addr = mgb4_read_reg(video, vindev->config->regs.address); 629 + if (addr >= MGB4_ERR_QUEUE_FULL) { 630 + dev_dbg(dev, "frame queue error (%d)\n", (int)addr); 631 + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 632 + return; 633 + } 634 + 635 + rv = mgb4_dma_transfer(vindev->mgbdev, vindev->config->dma_channel, 636 + false, addr, 637 + vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0)); 638 + if (rv < 0) { 639 + dev_warn(dev, "DMA transfer error\n"); 640 + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 641 + } else { 642 + buf->vb.vb2_buf.timestamp = ktime_get_ns(); 643 + buf->vb.sequence = vindev->sequence++; 644 + buf->vb.field = V4L2_FIELD_NONE; 645 + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 646 + } 647 + } 648 + 649 + static void signal_change(struct work_struct *work) 650 + { 651 + struct mgb4_vin_dev *vindev = container_of(work, struct mgb4_vin_dev, 652 + err_work); 653 + struct mgb4_regs *video = &vindev->mgbdev->video; 654 + struct v4l2_bt_timings *timings = &vindev->timings.bt; 655 + struct device *dev = &vindev->mgbdev->pdev->dev; 656 + 657 + u32 resolution = mgb4_read_reg(video, vindev->config->regs.resolution); 658 + u32 width = resolution >> 16; 659 + u32 height = resolution & 0xFFFF; 660 + 661 + if (timings->width != width || timings->height != height) { 662 + static const struct v4l2_event ev = { 663 + .type = V4L2_EVENT_SOURCE_CHANGE, 664 + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, 665 + }; 666 + 667 + v4l2_event_queue(&vindev->vdev, &ev); 668 + 669 + if (vb2_is_streaming(&vindev->queue)) 670 + vb2_queue_error(&vindev->queue); 671 + } 672 + 673 + dev_dbg(dev, "stream changed to %ux%u\n", width, height); 674 + } 675 + 676 + static irqreturn_t vin_handler(int irq, void *ctx) 677 + { 678 + struct mgb4_vin_dev *vindev = (struct mgb4_vin_dev *)ctx; 679 + struct mgb4_regs *video = &vindev->mgbdev->video; 680 + 681 + schedule_work(&vindev->dma_work); 682 + 683 + mgb4_write_reg(video, 0xB4, 1U << vindev->config->vin_irq); 684 + 685 + return IRQ_HANDLED; 686 + } 687 + 688 + static irqreturn_t err_handler(int irq, void *ctx) 689 + { 690 + struct mgb4_vin_dev *vindev = (struct mgb4_vin_dev *)ctx; 691 + struct mgb4_regs *video = &vindev->mgbdev->video; 692 + 693 + schedule_work(&vindev->err_work); 694 + 695 + mgb4_write_reg(video, 0xB4, 1U << vindev->config->err_irq); 696 + 697 + return IRQ_HANDLED; 698 + } 699 + 700 + static int deser_init(struct mgb4_vin_dev *vindev, int id) 701 + { 702 + int rv, addr_size; 703 + size_t values_count; 704 + const struct mgb4_i2c_kv *values; 705 + const struct i2c_board_info *info; 706 + struct device *dev = &vindev->mgbdev->pdev->dev; 707 + 708 + if (MGB4_IS_GMSL(vindev->mgbdev)) { 709 + info = &gmsl_deser_info[id]; 710 + addr_size = 16; 711 + values = gmsl_i2c; 712 + values_count = ARRAY_SIZE(gmsl_i2c); 713 + } else { 714 + info = &fpdl3_deser_info[id]; 715 + addr_size = 8; 716 + values = fpdl3_i2c; 717 + values_count = ARRAY_SIZE(fpdl3_i2c); 718 + } 719 + 720 + rv = mgb4_i2c_init(&vindev->deser, vindev->mgbdev->i2c_adap, info, 721 + addr_size); 722 + if (rv < 0) { 723 + dev_err(dev, "failed to create deserializer\n"); 724 + return rv; 725 + } 726 + rv = mgb4_i2c_configure(&vindev->deser, values, values_count); 727 + if (rv < 0) { 728 + dev_err(dev, "failed to configure deserializer\n"); 729 + goto err_i2c_dev; 730 + } 731 + 732 + return 0; 733 + 734 + err_i2c_dev: 735 + mgb4_i2c_free(&vindev->deser); 736 + 737 + return rv; 738 + } 739 + 740 + static void fpga_init(struct mgb4_vin_dev *vindev) 741 + { 742 + struct mgb4_regs *video = &vindev->mgbdev->video; 743 + const struct mgb4_vin_regs *regs = &vindev->config->regs; 744 + 745 + mgb4_write_reg(video, regs->config, 0x00000001); 746 + mgb4_write_reg(video, regs->sync, 0x03E80002); 747 + mgb4_write_reg(video, regs->padding, 0x00000000); 748 + mgb4_write_reg(video, regs->config, 1U << 9); 749 + } 750 + 751 + #ifdef CONFIG_DEBUG_FS 752 + static void debugfs_init(struct mgb4_vin_dev *vindev) 753 + { 754 + struct mgb4_regs *video = &vindev->mgbdev->video; 755 + 756 + vindev->debugfs = debugfs_create_dir(vindev->vdev.name, 757 + vindev->mgbdev->debugfs); 758 + if (!vindev->debugfs) 759 + return; 760 + 761 + vindev->regs[0].name = "CONFIG"; 762 + vindev->regs[0].offset = vindev->config->regs.config; 763 + vindev->regs[1].name = "STATUS"; 764 + vindev->regs[1].offset = vindev->config->regs.status; 765 + vindev->regs[2].name = "RESOLUTION"; 766 + vindev->regs[2].offset = vindev->config->regs.resolution; 767 + vindev->regs[3].name = "FRAME_PERIOD"; 768 + vindev->regs[3].offset = vindev->config->regs.frame_period; 769 + vindev->regs[4].name = "HS_VS_GENER_SETTINGS"; 770 + vindev->regs[4].offset = vindev->config->regs.sync; 771 + vindev->regs[5].name = "PCLK_FREQUENCY"; 772 + vindev->regs[5].offset = vindev->config->regs.pclk; 773 + vindev->regs[6].name = "VIDEO_PARAMS_1"; 774 + vindev->regs[6].offset = vindev->config->regs.signal; 775 + vindev->regs[7].name = "VIDEO_PARAMS_2"; 776 + vindev->regs[7].offset = vindev->config->regs.signal2; 777 + vindev->regs[8].name = "PADDING_PIXELS"; 778 + vindev->regs[8].offset = vindev->config->regs.padding; 779 + 780 + vindev->regset.base = video->membase; 781 + vindev->regset.regs = vindev->regs; 782 + vindev->regset.nregs = ARRAY_SIZE(vindev->regs); 783 + 784 + debugfs_create_regset32("registers", 0444, vindev->debugfs, 785 + &vindev->regset); 786 + } 787 + #endif 788 + 789 + struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id) 790 + { 791 + int rv; 792 + const struct attribute_group **groups; 793 + struct mgb4_vin_dev *vindev; 794 + struct pci_dev *pdev = mgbdev->pdev; 795 + struct device *dev = &pdev->dev; 796 + int vin_irq, err_irq; 797 + 798 + vindev = kzalloc(sizeof(*vindev), GFP_KERNEL); 799 + if (!vindev) 800 + return NULL; 801 + 802 + vindev->mgbdev = mgbdev; 803 + vindev->config = &vin_cfg[id]; 804 + 805 + /* Frame queue*/ 806 + INIT_LIST_HEAD(&vindev->buf_list); 807 + spin_lock_init(&vindev->qlock); 808 + 809 + /* Work queues */ 810 + INIT_WORK(&vindev->dma_work, dma_transfer); 811 + INIT_WORK(&vindev->err_work, signal_change); 812 + 813 + /* IRQ callback */ 814 + vin_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->vin_irq); 815 + rv = request_irq(vin_irq, vin_handler, 0, "mgb4-vin", vindev); 816 + if (rv) { 817 + dev_err(dev, "failed to register vin irq handler\n"); 818 + goto err_alloc; 819 + } 820 + /* Error IRQ callback */ 821 + err_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->err_irq); 822 + rv = request_irq(err_irq, err_handler, 0, "mgb4-err", vindev); 823 + if (rv) { 824 + dev_err(dev, "failed to register err irq handler\n"); 825 + goto err_vin_irq; 826 + } 827 + 828 + /* Set the FPGA registers default values */ 829 + fpga_init(vindev); 830 + 831 + /* Set the deserializer default values */ 832 + rv = deser_init(vindev, id); 833 + if (rv) 834 + goto err_err_irq; 835 + 836 + /* V4L2 stuff init */ 837 + rv = v4l2_device_register(dev, &vindev->v4l2dev); 838 + if (rv) { 839 + dev_err(dev, "failed to register v4l2 device\n"); 840 + goto err_err_irq; 841 + } 842 + 843 + mutex_init(&vindev->lock); 844 + 845 + vindev->queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 846 + vindev->queue.io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; 847 + vindev->queue.buf_struct_size = sizeof(struct mgb4_frame_buffer); 848 + vindev->queue.ops = &queue_ops; 849 + vindev->queue.mem_ops = &vb2_dma_sg_memops; 850 + vindev->queue.gfp_flags = GFP_DMA32; 851 + vindev->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 852 + vindev->queue.min_buffers_needed = 2; 853 + vindev->queue.drv_priv = vindev; 854 + vindev->queue.lock = &vindev->lock; 855 + vindev->queue.dev = dev; 856 + rv = vb2_queue_init(&vindev->queue); 857 + if (rv) { 858 + dev_err(dev, "failed to initialize vb2 queue\n"); 859 + goto err_v4l2_dev; 860 + } 861 + 862 + snprintf(vindev->vdev.name, sizeof(vindev->vdev.name), "mgb4-in%d", 863 + id + 1); 864 + vindev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE 865 + | V4L2_CAP_STREAMING; 866 + vindev->vdev.fops = &video_fops; 867 + vindev->vdev.ioctl_ops = &video_ioctl_ops; 868 + vindev->vdev.release = video_device_release_empty; 869 + vindev->vdev.v4l2_dev = &vindev->v4l2dev; 870 + vindev->vdev.lock = &vindev->lock; 871 + vindev->vdev.queue = &vindev->queue; 872 + video_set_drvdata(&vindev->vdev, vindev); 873 + 874 + /* Enable the video signal change watcher */ 875 + xdma_enable_user_irq(vindev->mgbdev->xdev, err_irq); 876 + 877 + /* Register the video device */ 878 + rv = video_register_device(&vindev->vdev, VFL_TYPE_VIDEO, -1); 879 + if (rv) { 880 + dev_err(dev, "failed to register video device\n"); 881 + goto err_v4l2_dev; 882 + } 883 + 884 + /* Module sysfs attributes */ 885 + groups = MGB4_IS_GMSL(mgbdev) 886 + ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups; 887 + rv = device_add_groups(&vindev->vdev.dev, groups); 888 + if (rv) { 889 + dev_err(dev, "failed to create sysfs attributes\n"); 890 + goto err_video_dev; 891 + } 892 + 893 + #ifdef CONFIG_DEBUG_FS 894 + debugfs_init(vindev); 895 + #endif 896 + 897 + return vindev; 898 + 899 + err_video_dev: 900 + video_unregister_device(&vindev->vdev); 901 + err_v4l2_dev: 902 + v4l2_device_unregister(&vindev->v4l2dev); 903 + err_err_irq: 904 + free_irq(err_irq, vindev); 905 + err_vin_irq: 906 + free_irq(vin_irq, vindev); 907 + err_alloc: 908 + kfree(vindev); 909 + 910 + return NULL; 911 + } 912 + 913 + void mgb4_vin_free(struct mgb4_vin_dev *vindev) 914 + { 915 + const struct attribute_group **groups; 916 + int vin_irq = xdma_get_user_irq(vindev->mgbdev->xdev, 917 + vindev->config->vin_irq); 918 + int err_irq = xdma_get_user_irq(vindev->mgbdev->xdev, 919 + vindev->config->err_irq); 920 + 921 + xdma_disable_user_irq(vindev->mgbdev->xdev, err_irq); 922 + 923 + free_irq(vin_irq, vindev); 924 + free_irq(err_irq, vindev); 925 + 926 + #ifdef CONFIG_DEBUG_FS 927 + debugfs_remove_recursive(vindev->debugfs); 928 + #endif 929 + 930 + groups = MGB4_IS_GMSL(vindev->mgbdev) 931 + ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups; 932 + device_remove_groups(&vindev->vdev.dev, groups); 933 + 934 + mgb4_i2c_free(&vindev->deser); 935 + video_unregister_device(&vindev->vdev); 936 + v4l2_device_unregister(&vindev->v4l2dev); 937 + 938 + kfree(vindev); 939 + }
+69
drivers/media/pci/mgb4/mgb4_vin.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_VIN_H__ 8 + #define __MGB4_VIN_H__ 9 + 10 + #include <media/v4l2-device.h> 11 + #include <media/v4l2-dev.h> 12 + #include <media/v4l2-ctrls.h> 13 + #include <media/videobuf2-core.h> 14 + #include <linux/debugfs.h> 15 + #include "mgb4_i2c.h" 16 + 17 + struct mgb4_vin_regs { 18 + u32 address; 19 + u32 config; 20 + u32 status; 21 + u32 resolution; 22 + u32 frame_period; 23 + u32 sync; 24 + u32 pclk; 25 + u32 signal; 26 + u32 signal2; 27 + u32 padding; 28 + }; 29 + 30 + struct mgb4_vin_config { 31 + int id; 32 + int dma_channel; 33 + int vin_irq; 34 + int err_irq; 35 + struct mgb4_vin_regs regs; 36 + }; 37 + 38 + struct mgb4_vin_dev { 39 + struct mgb4_dev *mgbdev; 40 + struct v4l2_device v4l2dev; 41 + struct video_device vdev; 42 + struct vb2_queue queue; 43 + struct mutex lock; /* vdev lock */ 44 + 45 + spinlock_t qlock; /* video buffer queue lock */ 46 + struct list_head buf_list; 47 + struct work_struct dma_work, err_work; 48 + 49 + unsigned int sequence; 50 + 51 + struct v4l2_dv_timings timings; 52 + u32 freq_range; 53 + u32 padding; 54 + 55 + struct mgb4_i2c_client deser; 56 + 57 + const struct mgb4_vin_config *config; 58 + 59 + #ifdef CONFIG_DEBUG_FS 60 + struct dentry *debugfs; 61 + struct debugfs_regset32 regset; 62 + struct debugfs_reg32 regs[9]; 63 + #endif 64 + }; 65 + 66 + struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id); 67 + void mgb4_vin_free(struct mgb4_vin_dev *vindev); 68 + 69 + #endif
+602
drivers/media/pci/mgb4/mgb4_vout.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + * 6 + * This is the v4l2 output device module. It initializes the signal serializers 7 + * and creates the v4l2 video devices. 8 + * 9 + * When the device is in loopback mode (a direct, in HW, in->out frame passing 10 + * mode) we disable the v4l2 output by returning EBUSY in the open() syscall. 11 + */ 12 + 13 + #include <linux/pci.h> 14 + #include <linux/align.h> 15 + #include <linux/dma/amd_xdma.h> 16 + #include <media/v4l2-ioctl.h> 17 + #include <media/videobuf2-v4l2.h> 18 + #include <media/videobuf2-dma-sg.h> 19 + #include "mgb4_core.h" 20 + #include "mgb4_dma.h" 21 + #include "mgb4_sysfs.h" 22 + #include "mgb4_io.h" 23 + #include "mgb4_cmt.h" 24 + #include "mgb4_vout.h" 25 + 26 + ATTRIBUTE_GROUPS(mgb4_fpdl3_out); 27 + ATTRIBUTE_GROUPS(mgb4_gmsl_out); 28 + 29 + static const struct mgb4_vout_config vout_cfg[] = { 30 + {0, 0, 8, {0x78, 0x60, 0x64, 0x68, 0x74, 0x6C, 0x70, 0x7c}}, 31 + {1, 1, 9, {0x98, 0x80, 0x84, 0x88, 0x94, 0x8c, 0x90, 0x9c}} 32 + }; 33 + 34 + static const struct i2c_board_info fpdl3_ser_info[] = { 35 + {I2C_BOARD_INFO("serializer1", 0x14)}, 36 + {I2C_BOARD_INFO("serializer2", 0x16)}, 37 + }; 38 + 39 + static const struct mgb4_i2c_kv fpdl3_i2c[] = { 40 + {0x05, 0xFF, 0x04}, {0x06, 0xFF, 0x01}, {0xC2, 0xFF, 0x80} 41 + }; 42 + 43 + static void return_all_buffers(struct mgb4_vout_dev *voutdev, 44 + enum vb2_buffer_state state) 45 + { 46 + struct mgb4_frame_buffer *buf, *node; 47 + unsigned long flags; 48 + 49 + spin_lock_irqsave(&voutdev->qlock, flags); 50 + list_for_each_entry_safe(buf, node, &voutdev->buf_list, list) { 51 + vb2_buffer_done(&buf->vb.vb2_buf, state); 52 + list_del(&buf->list); 53 + } 54 + spin_unlock_irqrestore(&voutdev->qlock, flags); 55 + } 56 + 57 + static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, 58 + unsigned int *nplanes, unsigned int sizes[], 59 + struct device *alloc_devs[]) 60 + { 61 + struct mgb4_vout_dev *voutdev = vb2_get_drv_priv(q); 62 + unsigned int size; 63 + 64 + /* 65 + * If I/O reconfiguration is in process, do not allow to start 66 + * the queue. See video_source_store() in mgb4_sysfs_out.c for 67 + * details. 68 + */ 69 + if (test_bit(0, &voutdev->mgbdev->io_reconfig)) 70 + return -EBUSY; 71 + 72 + size = (voutdev->width + voutdev->padding) * voutdev->height * 4; 73 + 74 + if (*nplanes) 75 + return sizes[0] < size ? -EINVAL : 0; 76 + *nplanes = 1; 77 + sizes[0] = size; 78 + 79 + return 0; 80 + } 81 + 82 + static int buffer_init(struct vb2_buffer *vb) 83 + { 84 + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 85 + struct mgb4_frame_buffer *buf = to_frame_buffer(vbuf); 86 + 87 + INIT_LIST_HEAD(&buf->list); 88 + 89 + return 0; 90 + } 91 + 92 + static int buffer_prepare(struct vb2_buffer *vb) 93 + { 94 + struct mgb4_vout_dev *voutdev = vb2_get_drv_priv(vb->vb2_queue); 95 + struct device *dev = &voutdev->mgbdev->pdev->dev; 96 + unsigned int size; 97 + 98 + size = (voutdev->width + voutdev->padding) * voutdev->height * 4; 99 + 100 + if (vb2_plane_size(vb, 0) < size) { 101 + dev_err(dev, "buffer too small (%lu < %u)\n", 102 + vb2_plane_size(vb, 0), size); 103 + return -EINVAL; 104 + } 105 + 106 + vb2_set_plane_payload(vb, 0, size); 107 + 108 + return 0; 109 + } 110 + 111 + static void buffer_queue(struct vb2_buffer *vb) 112 + { 113 + struct mgb4_vout_dev *vindev = vb2_get_drv_priv(vb->vb2_queue); 114 + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 115 + struct mgb4_frame_buffer *buf = to_frame_buffer(vbuf); 116 + unsigned long flags; 117 + 118 + spin_lock_irqsave(&vindev->qlock, flags); 119 + list_add_tail(&buf->list, &vindev->buf_list); 120 + spin_unlock_irqrestore(&vindev->qlock, flags); 121 + } 122 + 123 + static void stop_streaming(struct vb2_queue *vq) 124 + { 125 + struct mgb4_vout_dev *voutdev = vb2_get_drv_priv(vq); 126 + struct mgb4_dev *mgbdev = voutdev->mgbdev; 127 + int irq = xdma_get_user_irq(mgbdev->xdev, voutdev->config->irq); 128 + 129 + xdma_disable_user_irq(mgbdev->xdev, irq); 130 + cancel_work_sync(&voutdev->dma_work); 131 + mgb4_mask_reg(&mgbdev->video, voutdev->config->regs.config, 0x2, 0x0); 132 + return_all_buffers(voutdev, VB2_BUF_STATE_ERROR); 133 + } 134 + 135 + static int start_streaming(struct vb2_queue *vq, unsigned int count) 136 + { 137 + struct mgb4_vout_dev *voutdev = vb2_get_drv_priv(vq); 138 + struct mgb4_dev *mgbdev = voutdev->mgbdev; 139 + struct device *dev = &mgbdev->pdev->dev; 140 + struct mgb4_frame_buffer *buf; 141 + struct mgb4_regs *video = &mgbdev->video; 142 + const struct mgb4_vout_config *config = voutdev->config; 143 + int irq = xdma_get_user_irq(mgbdev->xdev, config->irq); 144 + int rv; 145 + u32 addr; 146 + 147 + mgb4_mask_reg(video, config->regs.config, 0x2, 0x2); 148 + 149 + addr = mgb4_read_reg(video, config->regs.address); 150 + if (addr >= MGB4_ERR_QUEUE_FULL) { 151 + dev_dbg(dev, "frame queue error (%d)\n", (int)addr); 152 + return_all_buffers(voutdev, VB2_BUF_STATE_QUEUED); 153 + return -EBUSY; 154 + } 155 + 156 + buf = list_first_entry(&voutdev->buf_list, struct mgb4_frame_buffer, 157 + list); 158 + list_del_init(voutdev->buf_list.next); 159 + 160 + rv = mgb4_dma_transfer(mgbdev, config->dma_channel, true, addr, 161 + vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0)); 162 + if (rv < 0) { 163 + dev_warn(dev, "DMA transfer error\n"); 164 + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 165 + } else { 166 + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 167 + } 168 + 169 + xdma_enable_user_irq(mgbdev->xdev, irq); 170 + 171 + return 0; 172 + } 173 + 174 + static const struct vb2_ops queue_ops = { 175 + .queue_setup = queue_setup, 176 + .buf_init = buffer_init, 177 + .buf_prepare = buffer_prepare, 178 + .buf_queue = buffer_queue, 179 + .start_streaming = start_streaming, 180 + .stop_streaming = stop_streaming, 181 + .wait_prepare = vb2_ops_wait_prepare, 182 + .wait_finish = vb2_ops_wait_finish 183 + }; 184 + 185 + static int vidioc_querycap(struct file *file, void *priv, 186 + struct v4l2_capability *cap) 187 + { 188 + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); 189 + strscpy(cap->card, "MGB4 PCIe Card", sizeof(cap->card)); 190 + 191 + return 0; 192 + } 193 + 194 + static int vidioc_enum_fmt(struct file *file, void *priv, 195 + struct v4l2_fmtdesc *f) 196 + { 197 + if (f->index != 0) 198 + return -EINVAL; 199 + 200 + f->pixelformat = V4L2_PIX_FMT_ABGR32; 201 + 202 + return 0; 203 + } 204 + 205 + static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) 206 + { 207 + struct mgb4_vout_dev *voutdev = video_drvdata(file); 208 + 209 + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; 210 + f->fmt.pix.width = voutdev->width; 211 + f->fmt.pix.height = voutdev->height; 212 + f->fmt.pix.field = V4L2_FIELD_NONE; 213 + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; 214 + f->fmt.pix.bytesperline = (f->fmt.pix.width + voutdev->padding) * 4; 215 + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 216 + 217 + return 0; 218 + } 219 + 220 + static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) 221 + { 222 + struct mgb4_vout_dev *voutdev = video_drvdata(file); 223 + 224 + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; 225 + f->fmt.pix.width = voutdev->width; 226 + f->fmt.pix.height = voutdev->height; 227 + f->fmt.pix.field = V4L2_FIELD_NONE; 228 + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; 229 + f->fmt.pix.bytesperline = max(f->fmt.pix.width * 4, 230 + ALIGN_DOWN(f->fmt.pix.bytesperline, 4)); 231 + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; 232 + 233 + return 0; 234 + } 235 + 236 + static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) 237 + { 238 + struct mgb4_vout_dev *voutdev = video_drvdata(file); 239 + struct mgb4_regs *video = &voutdev->mgbdev->video; 240 + 241 + if (vb2_is_busy(&voutdev->queue)) 242 + return -EBUSY; 243 + 244 + vidioc_try_fmt(file, priv, f); 245 + 246 + voutdev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width * 4)) / 4; 247 + mgb4_write_reg(video, voutdev->config->regs.padding, voutdev->padding); 248 + 249 + return 0; 250 + } 251 + 252 + static int vidioc_g_output(struct file *file, void *priv, unsigned int *i) 253 + { 254 + *i = 0; 255 + return 0; 256 + } 257 + 258 + static int vidioc_s_output(struct file *file, void *priv, unsigned int i) 259 + { 260 + return i ? -EINVAL : 0; 261 + } 262 + 263 + static int vidioc_enum_output(struct file *file, void *priv, 264 + struct v4l2_output *out) 265 + { 266 + if (out->index != 0) 267 + return -EINVAL; 268 + 269 + out->type = V4L2_OUTPUT_TYPE_ANALOG; 270 + strscpy(out->name, "MGB4", sizeof(out->name)); 271 + 272 + return 0; 273 + } 274 + 275 + static const struct v4l2_ioctl_ops video_ioctl_ops = { 276 + .vidioc_querycap = vidioc_querycap, 277 + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, 278 + .vidioc_try_fmt_vid_out = vidioc_try_fmt, 279 + .vidioc_s_fmt_vid_out = vidioc_s_fmt, 280 + .vidioc_g_fmt_vid_out = vidioc_g_fmt, 281 + .vidioc_enum_output = vidioc_enum_output, 282 + .vidioc_g_output = vidioc_g_output, 283 + .vidioc_s_output = vidioc_s_output, 284 + .vidioc_reqbufs = vb2_ioctl_reqbufs, 285 + .vidioc_create_bufs = vb2_ioctl_create_bufs, 286 + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 287 + .vidioc_querybuf = vb2_ioctl_querybuf, 288 + .vidioc_qbuf = vb2_ioctl_qbuf, 289 + .vidioc_dqbuf = vb2_ioctl_dqbuf, 290 + .vidioc_expbuf = vb2_ioctl_expbuf, 291 + .vidioc_streamon = vb2_ioctl_streamon, 292 + .vidioc_streamoff = vb2_ioctl_streamoff, 293 + }; 294 + 295 + static int fh_open(struct file *file) 296 + { 297 + struct mgb4_vout_dev *voutdev = video_drvdata(file); 298 + struct mgb4_regs *video = &voutdev->mgbdev->video; 299 + struct device *dev = &voutdev->mgbdev->pdev->dev; 300 + u32 config, resolution; 301 + int rv; 302 + 303 + /* Return EBUSY when the device is in loopback mode */ 304 + config = mgb4_read_reg(video, voutdev->config->regs.config); 305 + if ((config & 0xc) >> 2 != voutdev->config->id + MGB4_VIN_DEVICES) { 306 + dev_dbg(dev, "can not open - device in loopback mode"); 307 + return -EBUSY; 308 + } 309 + 310 + mutex_lock(&voutdev->lock); 311 + 312 + rv = v4l2_fh_open(file); 313 + if (rv) 314 + goto out; 315 + 316 + if (!v4l2_fh_is_singular_file(file)) 317 + goto out; 318 + 319 + resolution = mgb4_read_reg(video, voutdev->config->regs.resolution); 320 + voutdev->width = resolution >> 16; 321 + voutdev->height = resolution & 0xFFFF; 322 + 323 + out: 324 + mutex_unlock(&voutdev->lock); 325 + return rv; 326 + } 327 + 328 + static const struct v4l2_file_operations video_fops = { 329 + .owner = THIS_MODULE, 330 + .open = fh_open, 331 + .release = vb2_fop_release, 332 + .unlocked_ioctl = video_ioctl2, 333 + .write = vb2_fop_write, 334 + .mmap = vb2_fop_mmap, 335 + .poll = vb2_fop_poll, 336 + }; 337 + 338 + static void dma_transfer(struct work_struct *work) 339 + { 340 + struct mgb4_vout_dev *voutdev = container_of(work, struct mgb4_vout_dev, 341 + dma_work); 342 + struct device *dev = &voutdev->mgbdev->pdev->dev; 343 + struct mgb4_regs *video = &voutdev->mgbdev->video; 344 + struct mgb4_frame_buffer *buf = NULL; 345 + unsigned long flags; 346 + u32 addr; 347 + int rv; 348 + 349 + spin_lock_irqsave(&voutdev->qlock, flags); 350 + if (!list_empty(&voutdev->buf_list)) { 351 + buf = list_first_entry(&voutdev->buf_list, 352 + struct mgb4_frame_buffer, list); 353 + list_del_init(voutdev->buf_list.next); 354 + } 355 + spin_unlock_irqrestore(&voutdev->qlock, flags); 356 + 357 + if (!buf) 358 + return; 359 + 360 + addr = mgb4_read_reg(video, voutdev->config->regs.address); 361 + if (addr >= MGB4_ERR_QUEUE_FULL) { 362 + dev_dbg(dev, "frame queue error (%d)\n", (int)addr); 363 + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 364 + return; 365 + } 366 + 367 + rv = mgb4_dma_transfer(voutdev->mgbdev, voutdev->config->dma_channel, 368 + true, addr, 369 + vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0)); 370 + if (rv < 0) { 371 + dev_warn(dev, "DMA transfer error\n"); 372 + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 373 + } else { 374 + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 375 + } 376 + } 377 + 378 + static irqreturn_t handler(int irq, void *ctx) 379 + { 380 + struct mgb4_vout_dev *voutdev = (struct mgb4_vout_dev *)ctx; 381 + struct mgb4_regs *video = &voutdev->mgbdev->video; 382 + 383 + schedule_work(&voutdev->dma_work); 384 + 385 + mgb4_write_reg(video, 0xB4, 1U << voutdev->config->irq); 386 + 387 + return IRQ_HANDLED; 388 + } 389 + 390 + static int ser_init(struct mgb4_vout_dev *voutdev, int id) 391 + { 392 + int rv; 393 + const struct i2c_board_info *info = &fpdl3_ser_info[id]; 394 + struct mgb4_i2c_client *ser = &voutdev->ser; 395 + struct device *dev = &voutdev->mgbdev->pdev->dev; 396 + 397 + if (MGB4_IS_GMSL(voutdev->mgbdev)) 398 + return 0; 399 + 400 + rv = mgb4_i2c_init(ser, voutdev->mgbdev->i2c_adap, info, 8); 401 + if (rv < 0) { 402 + dev_err(dev, "failed to create serializer\n"); 403 + return rv; 404 + } 405 + rv = mgb4_i2c_configure(ser, fpdl3_i2c, ARRAY_SIZE(fpdl3_i2c)); 406 + if (rv < 0) { 407 + dev_err(dev, "failed to configure serializer\n"); 408 + goto err_i2c_dev; 409 + } 410 + 411 + return 0; 412 + 413 + err_i2c_dev: 414 + mgb4_i2c_free(ser); 415 + 416 + return rv; 417 + } 418 + 419 + static void fpga_init(struct mgb4_vout_dev *voutdev) 420 + { 421 + struct mgb4_regs *video = &voutdev->mgbdev->video; 422 + const struct mgb4_vout_regs *regs = &voutdev->config->regs; 423 + 424 + mgb4_write_reg(video, regs->config, 0x00000011); 425 + mgb4_write_reg(video, regs->resolution, 426 + (MGB4_DEFAULT_WIDTH << 16) | MGB4_DEFAULT_HEIGHT); 427 + mgb4_write_reg(video, regs->hsync, 0x00102020); 428 + mgb4_write_reg(video, regs->vsync, 0x40020202); 429 + mgb4_write_reg(video, regs->frame_period, MGB4_DEFAULT_PERIOD); 430 + mgb4_write_reg(video, regs->padding, 0x00000000); 431 + 432 + voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 70000 >> 1) << 1; 433 + 434 + mgb4_write_reg(video, regs->config, 435 + (voutdev->config->id + MGB4_VIN_DEVICES) << 2 | 1 << 4); 436 + } 437 + 438 + #ifdef CONFIG_DEBUG_FS 439 + static void debugfs_init(struct mgb4_vout_dev *voutdev) 440 + { 441 + struct mgb4_regs *video = &voutdev->mgbdev->video; 442 + 443 + voutdev->debugfs = debugfs_create_dir(voutdev->vdev.name, 444 + voutdev->mgbdev->debugfs); 445 + if (!voutdev->debugfs) 446 + return; 447 + 448 + voutdev->regs[0].name = "CONFIG"; 449 + voutdev->regs[0].offset = voutdev->config->regs.config; 450 + voutdev->regs[1].name = "STATUS"; 451 + voutdev->regs[1].offset = voutdev->config->regs.status; 452 + voutdev->regs[2].name = "RESOLUTION"; 453 + voutdev->regs[2].offset = voutdev->config->regs.resolution; 454 + voutdev->regs[3].name = "VIDEO_PARAMS_1"; 455 + voutdev->regs[3].offset = voutdev->config->regs.hsync; 456 + voutdev->regs[4].name = "VIDEO_PARAMS_2"; 457 + voutdev->regs[4].offset = voutdev->config->regs.vsync; 458 + voutdev->regs[5].name = "FRAME_PERIOD"; 459 + voutdev->regs[5].offset = voutdev->config->regs.frame_period; 460 + voutdev->regs[6].name = "PADDING"; 461 + voutdev->regs[6].offset = voutdev->config->regs.padding; 462 + 463 + voutdev->regset.base = video->membase; 464 + voutdev->regset.regs = voutdev->regs; 465 + voutdev->regset.nregs = ARRAY_SIZE(voutdev->regs); 466 + 467 + debugfs_create_regset32("registers", 0444, voutdev->debugfs, 468 + &voutdev->regset); 469 + } 470 + #endif 471 + 472 + struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id) 473 + { 474 + int rv, irq; 475 + const struct attribute_group **groups; 476 + struct mgb4_vout_dev *voutdev; 477 + struct pci_dev *pdev = mgbdev->pdev; 478 + struct device *dev = &pdev->dev; 479 + 480 + voutdev = kzalloc(sizeof(*voutdev), GFP_KERNEL); 481 + if (!voutdev) 482 + return NULL; 483 + 484 + voutdev->mgbdev = mgbdev; 485 + voutdev->config = &vout_cfg[id]; 486 + 487 + /* Frame queue */ 488 + INIT_LIST_HEAD(&voutdev->buf_list); 489 + spin_lock_init(&voutdev->qlock); 490 + 491 + /* DMA transfer stuff */ 492 + INIT_WORK(&voutdev->dma_work, dma_transfer); 493 + 494 + /* IRQ callback */ 495 + irq = xdma_get_user_irq(mgbdev->xdev, voutdev->config->irq); 496 + rv = request_irq(irq, handler, 0, "mgb4-vout", voutdev); 497 + if (rv) { 498 + dev_err(dev, "failed to register irq handler\n"); 499 + goto err_alloc; 500 + } 501 + 502 + /* Set the FPGA registers default values */ 503 + fpga_init(voutdev); 504 + 505 + /* Set the serializer default values */ 506 + rv = ser_init(voutdev, id); 507 + if (rv) 508 + goto err_irq; 509 + 510 + /* V4L2 stuff init */ 511 + rv = v4l2_device_register(dev, &voutdev->v4l2dev); 512 + if (rv) { 513 + dev_err(dev, "failed to register v4l2 device\n"); 514 + goto err_irq; 515 + } 516 + 517 + mutex_init(&voutdev->lock); 518 + 519 + voutdev->queue.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 520 + voutdev->queue.io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE; 521 + voutdev->queue.buf_struct_size = sizeof(struct mgb4_frame_buffer); 522 + voutdev->queue.ops = &queue_ops; 523 + voutdev->queue.mem_ops = &vb2_dma_sg_memops; 524 + voutdev->queue.gfp_flags = GFP_DMA32; 525 + voutdev->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 526 + voutdev->queue.min_buffers_needed = 2; 527 + voutdev->queue.drv_priv = voutdev; 528 + voutdev->queue.lock = &voutdev->lock; 529 + voutdev->queue.dev = dev; 530 + rv = vb2_queue_init(&voutdev->queue); 531 + if (rv) { 532 + dev_err(dev, "failed to initialize vb2 queue\n"); 533 + goto err_v4l2_dev; 534 + } 535 + 536 + snprintf(voutdev->vdev.name, sizeof(voutdev->vdev.name), "mgb4-out%d", 537 + id + 1); 538 + voutdev->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE 539 + | V4L2_CAP_STREAMING; 540 + voutdev->vdev.vfl_dir = VFL_DIR_TX; 541 + voutdev->vdev.fops = &video_fops; 542 + voutdev->vdev.ioctl_ops = &video_ioctl_ops; 543 + voutdev->vdev.release = video_device_release_empty; 544 + voutdev->vdev.v4l2_dev = &voutdev->v4l2dev; 545 + voutdev->vdev.lock = &voutdev->lock; 546 + voutdev->vdev.queue = &voutdev->queue; 547 + video_set_drvdata(&voutdev->vdev, voutdev); 548 + 549 + rv = video_register_device(&voutdev->vdev, VFL_TYPE_VIDEO, -1); 550 + if (rv) { 551 + dev_err(dev, "failed to register video device\n"); 552 + goto err_v4l2_dev; 553 + } 554 + 555 + /* Module sysfs attributes */ 556 + groups = MGB4_IS_GMSL(mgbdev) 557 + ? mgb4_gmsl_out_groups : mgb4_fpdl3_out_groups; 558 + rv = device_add_groups(&voutdev->vdev.dev, groups); 559 + if (rv) { 560 + dev_err(dev, "failed to create sysfs attributes\n"); 561 + goto err_video_dev; 562 + } 563 + 564 + #ifdef CONFIG_DEBUG_FS 565 + debugfs_init(voutdev); 566 + #endif 567 + 568 + return voutdev; 569 + 570 + err_video_dev: 571 + video_unregister_device(&voutdev->vdev); 572 + err_v4l2_dev: 573 + v4l2_device_unregister(&voutdev->v4l2dev); 574 + err_irq: 575 + free_irq(irq, voutdev); 576 + err_alloc: 577 + kfree(voutdev); 578 + 579 + return NULL; 580 + } 581 + 582 + void mgb4_vout_free(struct mgb4_vout_dev *voutdev) 583 + { 584 + const struct attribute_group **groups; 585 + int irq = xdma_get_user_irq(voutdev->mgbdev->xdev, voutdev->config->irq); 586 + 587 + free_irq(irq, voutdev); 588 + 589 + #ifdef CONFIG_DEBUG_FS 590 + debugfs_remove_recursive(voutdev->debugfs); 591 + #endif 592 + 593 + groups = MGB4_IS_GMSL(voutdev->mgbdev) 594 + ? mgb4_gmsl_out_groups : mgb4_fpdl3_out_groups; 595 + device_remove_groups(&voutdev->vdev.dev, groups); 596 + 597 + mgb4_i2c_free(&voutdev->ser); 598 + video_unregister_device(&voutdev->vdev); 599 + v4l2_device_unregister(&voutdev->v4l2dev); 600 + 601 + kfree(voutdev); 602 + }
+65
drivers/media/pci/mgb4/mgb4_vout.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2021-2023 Digiteq Automotive 4 + * author: Martin Tuma <martin.tuma@digiteqautomotive.com> 5 + */ 6 + 7 + #ifndef __MGB4_VOUT_H__ 8 + #define __MGB4_VOUT_H__ 9 + 10 + #include <media/v4l2-device.h> 11 + #include <media/v4l2-dev.h> 12 + #include <media/v4l2-ctrls.h> 13 + #include <media/videobuf2-core.h> 14 + #include <linux/debugfs.h> 15 + #include "mgb4_i2c.h" 16 + 17 + struct mgb4_vout_regs { 18 + u32 address; 19 + u32 config; 20 + u32 status; 21 + u32 resolution; 22 + u32 frame_period; 23 + u32 hsync; 24 + u32 vsync; 25 + u32 padding; 26 + }; 27 + 28 + struct mgb4_vout_config { 29 + int id; 30 + int dma_channel; 31 + int irq; 32 + struct mgb4_vout_regs regs; 33 + }; 34 + 35 + struct mgb4_vout_dev { 36 + struct mgb4_dev *mgbdev; 37 + struct v4l2_device v4l2dev; 38 + struct video_device vdev; 39 + struct vb2_queue queue; 40 + struct mutex lock; /* vdev lock */ 41 + 42 + spinlock_t qlock; /* buffer queue lock */ 43 + struct list_head buf_list; 44 + struct work_struct dma_work; 45 + 46 + u32 width; 47 + u32 height; 48 + u32 freq; 49 + u32 padding; 50 + 51 + struct mgb4_i2c_client ser; 52 + 53 + const struct mgb4_vout_config *config; 54 + 55 + #ifdef CONFIG_DEBUG_FS 56 + struct dentry *debugfs; 57 + struct debugfs_regset32 regset; 58 + struct debugfs_reg32 regs[7]; 59 + #endif 60 + }; 61 + 62 + struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id); 63 + void mgb4_vout_free(struct mgb4_vout_dev *voutdev); 64 + 65 + #endif