2018-05-29 18:58:43

by Vishal Sagar

[permalink] [raw]
Subject: [PATCH 0/2] Add support for Xilinx CSI2 Receiver Subsystem

Xilinx MIPI CSI-2 Receiver Subsystem
------------------------------------

The Xilinx MIPI CSI-2 Receiver Subsystem Soft IP consists of a DPHY which
gets the data, an optional I2C, a CSI-2 Receiver which parses the data and
converts it into AXIS data.
This stream output maybe connected to a Xilinx Video Format Bridge.
The maximum number of lanes supported is fixed in the design.
The number of active lanes can be programmed.
For e.g. the design may set maximum lanes as 4 but if the camera sensor has
only 1 lane then the active lanes shall be set as 1.

The pixel format set in design acts as a filter allowing only the selected
data type or RAW8 data packets. The D-PHY register access can be gated in
the design. The base address of the DPHY depends on whether the internal
Xilinx I2C controller is enabled or not in design.

The device driver registers the MIPI CSI2 Rx Subsystem as a V4L2 sub device
having 2 pads. The sink pad is connected to the MIPI camera sensor and
output pad is connected to the video node.
Refer to xlnx,csi2rxss.txt for device tree node details.

This driver helps configure the number of active lanes to be set, setting
and handling interrupts and IP core enable. It logs the number of events
occurring according to their type between streaming ON and OFF.
It generates a v4l2 event for each short packet data received.
The application can then dequeue this event and get the requisite data
from the event structure.

It adds new V4L2 controls which are used to get the event counter values
and reset the subsystem.

The Xilinx CSI-2 Rx Subsystem outputs an AXI4 Stream data which can be
used for image processing. This data follows the video formats mentioned
in Xilinx UG934 when the Video Format Bridge and pixels per clock design
inputs are set. When the VFB is deselected then the video data width will
either be 32 or 64 bits.

Vishal Sagar (2):
media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem
media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

.../bindings/media/xilinx/xlnx,csi2rxss.txt | 117 ++
drivers/media/platform/xilinx/Kconfig | 12 +
drivers/media/platform/xilinx/Makefile | 1 +
drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751 ++++++++++++++++++++
include/uapi/linux/xilinx-csi2rxss.h | 25 +
include/uapi/linux/xilinx-v4l2-controls.h | 14 +
6 files changed, 1920 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
create mode 100644 include/uapi/linux/xilinx-csi2rxss.h

--
2.7.4

This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.


2018-05-29 18:59:25

by Vishal Sagar

[permalink] [raw]
Subject: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem

Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem.

The Xilinx MIPI CSI-2 Rx Subsystem consists of a DPHY, CSI-2 Rx, an
optional I2C controller and an optional Video Format Bridge (VFB). The
active lanes can be configured at run time if enabled in the IP. The
DPHY register interface may also be enabled.

Signed-off-by: Vishal Sagar <[email protected]>
---
.../bindings/media/xilinx/xlnx,csi2rxss.txt | 117 +++++++++++++++++++++
1 file changed, 117 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt

diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
new file mode 100644
index 0000000..31ed721
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
@@ -0,0 +1,117 @@
+
+Xilinx MIPI CSI2 Receiver Subsystem Device Tree Bindings
+--------------------------------------------------------
+
+The Xilinx MIPI CSI2 Receiver Subsystem is used to capture MIPI CSI2 traffic
+from compliant camera sensors and send the output as AXI4 Stream video data
+for image processing.
+
+The subsystem consists of a MIPI DPHY in slave mode which captures the
+data packets. This is passed along the MIPI CSI2 Rx IP which extracts the
+packet data. This data is taken in by the Video Format Bridge (VFB),
+if selected, and converted into AXI4 Stream video data at selected
+pixels per clock as per AXI4-Stream Video IP and System Design UG934.
+
+For more details, please refer to PG232 MIPI CSI-2 Receiver Subsystem.
+https://www.xilinx.com/support/documentation/ip_documentation/mipi_csi2_rx_subsystem/v3_0/pg232-mipi-csi2-rx.pdf
+
+Required properties:
+
+- compatible: Must contain "xlnx,mipi-csi2-rx-subsystem-2.0" or
+ "xlnx,mipi-csi2-rx-subsystem-3.0"
+
+- reg: Physical base address and length of the registers set for the device.
+
+- interrupt-parent: specifies the phandle to the parent interrupt controller
+
+- interrupts: Property with a value describing the interrupt number.
+
+- xlnx,max-lanes: Maximum active lanes in the design.
+
+- xlnx,vc: Virtual Channel, specifies virtual channel number to be filtered.
+ If this is 4 then all virtual channels are allowed.
+
+- xlnx,csi-pxl-format: This denotes the CSI Data type selected in hw design.
+ Packets other than this data type (except for RAW8 and User defined data
+ types) will be filtered out. Possible values are RAW6, RAW7, RAW8, RAW10,
+ RAW12, RAW14, RGB444, RGB555, RGB565, RGB666, RGB888 and YUV4228bit.
+
+- xlnx,axis-tdata-width: AXI Stream width, This denotes the AXI Stream width.
+ It depends on Data type chosen, Video Format Bridge enabled/disabled and
+ pixels per clock. If VFB is disabled then its value is either 0x20 (32 bit)
+ or 0x40(64 bit) width.
+
+- xlnx,video-format, xlnx,video-width: Video format and width, as defined in
+ video.txt.
+
+- port: Video port, using the DT bindings defined in ../video-interfaces.txt.
+ The CSI 2 Rx Subsystem has a two ports, one input port for connecting to
+ camera sensor and other is output port.
+
+- data-lanes: The number of data lanes through which CSI2 Rx Subsystem is
+ connected to the camera sensor as per video-interfaces.txt
+
+Optional properties:
+
+- xlnx,en-active-lanes: Enable Active lanes configuration in Protocol
+ Configuration Register.
+
+- xlnx,dphy-present: This is equivalent to whether DPHY register interface is
+ enabled or not.
+
+- xlnx,iic-present: This shows whether subsystem's IIC is present or not. This
+ affects the base address of the DPHY.
+
+- xlnx,vfb: Video Format Bridge, Denotes if Video Format Bridge is selected
+ so that output is as per AXI stream documented in UG934.
+
+- xlnx,ppc: Pixels per clock, Number of pixels to be transferred per pixel
+ clock. This is valid only if xlnx,vfb property is present.
+
+Example:
+
+ csiss_1: csiss@a0020000 {
+ compatible = "xlnx,mipi-csi2-rx-subsystem-3.0";
+ reg = <0x0 0xa0020000 0x0 0x20000>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 95 4>;
+
+ xlnx,max-lanes = <0x4>;
+ xlnx,en-active-lanes;
+ xlnx,dphy-present;
+ xlnx,iic-present;
+ xlnx,vc = <0x4>;
+ xlnx,csi-pxl-format = "RAW8";
+ xlnx,vfb;
+ xlnx,ppc = <0x4>;
+ xlnx,axis-tdata-width = <0x20>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ xlnx,video-format = <XVIP_VF_YUV_422>;
+ xlnx,video-width = <8>;
+ csiss_out: endpoint {
+ remote-endpoint = <&vcap_csiss_in>;
+ };
+ };
+ port@1 {
+ reg = <1>;
+
+ xlnx,video-format = <XVIP_VF_YUV_422>;
+ xlnx,video-width = <8>;
+
+ csiss_in: endpoint {
+ data-lanes = <1 2 3 4>;
+ /* MIPI CSI2 Camera handle */
+ remote-endpoint = <&vs2016_out>;
+ };
+
+ };
+
+ };
+ };
--
2.7.4

This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

2018-05-29 18:59:50

by Vishal Sagar

[permalink] [raw]
Subject: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images
from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready
for image processing.

It supports upto 4 lanes, filtering based on Virtual Channel selected in
IP, an optional Xilinx IIC controller, optional register interface for
the DPHY, multiple color formats, short packet capture,

This driver helps configure the number of active lanes to be set,
setting and handling interrupts and IP core enable.
It logs the count of events occurring according to their type between
streaming ON and OFF. The short packet reception is notified to
application via v4l2_event.

Signed-off-by: Vishal Sagar <[email protected]>
---
drivers/media/platform/xilinx/Kconfig | 12 +
drivers/media/platform/xilinx/Makefile | 1 +
drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751 +++++++++++++++++++++++
include/uapi/linux/xilinx-csi2rxss.h | 25 +
include/uapi/linux/xilinx-v4l2-controls.h | 14 +
5 files changed, 1803 insertions(+)
create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
create mode 100644 include/uapi/linux/xilinx-csi2rxss.h

diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
index a5d21b7..06d5944 100644
--- a/drivers/media/platform/xilinx/Kconfig
+++ b/drivers/media/platform/xilinx/Kconfig
@@ -8,6 +8,18 @@ config VIDEO_XILINX

if VIDEO_XILINX

+config VIDEO_XILINX_CSI2RXSS
+ tristate "Xilinx CSI2 Rx Subsystem"
+ depends on VIDEO_XILINX
+ help
+ Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
+ based driver that takes input from CSI2 Tx source and converts
+ it into an AXI4-Stream. It has a DPHY (whose register interface
+ can be enabled, an optional I2C controller and an optional Video
+ Format Bridge which converts the AXI4-Stream data to Xilinx Video
+ Bus formats based on UG934. The driver is used to set the number
+ of active lanes and get short packet data.
+
config VIDEO_XILINX_TPG
tristate "Xilinx Video Test Pattern Generator"
depends on VIDEO_XILINX
diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile
index e8a0f2a..768f079 100644
--- a/drivers/media/platform/xilinx/Makefile
+++ b/drivers/media/platform/xilinx/Makefile
@@ -1,5 +1,6 @@
xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o

obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
+obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o
obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
new file mode 100644
index 0000000..03f387c
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
@@ -0,0 +1,1751 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Xilinx MIPI CSI2 Rx Subsystem
+ *
+ * Copyright (C) 2016 - 2018 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <[email protected]>
+ *
+ */
+
+#include <dt-bindings/media/xilinx-vip.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+#include <linux/v4l2-subdev.h>
+#include <linux/xilinx-csi2rxss.h>
+#include <linux/xilinx-v4l2-controls.h>
+#include <media/media-entity.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include "xilinx-vip.h"
+
+/*
+ * MIPI CSI2 Rx register map, bitmask and offsets
+ */
+#define XCSI_CCR_OFFSET 0x00000000
+#define XCSI_CCR_SOFTRESET_SHIFT 1
+#define XCSI_CCR_COREENB_SHIFT 0
+#define XCSI_CCR_SOFTRESET_MASK BIT(XCSI_CCR_SOFTRESET_SHIFT)
+#define XCSI_CCR_COREENB_MASK BIT(XCSI_CCR_COREENB_SHIFT)
+
+#define XCSI_PCR_OFFSET 0x00000004
+#define XCSI_PCR_MAXLANES_MASK 0x00000018
+#define XCSI_PCR_ACTLANES_MASK 0x00000003
+#define XCSI_PCR_MAXLANES_SHIFT 3
+#define XCSI_PCR_ACTLANES_SHIFT 0
+
+#define XCSI_CSR_OFFSET 0x00000010
+#define XCSI_CSR_PKTCOUNT_SHIFT 16
+#define XCSI_CSR_SPFIFOFULL_SHIFT 3
+#define XCSI_CSR_SPFIFONE_SHIFT 2
+#define XCSI_CSR_SLBF_SHIFT 1
+#define XCSI_CSR_RIPCD_SHIFT 0
+#define XCSI_CSR_PKTCOUNT_MASK 0xFFFF0000
+#define XCSI_CSR_SPFIFOFULL_MASK BIT(XCSI_CSR_SPFIFOFULL_SHIFT)
+#define XCSI_CSR_SPFIFONE_MASK BIT(XCSI_CSR_SPFIFONE_SHIFT)
+#define XCSI_CSR_SLBF_MASK BIT(XCSI_CSR_SLBF_SHIFT)
+#define XCSI_CSR_RIPCD_MASK BIT(XCSI_CSR_RIPCD_SHIFT)
+
+#define XCSI_GIER_OFFSET 0x00000020
+#define XCSI_GIER_GIE_SHIFT 0
+#define XCSI_GIER_GIE_MASK BIT(XCSI_GIER_GIE_SHIFT)
+#define XCSI_GIER_SET 1
+#define XCSI_GIER_RESET 0
+
+#define XCSI_ISR_OFFSET 0x00000024
+#define XCSI_ISR_FR_SHIFT 31
+#define XCSI_ISR_ILC_SHIFT 21
+#define XCSI_ISR_SPFIFOF_SHIFT 20
+#define XCSI_ISR_SPFIFONE_SHIFT 19
+#define XCSI_ISR_SLBF_SHIFT 18
+#define XCSI_ISR_STOP_SHIFT 17
+#define XCSI_ISR_SOTERR_SHIFT 13
+#define XCSI_ISR_SOTSYNCERR_SHIFT 12
+#define XCSI_ISR_ECC2BERR_SHIFT 11
+#define XCSI_ISR_ECC1BERR_SHIFT 10
+#define XCSI_ISR_CRCERR_SHIFT 9
+#define XCSI_ISR_DATAIDERR_SHIFT 8
+#define XCSI_ISR_VC3FSYNCERR_SHIFT 7
+#define XCSI_ISR_VC3FLVLERR_SHIFT 6
+#define XCSI_ISR_VC2FSYNCERR_SHIFT 5
+#define XCSI_ISR_VC2FLVLERR_SHIFT 4
+#define XCSI_ISR_VC1FSYNCERR_SHIFT 3
+#define XCSI_ISR_VC1FLVLERR_SHIFT 2
+#define XCSI_ISR_VC0FSYNCERR_SHIFT 1
+#define XCSI_ISR_VC0FLVLERR_SHIFT 0
+#define XCSI_ISR_FR_MASK BIT(XCSI_ISR_FR_SHIFT)
+#define XCSI_ISR_ILC_MASK BIT(XCSI_ISR_ILC_SHIFT)
+#define XCSI_ISR_SPFIFOF_MASK BIT(XCSI_ISR_SPFIFOF_SHIFT)
+#define XCSI_ISR_SPFIFONE_MASK BIT(XCSI_ISR_SPFIFONE_SHIFT)
+#define XCSI_ISR_SLBF_MASK BIT(XCSI_ISR_SLBF_SHIFT)
+#define XCSI_ISR_STOP_MASK BIT(XCSI_ISR_STOP_SHIFT)
+#define XCSI_ISR_SOTERR_MASK BIT(XCSI_ISR_SOTERR_SHIFT)
+#define XCSI_ISR_SOTSYNCERR_MASK BIT(XCSI_ISR_SOTSYNCERR_SHIFT)
+#define XCSI_ISR_ECC2BERR_MASK BIT(XCSI_ISR_ECC2BERR_SHIFT)
+#define XCSI_ISR_ECC1BERR_MASK BIT(XCSI_ISR_ECC1BERR_SHIFT)
+#define XCSI_ISR_CRCERR_MASK BIT(XCSI_ISR_CRCERR_SHIFT)
+#define XCSI_ISR_DATAIDERR_MASK BIT(XCSI_ISR_DATAIDERR_SHIFT)
+#define XCSI_ISR_VC3FSYNCERR_MASK BIT(XCSI_ISR_VC3FSYNCERR_SHIFT)
+#define XCSI_ISR_VC3FLVLERR_MASK BIT(XCSI_ISR_VC3FLVLERR_SHIFT)
+#define XCSI_ISR_VC2FSYNCERR_MASK BIT(XCSI_ISR_VC2FSYNCERR_SHIFT)
+#define XCSI_ISR_VC2FLVLERR_MASK BIT(XCSI_ISR_VC2FLVLERR_SHIFT)
+#define XCSI_ISR_VC1FSYNCERR_MASK BIT(XCSI_ISR_VC1FSYNCERR_SHIFT)
+#define XCSI_ISR_VC1FLVLERR_MASK BIT(XCSI_ISR_VC1FLVLERR_SHIFT)
+#define XCSI_ISR_VC0FSYNCERR_MASK BIT(XCSI_ISR_VC0FSYNCERR_SHIFT)
+#define XCSI_ISR_VC0FLVLERR_MASK BIT(XCSI_ISR_VC0FLVLERR_SHIFT)
+#define XCSI_ISR_ALLINTR_MASK 0x803FFFFF
+
+#define XCSI_INTR_PROT_MASK (XCSI_ISR_VC3FSYNCERR_MASK | \
+ XCSI_ISR_VC3FLVLERR_MASK | \
+ XCSI_ISR_VC2FSYNCERR_MASK | \
+ XCSI_ISR_VC2FLVLERR_MASK | \
+ XCSI_ISR_VC1FSYNCERR_MASK | \
+ XCSI_ISR_VC1FLVLERR_MASK | \
+ XCSI_ISR_VC0FSYNCERR_MASK | \
+ XCSI_ISR_VC0FLVLERR_MASK)
+
+#define XCSI_INTR_PKTLVL_MASK (XCSI_ISR_ECC2BERR_MASK | \
+ XCSI_ISR_ECC1BERR_MASK | \
+ XCSI_ISR_CRCERR_MASK | \
+ XCSI_ISR_DATAIDERR_MASK)
+
+#define XCSI_INTR_DPHY_MASK (XCSI_ISR_SOTERR_MASK | \
+ XCSI_ISR_SOTSYNCERR_MASK)
+
+#define XCSI_INTR_SPKT_MASK (XCSI_ISR_SPFIFOF_MASK | \
+ XCSI_ISR_SPFIFONE_MASK)
+
+#define XCSI_INTR_FRAMERCVD_MASK (XCSI_ISR_FR_MASK)
+
+#define XCSI_INTR_ERR_MASK (XCSI_ISR_ILC_MASK | \
+ XCSI_ISR_SLBF_MASK | \
+ XCSI_ISR_STOP_MASK)
+
+#define XCSI_IER_OFFSET 0x00000028
+#define XCSI_IER_FR_SHIFT 31
+#define XCSI_IER_ILC_SHIFT 21
+#define XCSI_IER_SPFIFOF_SHIFT 20
+#define XCSI_IER_SPFIFONE_SHIFT 19
+#define XCSI_IER_SLBF_SHIFT 18
+#define XCSI_IER_STOP_SHIFT 17
+#define XCSI_IER_SOTERR_SHIFT 13
+#define XCSI_IER_SOTSYNCERR_SHIFT 12
+#define XCSI_IER_ECC2BERR_SHIFT 11
+#define XCSI_IER_ECC1BERR_SHIFT 10
+#define XCSI_IER_CRCERR_SHIFT 9
+#define XCSI_IER_DATAIDERR_SHIFT 8
+#define XCSI_IER_VC3FSYNCERR_SHIFT 7
+#define XCSI_IER_VC3FLVLERR_SHIFT 6
+#define XCSI_IER_VC2FSYNCERR_SHIFT 5
+#define XCSI_IER_VC2FLVLERR_SHIFT 4
+#define XCSI_IER_VC1FSYNCERR_SHIFT 3
+#define XCSI_IER_VC1FLVLERR_SHIFT 2
+#define XCSI_IER_VC0FSYNCERR_SHIFT 1
+#define XCSI_IER_VC0FLVLERR_SHIFT 0
+#define XCSI_IER_FR_MASK BIT(XCSI_IER_FR_SHIFT)
+#define XCSI_IER_ILC_MASK BIT(XCSI_IER_ILC_SHIFT)
+#define XCSI_IER_SPFIFOF_MASK BIT(XCSI_IER_SPFIFOF_SHIFT)
+#define XCSI_IER_SPFIFONE_MASK BIT(XCSI_IER_SPFIFONE_SHIFT)
+#define XCSI_IER_SLBF_MASK BIT(XCSI_IER_SLBF_SHIFT)
+#define XCSI_IER_STOP_MASK BIT(XCSI_IER_STOP_SHIFT)
+#define XCSI_IER_SOTERR_MASK BIT(XCSI_IER_SOTERR_SHIFT)
+#define XCSI_IER_SOTSYNCERR_MASK BIT(XCSI_IER_SOTSYNCERR_SHIFT)
+#define XCSI_IER_ECC2BERR_MASK BIT(XCSI_IER_ECC2BERR_SHIFT)
+#define XCSI_IER_ECC1BERR_MASK BIT(XCSI_IER_ECC1BERR_SHIFT)
+#define XCSI_IER_CRCERR_MASK BIT(XCSI_IER_CRCERR_SHIFT)
+#define XCSI_IER_DATAIDERR_MASK BIT(XCSI_IER_DATAIDERR_SHIFT)
+#define XCSI_IER_VC3FSYNCERR_MASK BIT(XCSI_IER_VC3FSYNCERR_SHIFT)
+#define XCSI_IER_VC3FLVLERR_MASK BIT(XCSI_IER_VC3FLVLERR_SHIFT)
+#define XCSI_IER_VC2FSYNCERR_MASK BIT(XCSI_IER_VC2FSYNCERR_SHIFT)
+#define XCSI_IER_VC2FLVLERR_MASK BIT(XCSI_IER_VC2FLVLERR_SHIFT)
+#define XCSI_IER_VC1FSYNCERR_MASK BIT(XCSI_IER_VC1FSYNCERR_SHIFT)
+#define XCSI_IER_VC1FLVLERR_MASK BIT(XCSI_IER_VC1FLVLERR_SHIFT)
+#define XCSI_IER_VC0FSYNCERR_MASK BIT(XCSI_IER_VC0FSYNCERR_SHIFT)
+#define XCSI_IER_VC0FLVLERR_MASK BIT(XCSI_IER_VC0FLVLERR_SHIFT)
+#define XCSI_IER_ALLINTR_MASK 0x803FFFFF
+
+#define XCSI_SPKTR_OFFSET 0x00000030
+#define XCSI_SPKTR_DATA_SHIFT 8
+#define XCSI_SPKTR_VC_SHIFT 6
+#define XCSI_SPKTR_DT_SHIFT 0
+#define XCSI_SPKTR_DATA_MASK 0x00FFFF00
+#define XCSI_SPKTR_VC_MASK 0x000000C0
+#define XCSI_SPKTR_DT_MASK 0x0000003F
+
+#define XCSI_CLKINFR_OFFSET 0x0000003C
+#define XCSI_CLKINFR_STOP_SHIFT 1
+#define XCSI_CLKINFR_STOP_MASK BIT(XCSI_CLKINFR_STOP_SHIFT)
+
+#define XCSI_L0INFR_OFFSET 0x00000040
+#define XCSI_L1INFR_OFFSET 0x00000044
+#define XCSI_L2INFR_OFFSET 0x00000048
+#define XCSI_L3INFR_OFFSET 0x0000004C
+#define XCSI_LXINFR_STOP_SHIFT 5
+#define XCSI_LXINFR_SOTERR_SHIFT 1
+#define XCSI_LXINFR_SOTSYNCERR_SHIFT 0
+#define XCSI_LXINFR_STOP_MASK BIT(XCSI_LXINFR_STOP_SHIFT)
+#define XCSI_LXINFR_SOTERR_MASK BIT(XCSI_LXINFR_SOTERR_SHIFT)
+#define XCSI_LXINFR_SOTSYNCERR_MASK BIT(XCSI_LXINFR_SOTSYNCERR_SHIFT)
+
+#define XCSI_VC0INF1R_OFFSET 0x00000060
+#define XCSI_VC1INF1R_OFFSET 0x00000068
+#define XCSI_VC2INF1R_OFFSET 0x00000070
+#define XCSI_VC3INF1R_OFFSET 0x00000078
+#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16
+#define XCSI_VCXINF1R_BYTECOUNT_SHIFT 0
+#define XCSI_VCXINF1R_LINECOUNT_MASK 0xFFFF0000
+#define XCSI_VCXINF1R_BYTECOUNT_MASK 0x0000FFFF
+
+#define XCSI_VC0INF2R_OFFSET 0x00000064
+#define XCSI_VC1INF2R_OFFSET 0x0000006C
+#define XCSI_VC2INF2R_OFFSET 0x00000074
+#define XCSI_VC3INF2R_OFFSET 0x0000007C
+#define XCSI_VCXINF2R_DATATYPE_SHIFT 0
+#define XCSI_VCXINF2R_DATATYPE_MASK 0x0000003F
+
+#define XDPHY_CTRLREG_OFFSET 0x0
+#define XDPHY_CTRLREG_DPHYEN_SHIFT 1
+#define XDPHY_CTRLREG_DPHYEN_MASK BIT(XDPHY_CTRLREG_DPHYEN_SHIFT)
+
+#define XDPHY_CLKSTATREG_OFFSET 0x18
+#define XDPHY_CLKSTATREG_MODE_SHIFT 0
+#define XDPHY_CLKSTATREG_MODE_MASK 0x3
+#define XDPHY_LOW_POWER_MODE 0x0
+#define XDPHY_HI_SPEED_MODE 0x1
+#define XDPHY_ESC_MODE 0x2
+
+/*
+ * Interrupt mask
+ */
+#define XCSI_INTR_MASK (XCSI_ISR_ALLINTR_MASK & ~XCSI_ISR_STOP_MASK)
+/*
+ * Timeout for reset
+ */
+#define XCSI_TIMEOUT_VAL (1000) /* us */
+
+/*
+ * Max string length for CSI Data type string
+ */
+#define MAX_XIL_CSIDT_STR_LENGTH 64
+
+/*
+ * Maximum number of short packet events per file handle.
+ */
+#define XCSI_MAX_SPKT (512)
+
+/* Number of media pads */
+#define XILINX_CSI_MEDIA_PADS (2)
+
+#define XCSI_DEFAULT_WIDTH (1920)
+#define XCSI_DEFAULT_HEIGHT (1080)
+
+/*
+ * Macro to return "true" or "false" string if bit is set
+ */
+#define XCSI_GET_BITSET_STR(val, mask) (val) & (mask) ? "true" : "false"
+
+enum csi_datatypes {
+ MIPI_CSI_DT_FRAME_START_CODE = 0x00,
+ MIPI_CSI_DT_FRAME_END_CODE,
+ MIPI_CSI_DT_LINE_START_CODE,
+ MIPI_CSI_DT_LINE_END_CODE,
+ MIPI_CSI_DT_SYNC_RSVD_04,
+ MIPI_CSI_DT_SYNC_RSVD_05,
+ MIPI_CSI_DT_SYNC_RSVD_06,
+ MIPI_CSI_DT_SYNC_RSVD_07,
+ MIPI_CSI_DT_GSPKT_08,
+ MIPI_CSI_DT_GSPKT_09,
+ MIPI_CSI_DT_GSPKT_0A,
+ MIPI_CSI_DT_GSPKT_0B,
+ MIPI_CSI_DT_GSPKT_0C,
+ MIPI_CSI_DT_GSPKT_0D,
+ MIPI_CSI_DT_GSPKT_0E,
+ MIPI_CSI_DT_GSPKT_0F,
+ MIPI_CSI_DT_GLPKT_10,
+ MIPI_CSI_DT_GLPKT_11,
+ MIPI_CSI_DT_GLPKT_12,
+ MIPI_CSI_DT_GLPKT_13,
+ MIPI_CSI_DT_GLPKT_14,
+ MIPI_CSI_DT_GLPKT_15,
+ MIPI_CSI_DT_GLPKT_16,
+ MIPI_CSI_DT_GLPKT_17,
+ MIPI_CSI_DT_YUV_420_8B,
+ MIPI_CSI_DT_YUV_420_10B,
+ MIPI_CSI_DT_YUV_420_8B_LEGACY,
+ MIPI_CSI_DT_YUV_RSVD,
+ MIPI_CSI_DT_YUV_420_8B_CSPS,
+ MIPI_CSI_DT_YUV_420_10B_CSPS,
+ MIPI_CSI_DT_YUV_422_8B,
+ MIPI_CSI_DT_YUV_422_10B,
+ MIPI_CSI_DT_RGB_444,
+ MIPI_CSI_DT_RGB_555,
+ MIPI_CSI_DT_RGB_565,
+ MIPI_CSI_DT_RGB_666,
+ MIPI_CSI_DT_RGB_888,
+ MIPI_CSI_DT_RGB_RSVD_25,
+ MIPI_CSI_DT_RGB_RSVD_26,
+ MIPI_CSI_DT_RGB_RSVD_27,
+ MIPI_CSI_DT_RAW_6,
+ MIPI_CSI_DT_RAW_7,
+ MIPI_CSI_DT_RAW_8,
+ MIPI_CSI_DT_RAW_10,
+ MIPI_CSI_DT_RAW_12,
+ MIPI_CSI_DT_RAW_14,
+ MIPI_CSI_DT_RAW_RSVD_2E,
+ MIPI_CSI_DT_RAW_RSVD_2F,
+ MIPI_CSI_DT_USER_30,
+ MIPI_CSI_DT_USER_31,
+ MIPI_CSI_DT_USER_32,
+ MIPI_CSI_DT_USER_33,
+ MIPI_CSI_DT_USER_34,
+ MIPI_CSI_DT_USER_35,
+ MIPI_CSI_DT_USER_36,
+ MIPI_CSI_DT_USER_37,
+ MIPI_CSI_DT_RSVD_38,
+ MIPI_CSI_DT_RSVD_39,
+ MIPI_CSI_DT_RSVD_3A,
+ MIPI_CSI_DT_RSVD_3B,
+ MIPI_CSI_DT_RSVD_3C,
+ MIPI_CSI_DT_RSVD_3D,
+ MIPI_CSI_DT_RSVD_3E,
+ MIPI_CSI_DT_RSVD_3F
+};
+
+/**
+ * struct pixel_format - Data Type to string name structure
+ * @pixelformat: MIPI CSI2 Data type
+ * @pixelformatstr: String name of Data Type
+ */
+struct pixel_format {
+ enum csi_datatypes pixelformat;
+ char pixelformatstr[MAX_XIL_CSIDT_STR_LENGTH];
+};
+
+/**
+ * struct xcsi2rxss_event - Event log structure
+ * @mask: Event mask
+ * @name: Name of the event
+ * @counter: Count number of events
+ */
+struct xcsi2rxss_event {
+ u32 mask;
+ const char * const name;
+ unsigned int counter;
+};
+
+/*
+ * struct xcsi2rxss_core - Core configuration CSI2 Rx Subsystem device structure
+ * @dev: Platform structure
+ * @iomem: Base address of subsystem
+ * @irq: requested irq number
+ * @dphy_present: Flag for DPHY register interface presence
+ * @dphy_offset: DPHY registers offset
+ * @enable_active_lanes: If number of active lanes can be modified
+ * @max_num_lanes: Maximum number of lanes present
+ * @vfb: Video Format Bridge enabled or not
+ * @ppc: pixels per clock
+ * @vc: Virtual Channel
+ * @axis_tdata_width: AXI Stream data width
+ * @datatype: Data type filter
+ * @pxlformat: String with CSI pixel format from IP
+ * @num_lanes: Number of lanes requested from application
+ * @events: Structure to maintain event logs
+ */
+struct xcsi2rxss_core {
+ struct device *dev;
+ void __iomem *iomem;
+ int irq;
+ u32 dphy_offset;
+ bool dphy_present;
+ bool enable_active_lanes;
+ u32 max_num_lanes;
+ bool vfb;
+ u32 ppc;
+ u32 vc;
+ u32 axis_tdata_width;
+ u32 datatype;
+ const char *pxlformat;
+ u32 num_lanes;
+ struct xcsi2rxss_event *events;
+};
+
+/**
+ * struct xcsi2rxss_state - CSI2 Rx Subsystem device structure
+ * @core: Core structure for MIPI CSI2 Rx Subsystem
+ * @subdev: The v4l2 subdev structure
+ * @ctrl_handler: control handler
+ * @formats: Active V4L2 formats on each pad
+ * @default_format: default V4L2 media bus format
+ * @vip_format: format information corresponding to the active format
+ * @event: Holds the short packet event
+ * @lock: mutex for serializing operations
+ * @pads: media pads
+ * @npads: number of pads
+ * @streaming: Flag for storing streaming state
+ * @suspended: Flag for storing suspended state
+ *
+ * This structure contains the device driver related parameters
+ */
+struct xcsi2rxss_state {
+ struct xcsi2rxss_core core;
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_mbus_framefmt formats[2];
+ struct v4l2_mbus_framefmt default_format;
+ const struct xvip_video_format *vip_format;
+ struct v4l2_event event;
+ /*
+ * Used to serialize access.
+ */
+ struct mutex lock;
+ struct media_pad pads[XILINX_CSI_MEDIA_PADS];
+ unsigned int npads;
+ bool streaming;
+ bool suspended;
+};
+
+static inline struct xcsi2rxss_state *
+to_xcsi2rxssstate(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct xcsi2rxss_state, subdev);
+}
+
+/*
+ * Register related operations
+ */
+static inline u32 xcsi2rxss_read(struct xcsi2rxss_core *xcsi2rxss,
+ u32 addr)
+{
+ return ioread32(xcsi2rxss->iomem + addr);
+}
+
+static inline void xcsi2rxss_write(struct xcsi2rxss_core *xcsi2rxss,
+ u32 addr, u32 value)
+{
+ iowrite32(value, xcsi2rxss->iomem + addr);
+}
+
+static inline void xcsi2rxss_clr(struct xcsi2rxss_core *xcsi2rxss,
+ u32 addr, u32 clr)
+{
+ xcsi2rxss_write(xcsi2rxss,
+ addr,
+ xcsi2rxss_read(xcsi2rxss, addr) & ~clr);
+}
+
+static inline void xcsi2rxss_set(struct xcsi2rxss_core *xcsi2rxss,
+ u32 addr, u32 set)
+{
+ xcsi2rxss_write(xcsi2rxss,
+ addr,
+ xcsi2rxss_read(xcsi2rxss, addr) | set);
+}
+
+static const struct pixel_format pixel_formats[] = {
+ { MIPI_CSI_DT_YUV_420_8B, "YUV420_8bit" },
+ { MIPI_CSI_DT_YUV_420_10B, "YUV420_10bit" },
+ { MIPI_CSI_DT_YUV_420_8B_LEGACY, "Legacy_YUV420_8bit" },
+ { MIPI_CSI_DT_YUV_420_8B_CSPS, "YUV420_8bit_CSPS" },
+ { MIPI_CSI_DT_YUV_420_10B_CSPS, "YUV420_10bit_CSPS" },
+ { MIPI_CSI_DT_YUV_422_8B, "YUV422_8bit" },
+ { MIPI_CSI_DT_YUV_422_10B, "YUV422_10bit" },
+ { MIPI_CSI_DT_RGB_444, "RGB444" },
+ { MIPI_CSI_DT_RGB_555, "RGB555" },
+ { MIPI_CSI_DT_RGB_565, "RGB565" },
+ { MIPI_CSI_DT_RGB_666, "RGB666" },
+ { MIPI_CSI_DT_RGB_888, "RGB888" },
+ { MIPI_CSI_DT_RAW_6, "RAW6" },
+ { MIPI_CSI_DT_RAW_7, "RAW7" },
+ { MIPI_CSI_DT_RAW_8, "RAW8" },
+ { MIPI_CSI_DT_RAW_10, "RAW10" },
+ { MIPI_CSI_DT_RAW_12, "RAW12" },
+ { MIPI_CSI_DT_RAW_14, "RAW14 "}
+};
+
+static struct xcsi2rxss_event xcsi2rxss_events[] = {
+ { XCSI_ISR_FR_MASK, "Frame Received", 0 },
+ { XCSI_ISR_ILC_MASK, "Invalid Lane Count Error", 0 },
+ { XCSI_ISR_SPFIFOF_MASK, "Short Packet FIFO OverFlow Error", 0 },
+ { XCSI_ISR_SPFIFONE_MASK, "Short Packet FIFO Not Empty", 0 },
+ { XCSI_ISR_SLBF_MASK, "Streamline Buffer Full Error", 0 },
+ { XCSI_ISR_STOP_MASK, "Lane Stop State", 0 },
+ { XCSI_ISR_SOTERR_MASK, "SOT Error", 0 },
+ { XCSI_ISR_SOTSYNCERR_MASK, "SOT Sync Error", 0 },
+ { XCSI_ISR_ECC2BERR_MASK, "2 Bit ECC Unrecoverable Error", 0 },
+ { XCSI_ISR_ECC1BERR_MASK, "1 Bit ECC Recoverable Error", 0 },
+ { XCSI_ISR_CRCERR_MASK, "CRC Error", 0 },
+ { XCSI_ISR_DATAIDERR_MASK, "Data Id Error", 0 },
+ { XCSI_ISR_VC3FSYNCERR_MASK, "Virtual Channel 3 Frame Sync Error", 0 },
+ { XCSI_ISR_VC3FLVLERR_MASK, "Virtual Channel 3 Frame Level Error", 0 },
+ { XCSI_ISR_VC2FSYNCERR_MASK, "Virtual Channel 2 Frame Sync Error", 0 },
+ { XCSI_ISR_VC2FLVLERR_MASK, "Virtual Channel 2 Frame Level Error", 0 },
+ { XCSI_ISR_VC1FSYNCERR_MASK, "Virtual Channel 1 Frame Sync Error", 0 },
+ { XCSI_ISR_VC1FLVLERR_MASK, "Virtual Channel 1 Frame Level Error", 0 },
+ { XCSI_ISR_VC0FSYNCERR_MASK, "Virtual Channel 0 Frame Sync Error", 0 },
+ { XCSI_ISR_VC0FLVLERR_MASK, "Virtual Channel 0 Frame Level Error", 0 }
+};
+
+#define XMIPICSISS_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events)
+
+/**
+ * xcsi2rxss_clr_and_set - Clear and set the register with a bitmask
+ * @xcsi2rxss: Xilinx MIPI CSI2 Rx Subsystem subdev core struct
+ * @addr: address of register
+ * @clr: bitmask to be cleared
+ * @set: bitmask to be set
+ *
+ * Clear a bit(s) of mask @clr in the register at address @addr, then set
+ * a bit(s) of mask @set in the register after.
+ */
+static void xcsi2rxss_clr_and_set(struct xcsi2rxss_core *xcsi2rxss,
+ u32 addr, u32 clr, u32 set)
+{
+ u32 reg;
+
+ reg = xcsi2rxss_read(xcsi2rxss, addr);
+ reg &= ~clr;
+ reg |= set;
+ xcsi2rxss_write(xcsi2rxss, addr, reg);
+}
+
+/**
+ * xcsi2rxss_pxlfmtstrtodt - Convert pixel format string got from dts
+ * to data type.
+ * @pxlfmtstr: String obtained while parsing device node
+ *
+ * This function takes a CSI pixel format string obtained while parsing
+ * device tree node and converts it to data type.
+ *
+ * Eg. "RAW8" string is converted to 0x2A.
+ * Refer to MIPI CSI2 specification for details.
+ *
+ * Return: Equivalent pixel format value from table
+ */
+static u32 xcsi2rxss_pxlfmtstrtodt(const char *pxlfmtstr)
+{
+ u32 i;
+ u32 maxentries = ARRAY_SIZE(pixel_formats);
+
+ for (i = 0; i < maxentries; i++) {
+ if (!strncmp(pixel_formats[i].pixelformatstr,
+ pxlfmtstr, MAX_XIL_CSIDT_STR_LENGTH))
+ return pixel_formats[i].pixelformat;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * xcsi2rxss_pxlfmtdttostr - Convert pixel format data type to string.
+ * @datatype: MIPI CSI-2 Data Type
+ *
+ * This function takes a CSI pixel format data type and returns a
+ * pointer to the string name.
+ *
+ * Eg. 0x2A returns "RAW8" string.
+ * Refer to MIPI CSI2 specification for details.
+ *
+ * Return: Equivalent pixel format string from table
+ */
+static const char *xcsi2rxss_pxlfmtdttostr(u32 datatype)
+{
+ u32 i;
+ u32 maxentries = ARRAY_SIZE(pixel_formats);
+
+ for (i = 0; i < maxentries; i++) {
+ if (pixel_formats[i].pixelformat == datatype)
+ return pixel_formats[i].pixelformatstr;
+ }
+
+ return NULL;
+}
+
+/**
+ * xcsi2rxss_enable - Enable or disable the CSI Core
+ * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
+ * @flag: true for enabling, false for disabling
+ *
+ * This function enables/disables the MIPI CSI2 Rx Subsystem core.
+ * After enabling the CSI2 Rx core, the DPHY is enabled in case the
+ * register interface for it is present.
+ */
+static void xcsi2rxss_enable(struct xcsi2rxss_core *core, bool flag)
+{
+ u32 dphyctrlregoffset = core->dphy_offset + XDPHY_CTRLREG_OFFSET;
+
+ if (flag) {
+ xcsi2rxss_write(core, XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
+ if (core->dphy_present)
+ xcsi2rxss_write(core, dphyctrlregoffset,
+ XDPHY_CTRLREG_DPHYEN_MASK);
+ } else {
+ xcsi2rxss_write(core, XCSI_CCR_OFFSET, 0);
+ if (core->dphy_present)
+ xcsi2rxss_write(core, dphyctrlregoffset, 0);
+ }
+}
+
+/**
+ * xcsi2rxss_interrupts_enable - Enable or disable CSI interrupts
+ * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
+ * @flag: true for enabling, false for disabling
+ *
+ * This function enables/disables the interrupts for the MIPI CSI2
+ * Rx Subsystem.
+ */
+static void xcsi2rxss_interrupts_enable(struct xcsi2rxss_core *core, bool flag)
+{
+ if (flag) {
+ xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
+ xcsi2rxss_write(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
+ xcsi2rxss_set(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
+ } else {
+ xcsi2rxss_clr(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
+ xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
+ }
+}
+
+/**
+ * xcsi2rxss_reset - Does a soft reset of the MIPI CSI2 Rx Subsystem
+ * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
+ *
+ * Return: 0 - on success OR -ETIME if reset times out
+ */
+static int xcsi2rxss_reset(struct xcsi2rxss_core *core)
+{
+ u32 timeout = XCSI_TIMEOUT_VAL;
+
+ xcsi2rxss_set(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
+
+ while (xcsi2rxss_read(core, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD_MASK) {
+ if (timeout == 0) {
+ dev_err(core->dev, "Xilinx CSI2 Rx Subsystem Soft Reset Timeout!\n");
+ return -ETIME;
+ }
+
+ timeout--;
+ udelay(1);
+ }
+
+ xcsi2rxss_clr(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
+ return 0;
+}
+
+/**
+ * xcsi2rxss_irq_handler - Interrupt handler for CSI-2
+ * @irq: IRQ number
+ * @dev_id: Pointer to device state
+ *
+ * In the interrupt handler, a list of event counters are updated for
+ * corresponding interrupts. This is useful to get status / debug.
+ * If the short packet FIFO not empty or overflow interrupt is received
+ * capture the short packet and notify of event occurrence
+ *
+ * Return: IRQ_HANDLED after handling interrupts
+ */
+static irqreturn_t xcsi2rxss_irq_handler(int irq, void *dev_id)
+{
+ struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)dev_id;
+ struct xcsi2rxss_core *core = &state->core;
+ u32 status;
+
+ status = xcsi2rxss_read(core, XCSI_ISR_OFFSET) & XCSI_INTR_MASK;
+ dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
+
+ if (!status)
+ return IRQ_NONE;
+
+ if (status & XCSI_ISR_SPFIFONE_MASK) {
+ memset(&state->event, 0, sizeof(state->event));
+
+ state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT;
+
+ *((u32 *)(&state->event.u.data)) =
+ xcsi2rxss_read(core, XCSI_SPKTR_OFFSET);
+
+ v4l2_subdev_notify_event(&state->subdev, &state->event);
+ }
+
+ if (status & XCSI_ISR_SPFIFOF_MASK) {
+ dev_alert(core->dev, "Short packet FIFO overflowed\n");
+
+ memset(&state->event, 0, sizeof(state->event));
+
+ state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT_OVF;
+
+ v4l2_subdev_notify_event(&state->subdev, &state->event);
+ }
+
+ if (status & XCSI_ISR_SLBF_MASK) {
+ dev_alert(core->dev, "Stream Line Buffer Full!\n");
+
+ memset(&state->event, 0, sizeof(state->event));
+
+ state->event.type = V4L2_EVENT_XLNXCSIRX_SLBF;
+
+ v4l2_subdev_notify_event(&state->subdev, &state->event);
+ }
+
+ if (status & XCSI_ISR_ALLINTR_MASK) {
+ unsigned int i;
+
+ for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
+ if (!(status & core->events[i].mask))
+ continue;
+ core->events[i].counter++;
+ dev_dbg(core->dev, "%s: %d\n", core->events[i].name,
+ core->events[i].counter);
+ }
+ }
+
+ xcsi2rxss_write(core, XCSI_ISR_OFFSET, status);
+
+ return IRQ_HANDLED;
+}
+
+static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state)
+{
+ u32 i;
+
+ for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++)
+ state->core.events[i].counter = 0;
+}
+
+/**
+ * xcsi2rxss_log_counters - Print out the event counters.
+ * @state: Pointer to device state
+ *
+ */
+static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state)
+{
+ u32 i;
+
+ for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
+ if (state->core.events[i].counter > 0)
+ v4l2_info(&state->subdev, "%s events: %d\n",
+ state->core.events[i].name,
+ state->core.events[i].counter);
+ }
+}
+
+/**
+ * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver
+ * @sd: Pointer to V4L2 subdevice structure
+ *
+ * This function prints the current status of Xilinx MIPI CSI-2
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_log_status(struct v4l2_subdev *sd)
+{
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+ struct xcsi2rxss_core *core = &xcsi2rxss->core;
+ u32 reg, data, i;
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ xcsi2rxss_log_counters(xcsi2rxss);
+
+ v4l2_info(sd, "***** Core Status *****\n");
+ data = xcsi2rxss_read(core, XCSI_CSR_OFFSET);
+ v4l2_info(sd, "Short Packet FIFO Full = %s\n",
+ XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFOFULL_MASK));
+ v4l2_info(sd, "Short Packet FIFO Not Empty = %s\n",
+ XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFONE_MASK));
+ v4l2_info(sd, "Stream line buffer full = %s\n",
+ XCSI_GET_BITSET_STR(data, XCSI_CSR_SLBF_MASK));
+ v4l2_info(sd, "Soft reset/Core disable in progress = %s\n",
+ XCSI_GET_BITSET_STR(data, XCSI_CSR_RIPCD_MASK));
+
+ /* Clk & Lane Info */
+ v4l2_info(sd, "******** Clock Lane Info *********\n");
+ data = xcsi2rxss_read(core, XCSI_CLKINFR_OFFSET);
+ v4l2_info(sd, "Clock Lane in Stop State = %s\n",
+ XCSI_GET_BITSET_STR(data, XCSI_CLKINFR_STOP_MASK));
+
+ v4l2_info(sd, "******** Data Lane Info *********\n");
+ v4l2_info(sd, "Lane\tSoT Error\tSoT Sync Error\tStop State\n");
+ reg = XCSI_L0INFR_OFFSET;
+ for (i = 0; i < 4; i++) {
+ data = xcsi2rxss_read(core, reg);
+
+ v4l2_info(sd, "%d\t%s\t\t%s\t\t%s\n",
+ i,
+ XCSI_GET_BITSET_STR(data, XCSI_LXINFR_SOTERR_MASK),
+ data & XCSI_LXINFR_SOTSYNCERR_MASK ? "true" : "false",
+ XCSI_GET_BITSET_STR(data, XCSI_LXINFR_STOP_MASK));
+
+ reg += 4;
+ }
+
+ /* Virtual Channel Image Information */
+ v4l2_info(sd, "********** Virtual Channel Info ************\n");
+ v4l2_info(sd, "VC\tLine Count\tByte Count\tData Type\n");
+ reg = XCSI_VC0INF1R_OFFSET;
+ for (i = 0; i < 4; i++) {
+ u32 line_count, byte_count, data_type;
+ char *datatypestr;
+
+ /* Get line and byte count from VCXINFR1 Register */
+ data = xcsi2rxss_read(core, reg);
+ byte_count = (data & XCSI_VCXINF1R_BYTECOUNT_MASK) >>
+ XCSI_VCXINF1R_BYTECOUNT_SHIFT;
+ line_count = (data & XCSI_VCXINF1R_LINECOUNT_MASK) >>
+ XCSI_VCXINF1R_LINECOUNT_SHIFT;
+
+ /* Get data type from VCXINFR2 Register */
+ reg += 4;
+ data = xcsi2rxss_read(core, reg);
+ data_type = (data & XCSI_VCXINF2R_DATATYPE_MASK) >>
+ XCSI_VCXINF2R_DATATYPE_SHIFT;
+ datatypestr = (char *)xcsi2rxss_pxlfmtdttostr(data_type);
+
+ v4l2_info(sd, "%d\t%d\t\t%d\t\t%s\n",
+ i, line_count, byte_count, datatypestr);
+
+ /* Move to next pair of VC Info registers */
+ reg += 4;
+ }
+
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+/*
+ * xcsi2rxss_subscribe_event - Subscribe to the custom short packet
+ * receive event.
+ * @sd: V4L2 Sub device
+ * @fh: V4L2 File Handle
+ * @sub: Subcribe event structure
+ *
+ * There are two types of events to be subscribed.
+ *
+ * First is to register for receiving a short packet.
+ * The short packets received are queued up in a FIFO.
+ * On reception of a short packet, an event will be generated
+ * with the short packet contents copied to its data area.
+ * Application subscribed to this event will poll for POLLPRI.
+ * On getting the event, the app dequeues the event to get the short packet
+ * data.
+ *
+ * Second is to register for Short packet FIFO overflow
+ * In case the rate of receiving short packets is high and
+ * the short packet FIFO overflows, this event will be triggered.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_subscribe_event(struct v4l2_subdev *sd,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int ret;
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ switch (sub->type) {
+ case V4L2_EVENT_XLNXCSIRX_SPKT:
+ case V4L2_EVENT_XLNXCSIRX_SPKT_OVF:
+ case V4L2_EVENT_XLNXCSIRX_SLBF:
+ ret = v4l2_event_subscribe(fh, sub, XCSI_MAX_SPKT, NULL);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return ret;
+}
+
+/**
+ * xcsi2rxss_unsubscribe_event - Unsubscribe from all events registered
+ * @sd: V4L2 Sub device
+ * @fh: V4L2 file handle
+ * @sub: pointer to Event unsubscription structure
+ *
+ * Return: zero on success, else a negative error code.
+ */
+static int xcsi2rxss_unsubscribe_event(struct v4l2_subdev *sd,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ int ret = 0;
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+ mutex_lock(&xcsi2rxss->lock);
+ ret = v4l2_event_unsubscribe(fh, sub);
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return ret;
+}
+
+/**
+ * xcsi2rxss_s_ctrl - This is used to set the Xilinx MIPI CSI-2 V4L2 controls
+ * @ctrl: V4L2 control to be set
+ *
+ * This function is used to set the V4L2 controls for the Xilinx MIPI
+ * CSI-2 Rx Subsystem. It is used to set the active lanes in the system.
+ * The event counters can be reset.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+ u32 timeout = XCSI_TIMEOUT_VAL;
+ u32 active_lanes = 1;
+
+ struct xcsi2rxss_state *xcsi2rxss =
+ container_of(ctrl->handler,
+ struct xcsi2rxss_state, ctrl_handler);
+ struct xcsi2rxss_core *core = &xcsi2rxss->core;
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ switch (ctrl->id) {
+ case V4L2_CID_XILINX_MIPICSISS_ACT_LANES:
+ /*
+ * This will be called only when "Enable Active Lanes" parameter
+ * is set in design
+ */
+ xcsi2rxss_clr_and_set(core, XCSI_PCR_OFFSET,
+ XCSI_PCR_ACTLANES_MASK, ctrl->val - 1);
+
+ /*
+ * If the core is enabled, wait for active lanes to be
+ * set.
+ *
+ * If core is disabled or there is no clock from DPHY Tx
+ * then the read back won't reflect the updated value
+ * as the PPI clock will not be present.
+ */
+
+ if (core->dphy_present) {
+ u32 dphyclkstatregoffset = core->dphy_offset +
+ XDPHY_CLKSTATREG_OFFSET;
+
+ u32 dphyclkstat =
+ xcsi2rxss_read(core, dphyclkstatregoffset) &
+ XDPHY_CLKSTATREG_MODE_MASK;
+
+ u32 coreenable =
+ xcsi2rxss_read(core, XCSI_CCR_OFFSET) &
+ XCSI_CCR_COREENB_MASK;
+
+ char lpmstr[] = "Low Power";
+ char hsmstr[] = "High Speed";
+ char esmstr[] = "Escape";
+ char *modestr;
+
+ switch (dphyclkstat) {
+ case 0:
+ modestr = lpmstr;
+ break;
+ case 1:
+ modestr = hsmstr;
+ break;
+ case 2:
+ modestr = esmstr;
+ break;
+ default:
+ modestr = NULL;
+ break;
+ }
+
+ dev_dbg(core->dev, "DPHY Clock Lane in %s mode\n",
+ modestr);
+
+ if (dphyclkstat == XDPHY_HI_SPEED_MODE &&
+ coreenable) {
+ /* Wait for core to apply new active lanes */
+ while (timeout--)
+ udelay(1);
+
+ active_lanes =
+ xcsi2rxss_read(core, XCSI_PCR_OFFSET);
+ active_lanes &= XCSI_PCR_ACTLANES_MASK;
+ active_lanes++;
+
+ if (active_lanes != (u32)ctrl->val) {
+ dev_err(core->dev, "Failed to set active lanes!\n");
+ ret = -EAGAIN;
+ }
+ }
+ } else {
+ dev_dbg(core->dev, "No read back as no DPHY present.\n");
+ }
+
+ dev_dbg(core->dev, "Set active lanes: requested = %d, active = %d\n",
+ ctrl->val, active_lanes);
+ break;
+ case V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS:
+ xcsi2rxss_reset_event_counters(xcsi2rxss);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return ret;
+}
+
+/**
+ * xcsi2rxss_g_volatile_ctrl - get the Xilinx MIPI CSI-2 Rx controls
+ * @ctrl: Pointer to V4L2 control
+ *
+ * This is used to get the number of frames received by the Xilinx
+ * MIPI CSI-2 Rx.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ int ret = 0;
+ struct xcsi2rxss_state *xcsi2rxss =
+ container_of(ctrl->handler,
+ struct xcsi2rxss_state, ctrl_handler);
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ switch (ctrl->id) {
+ case V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER:
+ ctrl->val = xcsi2rxss->core.events[0].counter;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return ret;
+}
+
+static int xcsi2rxss_start_stream(struct xcsi2rxss_state *xcsi2rxss)
+{
+ int ret;
+
+ xcsi2rxss_enable(&xcsi2rxss->core, true);
+
+ ret = xcsi2rxss_reset(&xcsi2rxss->core);
+ if (ret < 0)
+ return ret;
+
+ xcsi2rxss_interrupts_enable(&xcsi2rxss->core, true);
+
+ return 0;
+}
+
+static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *xcsi2rxss)
+{
+ xcsi2rxss_interrupts_enable(&xcsi2rxss->core, false);
+ xcsi2rxss_enable(&xcsi2rxss->core, false);
+}
+
+/**
+ * xcsi2rxss_s_stream - It is used to start/stop the streaming.
+ * @sd: V4L2 Sub device
+ * @enable: Flag (True / False)
+ *
+ * This function controls the start or stop of streaming for the
+ * Xilinx MIPI CSI-2 Rx Subsystem provided the device isn't in
+ * suspended state.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ int ret = 0;
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ if (xcsi2rxss->suspended) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (enable) {
+ if (!xcsi2rxss->streaming) {
+ /* reset the event counters */
+ xcsi2rxss_reset_event_counters(xcsi2rxss);
+
+ ret = xcsi2rxss_start_stream(xcsi2rxss);
+ if (ret == 0)
+ xcsi2rxss->streaming = true;
+ }
+ } else {
+ if (xcsi2rxss->streaming) {
+ xcsi2rxss_stop_stream(xcsi2rxss);
+ xcsi2rxss->streaming = false;
+ }
+ }
+unlock:
+ mutex_unlock(&xcsi2rxss->lock);
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &xcsi2rxss->formats[pad];
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * xcsi2rxss_get_format - Get the pad format
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ * @fmt: Pointer to pad level media bus format
+ *
+ * This function is used to get the pad format information.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+ mutex_lock(&xcsi2rxss->lock);
+ fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
+ fmt->pad, fmt->which);
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+/**
+ * xcsi2rxss_set_format - This is used to set the pad format
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ * @fmt: Pointer to pad level media bus format
+ *
+ * This function is used to set the pad format.
+ * Since the pad format is fixed in hardware, it can't be
+ * modified on run time. So when a format set is requested by
+ * application, all parameters except the format type is
+ * saved for the pad and the original pad format is sent
+ * back to the application.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *__format;
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+ struct xcsi2rxss_core *core = &xcsi2rxss->core;
+ u32 code;
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ /*
+ * Only the format->code parameter matters for CSI as the
+ * CSI format cannot be changed at runtime.
+ * Ensure that format to set is copied to over to CSI pad format
+ */
+ __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
+ fmt->pad, fmt->which);
+
+ /* Save the pad format code */
+ code = __format->code;
+
+ /* If the bayer pattern to be set is SXXXX8 then only 1x8 type
+ * is supported and core's data type doesn't matter.
+ * In case the bayer pattern being set is SXXX10 then only
+ * 1x10 type are supported and core should be configured for RAW10.
+ * In case the bayer pattern being set is SXXX12 then only
+ * 1x12 type are supported and core should be configured for RAW12.
+ *
+ * Otherwise don't allow change.
+ */
+ if ((fmt->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
+ fmt->format.code == MEDIA_BUS_FMT_SGBRG8_1X8 ||
+ fmt->format.code == MEDIA_BUS_FMT_SGRBG8_1X8 ||
+ fmt->format.code == MEDIA_BUS_FMT_SRGGB8_1X8) ||
+ (core->datatype == MIPI_CSI_DT_RAW_10 &&
+ (fmt->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 ||
+ fmt->format.code == MEDIA_BUS_FMT_SGBRG10_1X10 ||
+ fmt->format.code == MEDIA_BUS_FMT_SGRBG10_1X10 ||
+ fmt->format.code == MEDIA_BUS_FMT_SRGGB10_1X10)) ||
+ (core->datatype == MIPI_CSI_DT_RAW_12 &&
+ (fmt->format.code == MEDIA_BUS_FMT_SBGGR12_1X12 ||
+ fmt->format.code == MEDIA_BUS_FMT_SGBRG12_1X12 ||
+ fmt->format.code == MEDIA_BUS_FMT_SGRBG12_1X12 ||
+ fmt->format.code == MEDIA_BUS_FMT_SRGGB12_1X12))) {
+ /* Copy over the format to be set */
+ *__format = fmt->format;
+ } else {
+ /* Restore the original pad format code */
+ fmt->format.code = code;
+ __format->code = code;
+ }
+
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+/**
+ * xcsi2rxss_open - Called on v4l2_open()
+ * @sd: Pointer to V4L2 sub device structure
+ * @fh: Pointer to V4L2 File handle
+ *
+ * This function is called on v4l2_open(). It sets the default format
+ * for both pads.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+ format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+ *format = xcsi2rxss->default_format;
+
+ format = v4l2_subdev_get_try_format(sd, fh->pad, 1);
+ *format = xcsi2rxss->default_format;
+
+ return 0;
+}
+
+static int xcsi2rxss_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static const struct media_entity_operations xcsi2rxss_media_ops = {
+ .link_validate = v4l2_subdev_link_validate
+};
+
+static const struct v4l2_ctrl_ops xcsi2rxss_ctrl_ops = {
+ .g_volatile_ctrl = xcsi2rxss_g_volatile_ctrl,
+ .s_ctrl = xcsi2rxss_s_ctrl
+};
+
+static struct v4l2_ctrl_config xcsi2rxss_ctrls[] = {
+ {
+ .ops = &xcsi2rxss_ctrl_ops,
+ .id = V4L2_CID_XILINX_MIPICSISS_ACT_LANES,
+ .name = "MIPI CSI2 Rx Subsystem: Active Lanes",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 1,
+ .max = 4,
+ .step = 1,
+ .def = 1,
+ }, {
+ .ops = &xcsi2rxss_ctrl_ops,
+ .id = V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER,
+ .name = "MIPI CSI2 Rx Subsystem: Frames Received Counter",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 0xFFFFFFFF,
+ .step = 1,
+ .def = 0,
+ .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+ }, {
+ .ops = &xcsi2rxss_ctrl_ops,
+ .id = V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS,
+ .name = "MIPI CSI2 Rx Subsystem: Reset Counters",
+ .type = V4L2_CTRL_TYPE_BUTTON,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+ .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
+ }
+};
+
+static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = {
+ .log_status = xcsi2rxss_log_status,
+ .subscribe_event = xcsi2rxss_subscribe_event,
+ .unsubscribe_event = xcsi2rxss_unsubscribe_event
+};
+
+static struct v4l2_subdev_video_ops xcsi2rxss_video_ops = {
+ .s_stream = xcsi2rxss_s_stream
+};
+
+static struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = {
+ .get_fmt = xcsi2rxss_get_format,
+ .set_fmt = xcsi2rxss_set_format,
+};
+
+static struct v4l2_subdev_ops xcsi2rxss_ops = {
+ .core = &xcsi2rxss_core_ops,
+ .video = &xcsi2rxss_video_ops,
+ .pad = &xcsi2rxss_pad_ops
+};
+
+static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = {
+ .open = xcsi2rxss_open,
+ .close = xcsi2rxss_close
+};
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+/*
+ * xcsi2rxss_pm_suspend - Function called on Power Suspend
+ * @dev: Pointer to device structure
+ *
+ * On power suspend the CSI-2 Core is disabled if the device isn't
+ * in suspended state and is streaming.
+ *
+ * Return: 0 on success
+ */
+static int __maybe_unused xcsi2rxss_pm_suspend(struct device *dev)
+{
+ struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ if (!xcsi2rxss->suspended && xcsi2rxss->streaming)
+ xcsi2rxss_clr(&xcsi2rxss->core,
+ XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
+
+ xcsi2rxss->suspended = true;
+
+ mutex_unlock(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+/*
+ * xcsi2rxss_pm_resume - Function called on Power Resume
+ * @dev: Pointer to device structure
+ *
+ * On power resume the CSI-2 Core is enabled when it is in suspended state
+ * and prior to entering suspended state it was streaming.
+ *
+ * Return: 0 on success
+ */
+static int __maybe_unused xcsi2rxss_pm_resume(struct device *dev)
+{
+ struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
+
+ mutex_lock(&xcsi2rxss->lock);
+
+ if (xcsi2rxss->suspended && xcsi2rxss->streaming)
+ xcsi2rxss_set(&xcsi2rxss->core,
+ XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
+
+ xcsi2rxss->suspended = false;
+
+ mutex_unlock(&xcsi2rxss->lock);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss)
+{
+ struct device_node *node = xcsi2rxss->core.dev->of_node;
+ struct device_node *ports = NULL;
+ struct device_node *port = NULL;
+ unsigned int nports = 0;
+ struct xcsi2rxss_core *core = &xcsi2rxss->core;
+ int ret;
+ bool iic_present;
+
+ core->dphy_present = of_property_read_bool(node, "xlnx,dphy-present");
+ dev_dbg(core->dev, "DPHY present property = %s\n",
+ core->dphy_present ? "Present" : "Absent");
+
+ iic_present = of_property_read_bool(node, "xlnx,iic-present");
+ dev_dbg(core->dev, "IIC present property = %s\n",
+ iic_present ? "Present" : "Absent");
+
+ if (core->dphy_present) {
+ if (iic_present)
+ core->dphy_offset = 0x20000;
+ else
+ core->dphy_offset = 0x10000;
+ }
+
+ ret = of_property_read_u32(node, "xlnx,max-lanes",
+ &core->max_num_lanes);
+ if (ret < 0) {
+ dev_err(core->dev, "missing xlnx,max-lanes property\n");
+ return ret;
+ }
+
+ if (core->max_num_lanes > 4 || core->max_num_lanes < 1) {
+ dev_err(core->dev, "%d max lanes : invalid xlnx,max-lanes property\n",
+ core->max_num_lanes);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, "xlnx,vc", &core->vc);
+ if (ret < 0) {
+ dev_err(core->dev, "missing xlnx,vc property\n");
+ return ret;
+ }
+ if (core->vc > 4) {
+ dev_err(core->dev, "invalid virtual channel property value.\n");
+ return -EINVAL;
+ }
+
+ core->enable_active_lanes =
+ of_property_read_bool(node, "xlnx,en-active-lanes");
+ dev_dbg(core->dev, "Enable active lanes property = %s\n",
+ core->enable_active_lanes ? "Present" : "Absent");
+
+ ret = of_property_read_string(node, "xlnx,csi-pxl-format",
+ &core->pxlformat);
+ if (ret < 0) {
+ dev_err(core->dev, "missing xlnx,csi-pxl-format property\n");
+ return ret;
+ }
+
+ core->datatype = xcsi2rxss_pxlfmtstrtodt(core->pxlformat);
+ if (core->datatype < MIPI_CSI_DT_YUV_420_8B ||
+ core->datatype > MIPI_CSI_DT_RAW_14) {
+ dev_err(core->dev, "Invalid xlnx,csi-pxl-format string\n");
+ return -EINVAL;
+ }
+
+ core->vfb = of_property_read_bool(node, "xlnx,vfb");
+ dev_dbg(core->dev, "Video Format Bridge property = %s\n",
+ core->vfb ? "Present" : "Absent");
+
+ if (core->vfb) {
+ ret = of_property_read_u32(node, "xlnx,ppc", &core->ppc);
+ if (ret < 0 || !(core->ppc == 1 || core->ppc == 2 ||
+ core->ppc == 4)) {
+ dev_err(core->dev, "Invalid xlnx,ppc property ret = %d ppc = %d\n",
+ ret, core->ppc);
+ return -EINVAL;
+ }
+ }
+
+ ports = of_get_child_by_name(node, "ports");
+ if (!ports)
+ ports = node;
+
+ for_each_child_of_node(ports, port) {
+ int ret;
+ const struct xvip_video_format *format;
+ struct device_node *endpoint;
+ struct v4l2_fwnode_endpoint v4lendpoint;
+
+ if (!port->name || of_node_cmp(port->name, "port"))
+ continue;
+
+ /*
+ * Currently only a subset of VFB enabled formats present in
+ * xvip are supported in the driver.
+ *
+ * If the VFB is disabled, the pixels per clock don't matter.
+ * The data width is either 32 or 64 bit as selected in design.
+ *
+ * For e.g. If Data Type is RGB888, VFB is disabled and
+ * data width is 32 bits.
+ *
+ * Clk Cycle | Byte 0 | Byte 1 | Byte 2 | Byte 3
+ * -----------+----------+----------+----------+----------
+ * 1 | B0 | G0 | R0 | B1
+ * 2 | G1 | R1 | B2 | G2
+ * 3 | R2 | B3 | G3 | R3
+ */
+ format = xvip_of_get_format(port);
+ if (IS_ERR(format)) {
+ dev_err(core->dev, "invalid format in DT");
+ return PTR_ERR(format);
+ }
+
+ if (core->vfb &&
+ format->vf_code != XVIP_VF_YUV_422 &&
+ format->vf_code != XVIP_VF_RBG &&
+ format->vf_code != XVIP_VF_MONO_SENSOR) {
+ dev_err(core->dev, "Invalid UG934 video format set.\n");
+ return -EINVAL;
+ }
+
+ /* Get and check the format description */
+ if (!xcsi2rxss->vip_format) {
+ xcsi2rxss->vip_format = format;
+ } else if (xcsi2rxss->vip_format != format) {
+ dev_err(core->dev, "in/out format mismatch in DT");
+ return -EINVAL;
+ }
+
+ endpoint = of_get_next_child(port, NULL);
+ if (!endpoint) {
+ dev_err(core->dev, "No port at\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
+ &v4lendpoint);
+ if (ret) {
+ of_node_put(endpoint);
+ return ret;
+ }
+
+ of_node_put(endpoint);
+ dev_dbg(core->dev, "%s : port %d bus type = %d\n",
+ __func__, nports, v4lendpoint.bus_type);
+
+ if (v4lendpoint.bus_type == V4L2_MBUS_CSI2) {
+ dev_dbg(core->dev, "%s : base.port = %d base.id = %d\n",
+ __func__, v4lendpoint.base.port,
+ v4lendpoint.base.id);
+
+ dev_dbg(core->dev, "%s : mipi number lanes = %d\n",
+ __func__,
+ v4lendpoint.bus.mipi_csi2.num_data_lanes);
+ } else {
+ dev_dbg(core->dev, "%s : Not a CSI2 bus\n", __func__);
+ }
+
+ /* Count the number of ports. */
+ nports++;
+ }
+
+ if (nports != 2) {
+ dev_err(core->dev, "invalid number of ports %u\n", nports);
+ return -EINVAL;
+ }
+ xcsi2rxss->npads = nports;
+
+ /*Register interrupt handler */
+ core->irq = irq_of_parse_and_map(node, 0);
+
+ ret = devm_request_irq(core->dev, core->irq, xcsi2rxss_irq_handler,
+ IRQF_SHARED, "xilinx-csi2rxss", xcsi2rxss);
+ if (ret) {
+ dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int xcsi2rxss_probe(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev;
+ struct xcsi2rxss_state *xcsi2rxss;
+ struct resource *res;
+
+ u32 i;
+ int ret;
+ int num_ctrls;
+
+ xcsi2rxss = devm_kzalloc(&pdev->dev, sizeof(*xcsi2rxss), GFP_KERNEL);
+ if (!xcsi2rxss)
+ return -ENOMEM;
+
+ mutex_init(&xcsi2rxss->lock);
+
+ xcsi2rxss->core.dev = &pdev->dev;
+
+ ret = xcsi2rxss_parse_of(xcsi2rxss);
+ if (ret < 0)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xcsi2rxss->core.iomem = devm_ioremap_resource(xcsi2rxss->core.dev, res);
+ if (IS_ERR(xcsi2rxss->core.iomem))
+ return PTR_ERR(xcsi2rxss->core.iomem);
+
+ /*
+ * Reset and initialize the core.
+ */
+ xcsi2rxss_reset(&xcsi2rxss->core);
+
+ xcsi2rxss->core.events = (struct xcsi2rxss_event *)&xcsi2rxss_events;
+
+ /* Initialize V4L2 subdevice and media entity */
+ xcsi2rxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
+ xcsi2rxss->pads[1].flags = MEDIA_PAD_FL_SINK;
+
+ /* Initialize the default format */
+ memset(&xcsi2rxss->default_format, 0,
+ sizeof(xcsi2rxss->default_format));
+ xcsi2rxss->default_format.code = xcsi2rxss->vip_format->code;
+ xcsi2rxss->default_format.field = V4L2_FIELD_NONE;
+ xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB;
+ xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH;
+ xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT;
+
+ xcsi2rxss->formats[0] = xcsi2rxss->default_format;
+ xcsi2rxss->formats[1] = xcsi2rxss->default_format;
+
+ /* Initialize V4L2 subdevice and media entity */
+ subdev = &xcsi2rxss->subdev;
+ v4l2_subdev_init(subdev, &xcsi2rxss_ops);
+
+ subdev->dev = &pdev->dev;
+ subdev->internal_ops = &xcsi2rxss_internal_ops;
+ strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
+
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ subdev->entity.ops = &xcsi2rxss_media_ops;
+
+ v4l2_set_subdevdata(subdev, xcsi2rxss);
+
+ ret = media_entity_pads_init(&subdev->entity, 2, xcsi2rxss->pads);
+ if (ret < 0)
+ goto error;
+
+ /*
+ * In case the Enable Active Lanes config parameter is not set,
+ * dynamic lane reconfiguration is not allowed.
+ * So V4L2_CID_XILINX_MIPICSISS_ACT_LANES ctrl will not be registered.
+ * Accordingly allocate the number of controls
+ */
+ num_ctrls = ARRAY_SIZE(xcsi2rxss_ctrls);
+
+ if (!xcsi2rxss->core.enable_active_lanes)
+ num_ctrls--;
+
+ dev_dbg(xcsi2rxss->core.dev, "# of ctrls = %d\n", num_ctrls);
+
+ v4l2_ctrl_handler_init(&xcsi2rxss->ctrl_handler, num_ctrls);
+
+ for (i = 0; i < ARRAY_SIZE(xcsi2rxss_ctrls); i++) {
+ struct v4l2_ctrl *ctrl;
+
+ if (xcsi2rxss_ctrls[i].id ==
+ V4L2_CID_XILINX_MIPICSISS_ACT_LANES) {
+ if (xcsi2rxss->core.enable_active_lanes) {
+ xcsi2rxss_ctrls[i].max =
+ xcsi2rxss->core.max_num_lanes;
+ } else {
+ /* Don't register control */
+ dev_dbg(xcsi2rxss->core.dev,
+ "Skip active lane control\n");
+ continue;
+ }
+ }
+
+ dev_dbg(xcsi2rxss->core.dev, "%d ctrl = 0x%x\n",
+ i, xcsi2rxss_ctrls[i].id);
+ ctrl = v4l2_ctrl_new_custom(&xcsi2rxss->ctrl_handler,
+ &xcsi2rxss_ctrls[i], NULL);
+ if (!ctrl) {
+ dev_err(xcsi2rxss->core.dev, "Failed for %s ctrl\n",
+ xcsi2rxss_ctrls[i].name);
+ goto error;
+ }
+ }
+
+ dev_dbg(xcsi2rxss->core.dev, "# v4l2 ctrls registered = %d\n", i - 1);
+
+ if (xcsi2rxss->ctrl_handler.error) {
+ dev_err(&pdev->dev, "failed to add controls\n");
+ ret = xcsi2rxss->ctrl_handler.error;
+ goto error;
+ }
+
+ subdev->ctrl_handler = &xcsi2rxss->ctrl_handler;
+
+ ret = v4l2_ctrl_handler_setup(&xcsi2rxss->ctrl_handler);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set controls\n");
+ goto error;
+ }
+
+ platform_set_drvdata(pdev, xcsi2rxss);
+
+ dev_info(xcsi2rxss->core.dev, "Xilinx CSI2 Rx Subsystem device found!\n");
+
+ ret = v4l2_async_register_subdev(subdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register subdev\n");
+ goto error;
+ }
+
+ /* default states for streaming and suspend */
+ xcsi2rxss->streaming = false;
+ xcsi2rxss->suspended = false;
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
+ media_entity_cleanup(&subdev->entity);
+ mutex_destroy(&xcsi2rxss->lock);
+
+ return ret;
+}
+
+static int xcsi2rxss_remove(struct platform_device *pdev)
+{
+ struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev);
+ struct v4l2_subdev *subdev = &xcsi2rxss->subdev;
+
+ v4l2_async_unregister_subdev(subdev);
+ v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
+ media_entity_cleanup(&subdev->entity);
+ mutex_destroy(&xcsi2rxss->lock);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xcsi2rxss_pm_ops,
+ xcsi2rxss_pm_suspend, xcsi2rxss_pm_resume);
+
+static const struct of_device_id xcsi2rxss_of_id_table[] = {
+ { .compatible = "xlnx,mipi-csi2-rx-subsystem-2.0" },
+ { .compatible = "xlnx,mipi-csi2-rx-subsystem-3.0" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table);
+
+static struct platform_driver xcsi2rxss_driver = {
+ .driver = {
+ .name = "xilinx-csi2rxss",
+ .pm = &xcsi2rxss_pm_ops,
+ .of_match_table = xcsi2rxss_of_id_table,
+ },
+ .probe = xcsi2rxss_probe,
+ .remove = xcsi2rxss_remove,
+};
+module_platform_driver(xcsi2rxss_driver);
+MODULE_AUTHOR("Vishal Sagar <[email protected]>");
+MODULE_DESCRIPTION("Xilinx MIPI CSI2 Rx Subsystem Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/xilinx-csi2rxss.h b/include/uapi/linux/xilinx-csi2rxss.h
new file mode 100644
index 0000000..973d5b46
--- /dev/null
+++ b/include/uapi/linux/xilinx-csi2rxss.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Events from Xilinx MIPI CSI2 Rx Subsystem exposed to V4L2 application.
+ *
+ * Copyright (C) 2016 - 2018 Xilinx, Inc.
+ * Author: Vishal Sagar <[email protected]>
+ */
+#ifndef __UAPI_XILINX_CSI2RXSS_H__
+#define __UAPI_XILINX_CSI2RXSS_H__
+
+#include <linux/videodev2.h>
+
+/*
+ * Events
+ *
+ * V4L2_EVENT_XLNXCSIRX_SPKT: Short packet received
+ * V4L2_EVENT_XLNXCSIRX_SPKT_OVF: Short packet FIFO overflow
+ * V4L2_EVENT_XLNXCSIRX_SLBF: Stream line buffer full
+ */
+#define V4L2_EVENT_XLNXCSIRX_CLASS (V4L2_EVENT_PRIVATE_START | 0x100)
+#define V4L2_EVENT_XLNXCSIRX_SPKT (V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
+#define V4L2_EVENT_XLNXCSIRX_SPKT_OVF (V4L2_EVENT_XLNXCSIRX_CLASS | 0x2)
+#define V4L2_EVENT_XLNXCSIRX_SLBF (V4L2_EVENT_XLNXCSIRX_CLASS | 0x3)
+
+#endif /* __UAPI_XILINX_CSI2RXSS_H__ */
diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
index b6441fe..4ca3b44 100644
--- a/include/uapi/linux/xilinx-v4l2-controls.h
+++ b/include/uapi/linux/xilinx-v4l2-controls.h
@@ -71,4 +71,18 @@
/* Noise level */
#define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17)

+/*
+ * Xilinx MIPI CSI2 Rx Subsystem
+ */
+
+/* Base ID */
+#define V4L2_CID_XILINX_MIPICSISS (V4L2_CID_USER_BASE + 0xc080)
+
+/* Active Lanes */
+#define V4L2_CID_XILINX_MIPICSISS_ACT_LANES (V4L2_CID_XILINX_MIPICSISS + 1)
+/* Frames received since streaming is set */
+#define V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER (V4L2_CID_XILINX_MIPICSISS + 2)
+/* Reset all event counters */
+#define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 3)
+
#endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
--
2.7.4

This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

2018-05-29 20:00:20

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

On 05/29/2018 11:54 AM, Vishal Sagar wrote:
>
> Signed-off-by: Vishal Sagar <[email protected]>
> ---
> drivers/media/platform/xilinx/Kconfig | 12 +
> drivers/media/platform/xilinx/Makefile | 1 +
> drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751 +++++++++++++++++++++++
> include/uapi/linux/xilinx-csi2rxss.h | 25 +
> include/uapi/linux/xilinx-v4l2-controls.h | 14 +
> 5 files changed, 1803 insertions(+)
> create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
> create mode 100644 include/uapi/linux/xilinx-csi2rxss.h
>
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index a5d21b7..06d5944 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -8,6 +8,18 @@ config VIDEO_XILINX
>
> if VIDEO_XILINX
>
> +config VIDEO_XILINX_CSI2RXSS
> + tristate "Xilinx CSI2 Rx Subsystem"
> + depends on VIDEO_XILINX
> + help
> + Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
> + based driver that takes input from CSI2 Tx source and converts
> + it into an AXI4-Stream. It has a DPHY (whose register interface
> + can be enabled, an optional I2C controller and an optional Video

can be enabled),

> + Format Bridge which converts the AXI4-Stream data to Xilinx Video
> + Bus formats based on UG934. The driver is used to set the number
> + of active lanes and get short packet data.
> +
> config VIDEO_XILINX_TPG
> tristate "Xilinx Video Test Pattern Generator"
> depends on VIDEO_XILINX



> This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

:(

--
~Randy

2018-05-30 06:23:05

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

Hi Randy,

Thanks for reviewing.

> -----Original Message-----
> From: Randy Dunlap [mailto:[email protected]]
> Sent: Wednesday, May 30, 2018 1:29 AM
> To: Vishal Sagar <[email protected]>; Hyun Kwon <[email protected]>;
> [email protected]; [email protected]; linux-
> [email protected]; [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected]; Dinesh
> Kumar <[email protected]>; [email protected]; linux-
> [email protected]
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem
> driver
>
> On 05/29/2018 11:54 AM, Vishal Sagar wrote:
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > drivers/media/platform/xilinx/Kconfig | 12 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751
> +++++++++++++++++++++++
> > include/uapi/linux/xilinx-csi2rxss.h | 25 +
> > include/uapi/linux/xilinx-v4l2-controls.h | 14 +
> > 5 files changed, 1803 insertions(+)
> > create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
> > create mode 100644 include/uapi/linux/xilinx-csi2rxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index a5d21b7..06d5944 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -8,6 +8,18 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +config VIDEO_XILINX_CSI2RXSS
> > + tristate "Xilinx CSI2 Rx Subsystem"
> > + depends on VIDEO_XILINX
> > + help
> > + Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
> > + based driver that takes input from CSI2 Tx source and converts
> > + it into an AXI4-Stream. It has a DPHY (whose register interface
> > + can be enabled, an optional I2C controller and an optional Video
>
> can be enabled),
>

Noted. I will update in next revision of patch.

> > + Format Bridge which converts the AXI4-Stream data to Xilinx Video
> > + Bus formats based on UG934. The driver is used to set the number
> > + of active lanes and get short packet data.
> > +
> > config VIDEO_XILINX_TPG
> > tristate "Xilinx Video Test Pattern Generator"
> > depends on VIDEO_XILINX
>
>
>
> > This email and any attachments are intended for the sole use of the named
> recipient(s) and contain(s) confidential information that may be proprietary,
> privileged or copyrighted under applicable law. If you are not the intended
> recipient, do not read, copy, or forward this email message or any attachments.
> Delete this email message and any attachments immediately.
>
> :(
>

Sorry about that.
I will work with IT on getting this removed before sending v2 for the patches.

-- Vishal

> --
> ~Randy

2018-05-31 01:19:31

by Hyun Kwon

[permalink] [raw]
Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

Hi Vishal,

Thanks for the patch.

On Tue, 2018-05-29 at 11:54:44 -0700, Vishal Sagar wrote:
> The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images
> from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready
> for image processing.
>
> It supports upto 4 lanes, filtering based on Virtual Channel selected in
> IP, an optional Xilinx IIC controller, optional register interface for
> the DPHY, multiple color formats, short packet capture,
>
> This driver helps configure the number of active lanes to be set,
> setting and handling interrupts and IP core enable.
> It logs the count of events occurring according to their type between
> streaming ON and OFF. The short packet reception is notified to
> application via v4l2_event.
>
> Signed-off-by: Vishal Sagar <[email protected]>
> ---
> drivers/media/platform/xilinx/Kconfig | 12 +
> drivers/media/platform/xilinx/Makefile | 1 +
> drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751 +++++++++++++++++++++++
> include/uapi/linux/xilinx-csi2rxss.h | 25 +
> include/uapi/linux/xilinx-v4l2-controls.h | 14 +
> 5 files changed, 1803 insertions(+)
> create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
> create mode 100644 include/uapi/linux/xilinx-csi2rxss.h
>
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index a5d21b7..06d5944 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -8,6 +8,18 @@ config VIDEO_XILINX
>
> if VIDEO_XILINX
>
> +config VIDEO_XILINX_CSI2RXSS
> + tristate "Xilinx CSI2 Rx Subsystem"
> + depends on VIDEO_XILINX
> + help
> + Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
> + based driver that takes input from CSI2 Tx source and converts
> + it into an AXI4-Stream. It has a DPHY (whose register interface
> + can be enabled, an optional I2C controller and an optional Video
> + Format Bridge which converts the AXI4-Stream data to Xilinx Video
> + Bus formats based on UG934. The driver is used to set the number
> + of active lanes and get short packet data.
> +
> config VIDEO_XILINX_TPG
> tristate "Xilinx Video Test Pattern Generator"
> depends on VIDEO_XILINX
> diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile
> index e8a0f2a..768f079 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -1,5 +1,6 @@
> xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
>
> obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
> +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o
> obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
> obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
> diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> new file mode 100644
> index 0000000..03f387c
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> @@ -0,0 +1,1751 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx MIPI CSI2 Rx Subsystem
> + *
> + * Copyright (C) 2016 - 2018 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <[email protected]>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/spinlock_types.h>
> +#include <linux/types.h>
> +#include <linux/v4l2-subdev.h>
> +#include <linux/xilinx-csi2rxss.h>
> +#include <linux/xilinx-v4l2-controls.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +#include "xilinx-vip.h"
> +
> +/*
> + * MIPI CSI2 Rx register map, bitmask and offsets
> + */
> +#define XCSI_CCR_OFFSET 0x00000000
> +#define XCSI_CCR_SOFTRESET_SHIFT 1
> +#define XCSI_CCR_COREENB_SHIFT 0
> +#define XCSI_CCR_SOFTRESET_MASK BIT(XCSI_CCR_SOFTRESET_SHIFT)
> +#define XCSI_CCR_COREENB_MASK BIT(XCSI_CCR_COREENB_SHIFT)

Single bit field doesn't have to be called as '_MASK', and thus the shift is not
needed. So,

#define XCSI_CCR_COREENB BIT(0)

would be enough. Same for the rest.

> +
> +#define XCSI_PCR_OFFSET 0x00000004
> +#define XCSI_PCR_MAXLANES_MASK 0x00000018
> +#define XCSI_PCR_ACTLANES_MASK 0x00000003

You can use GENMASK().

> +#define XCSI_PCR_MAXLANES_SHIFT 3
> +#define XCSI_PCR_ACTLANES_SHIFT 0

These are not even used. You can remove these.

> +
> +#define XCSI_CSR_OFFSET 0x00000010
> +#define XCSI_CSR_PKTCOUNT_SHIFT 16
> +#define XCSI_CSR_SPFIFOFULL_SHIFT 3
> +#define XCSI_CSR_SPFIFONE_SHIFT 2
> +#define XCSI_CSR_SLBF_SHIFT 1
> +#define XCSI_CSR_RIPCD_SHIFT 0
> +#define XCSI_CSR_PKTCOUNT_MASK 0xFFFF0000

I prefer the low case for hex values, but maybe it's just me.

> +#define XCSI_CSR_SPFIFOFULL_MASK BIT(XCSI_CSR_SPFIFOFULL_SHIFT)
> +#define XCSI_CSR_SPFIFONE_MASK BIT(XCSI_CSR_SPFIFONE_SHIFT)
> +#define XCSI_CSR_SLBF_MASK BIT(XCSI_CSR_SLBF_SHIFT)
> +#define XCSI_CSR_RIPCD_MASK BIT(XCSI_CSR_RIPCD_SHIFT)
> +
> +#define XCSI_GIER_OFFSET 0x00000020
> +#define XCSI_GIER_GIE_SHIFT 0
> +#define XCSI_GIER_GIE_MASK BIT(XCSI_GIER_GIE_SHIFT)
> +#define XCSI_GIER_SET 1
> +#define XCSI_GIER_RESET 0
> +
> +#define XCSI_ISR_OFFSET 0x00000024
> +#define XCSI_ISR_FR_SHIFT 31
> +#define XCSI_ISR_ILC_SHIFT 21
> +#define XCSI_ISR_SPFIFOF_SHIFT 20
> +#define XCSI_ISR_SPFIFONE_SHIFT 19
> +#define XCSI_ISR_SLBF_SHIFT 18
> +#define XCSI_ISR_STOP_SHIFT 17
> +#define XCSI_ISR_SOTERR_SHIFT 13
> +#define XCSI_ISR_SOTSYNCERR_SHIFT 12
> +#define XCSI_ISR_ECC2BERR_SHIFT 11
> +#define XCSI_ISR_ECC1BERR_SHIFT 10
> +#define XCSI_ISR_CRCERR_SHIFT 9
> +#define XCSI_ISR_DATAIDERR_SHIFT 8
> +#define XCSI_ISR_VC3FSYNCERR_SHIFT 7
> +#define XCSI_ISR_VC3FLVLERR_SHIFT 6
> +#define XCSI_ISR_VC2FSYNCERR_SHIFT 5
> +#define XCSI_ISR_VC2FLVLERR_SHIFT 4
> +#define XCSI_ISR_VC1FSYNCERR_SHIFT 3
> +#define XCSI_ISR_VC1FLVLERR_SHIFT 2
> +#define XCSI_ISR_VC0FSYNCERR_SHIFT 1
> +#define XCSI_ISR_VC0FLVLERR_SHIFT 0
> +#define XCSI_ISR_FR_MASK BIT(XCSI_ISR_FR_SHIFT)
> +#define XCSI_ISR_ILC_MASK BIT(XCSI_ISR_ILC_SHIFT)
> +#define XCSI_ISR_SPFIFOF_MASK BIT(XCSI_ISR_SPFIFOF_SHIFT)
> +#define XCSI_ISR_SPFIFONE_MASK BIT(XCSI_ISR_SPFIFONE_SHIFT)
> +#define XCSI_ISR_SLBF_MASK BIT(XCSI_ISR_SLBF_SHIFT)
> +#define XCSI_ISR_STOP_MASK BIT(XCSI_ISR_STOP_SHIFT)
> +#define XCSI_ISR_SOTERR_MASK BIT(XCSI_ISR_SOTERR_SHIFT)
> +#define XCSI_ISR_SOTSYNCERR_MASK BIT(XCSI_ISR_SOTSYNCERR_SHIFT)
> +#define XCSI_ISR_ECC2BERR_MASK BIT(XCSI_ISR_ECC2BERR_SHIFT)
> +#define XCSI_ISR_ECC1BERR_MASK BIT(XCSI_ISR_ECC1BERR_SHIFT)
> +#define XCSI_ISR_CRCERR_MASK BIT(XCSI_ISR_CRCERR_SHIFT)
> +#define XCSI_ISR_DATAIDERR_MASK BIT(XCSI_ISR_DATAIDERR_SHIFT)
> +#define XCSI_ISR_VC3FSYNCERR_MASK BIT(XCSI_ISR_VC3FSYNCERR_SHIFT)
> +#define XCSI_ISR_VC3FLVLERR_MASK BIT(XCSI_ISR_VC3FLVLERR_SHIFT)
> +#define XCSI_ISR_VC2FSYNCERR_MASK BIT(XCSI_ISR_VC2FSYNCERR_SHIFT)
> +#define XCSI_ISR_VC2FLVLERR_MASK BIT(XCSI_ISR_VC2FLVLERR_SHIFT)
> +#define XCSI_ISR_VC1FSYNCERR_MASK BIT(XCSI_ISR_VC1FSYNCERR_SHIFT)
> +#define XCSI_ISR_VC1FLVLERR_MASK BIT(XCSI_ISR_VC1FLVLERR_SHIFT)
> +#define XCSI_ISR_VC0FSYNCERR_MASK BIT(XCSI_ISR_VC0FSYNCERR_SHIFT)
> +#define XCSI_ISR_VC0FLVLERR_MASK BIT(XCSI_ISR_VC0FLVLERR_SHIFT)

Ditto for mask / shift and unused ones.

> +#define XCSI_ISR_ALLINTR_MASK 0x803FFFFF

You can OR maskes below to get this too, at least to be consistent with below.
But up to you.

> +
> +#define XCSI_INTR_PROT_MASK (XCSI_ISR_VC3FSYNCERR_MASK | \
> + XCSI_ISR_VC3FLVLERR_MASK | \
> + XCSI_ISR_VC2FSYNCERR_MASK | \
> + XCSI_ISR_VC2FLVLERR_MASK | \
> + XCSI_ISR_VC1FSYNCERR_MASK | \
> + XCSI_ISR_VC1FLVLERR_MASK | \
> + XCSI_ISR_VC0FSYNCERR_MASK | \
> + XCSI_ISR_VC0FLVLERR_MASK)
> +
> +#define XCSI_INTR_PKTLVL_MASK (XCSI_ISR_ECC2BERR_MASK | \
> + XCSI_ISR_ECC1BERR_MASK | \
> + XCSI_ISR_CRCERR_MASK | \
> + XCSI_ISR_DATAIDERR_MASK)
> +
> +#define XCSI_INTR_DPHY_MASK (XCSI_ISR_SOTERR_MASK | \
> + XCSI_ISR_SOTSYNCERR_MASK)
> +
> +#define XCSI_INTR_SPKT_MASK (XCSI_ISR_SPFIFOF_MASK | \
> + XCSI_ISR_SPFIFONE_MASK)
> +
> +#define XCSI_INTR_FRAMERCVD_MASK (XCSI_ISR_FR_MASK)
> +
> +#define XCSI_INTR_ERR_MASK (XCSI_ISR_ILC_MASK | \
> + XCSI_ISR_SLBF_MASK | \
> + XCSI_ISR_STOP_MASK)
> +
> +#define XCSI_IER_OFFSET 0x00000028
> +#define XCSI_IER_FR_SHIFT 31
> +#define XCSI_IER_ILC_SHIFT 21
> +#define XCSI_IER_SPFIFOF_SHIFT 20
> +#define XCSI_IER_SPFIFONE_SHIFT 19
> +#define XCSI_IER_SLBF_SHIFT 18
> +#define XCSI_IER_STOP_SHIFT 17
> +#define XCSI_IER_SOTERR_SHIFT 13
> +#define XCSI_IER_SOTSYNCERR_SHIFT 12
> +#define XCSI_IER_ECC2BERR_SHIFT 11
> +#define XCSI_IER_ECC1BERR_SHIFT 10
> +#define XCSI_IER_CRCERR_SHIFT 9
> +#define XCSI_IER_DATAIDERR_SHIFT 8
> +#define XCSI_IER_VC3FSYNCERR_SHIFT 7
> +#define XCSI_IER_VC3FLVLERR_SHIFT 6
> +#define XCSI_IER_VC2FSYNCERR_SHIFT 5
> +#define XCSI_IER_VC2FLVLERR_SHIFT 4
> +#define XCSI_IER_VC1FSYNCERR_SHIFT 3
> +#define XCSI_IER_VC1FLVLERR_SHIFT 2
> +#define XCSI_IER_VC0FSYNCERR_SHIFT 1
> +#define XCSI_IER_VC0FLVLERR_SHIFT 0
> +#define XCSI_IER_FR_MASK BIT(XCSI_IER_FR_SHIFT)
> +#define XCSI_IER_ILC_MASK BIT(XCSI_IER_ILC_SHIFT)
> +#define XCSI_IER_SPFIFOF_MASK BIT(XCSI_IER_SPFIFOF_SHIFT)
> +#define XCSI_IER_SPFIFONE_MASK BIT(XCSI_IER_SPFIFONE_SHIFT)
> +#define XCSI_IER_SLBF_MASK BIT(XCSI_IER_SLBF_SHIFT)
> +#define XCSI_IER_STOP_MASK BIT(XCSI_IER_STOP_SHIFT)
> +#define XCSI_IER_SOTERR_MASK BIT(XCSI_IER_SOTERR_SHIFT)
> +#define XCSI_IER_SOTSYNCERR_MASK BIT(XCSI_IER_SOTSYNCERR_SHIFT)
> +#define XCSI_IER_ECC2BERR_MASK BIT(XCSI_IER_ECC2BERR_SHIFT)
> +#define XCSI_IER_ECC1BERR_MASK BIT(XCSI_IER_ECC1BERR_SHIFT)
> +#define XCSI_IER_CRCERR_MASK BIT(XCSI_IER_CRCERR_SHIFT)
> +#define XCSI_IER_DATAIDERR_MASK BIT(XCSI_IER_DATAIDERR_SHIFT)
> +#define XCSI_IER_VC3FSYNCERR_MASK BIT(XCSI_IER_VC3FSYNCERR_SHIFT)
> +#define XCSI_IER_VC3FLVLERR_MASK BIT(XCSI_IER_VC3FLVLERR_SHIFT)
> +#define XCSI_IER_VC2FSYNCERR_MASK BIT(XCSI_IER_VC2FSYNCERR_SHIFT)
> +#define XCSI_IER_VC2FLVLERR_MASK BIT(XCSI_IER_VC2FLVLERR_SHIFT)
> +#define XCSI_IER_VC1FSYNCERR_MASK BIT(XCSI_IER_VC1FSYNCERR_SHIFT)
> +#define XCSI_IER_VC1FLVLERR_MASK BIT(XCSI_IER_VC1FLVLERR_SHIFT)
> +#define XCSI_IER_VC0FSYNCERR_MASK BIT(XCSI_IER_VC0FSYNCERR_SHIFT)
> +#define XCSI_IER_VC0FLVLERR_MASK BIT(XCSI_IER_VC0FLVLERR_SHIFT)
> +#define XCSI_IER_ALLINTR_MASK 0x803FFFFF

This is simply repeatation of above, XCSI_ISR_*. One common one can be used.

> +
> +#define XCSI_SPKTR_OFFSET 0x00000030
> +#define XCSI_SPKTR_DATA_SHIFT 8
> +#define XCSI_SPKTR_VC_SHIFT 6
> +#define XCSI_SPKTR_DT_SHIFT 0
> +#define XCSI_SPKTR_DATA_MASK 0x00FFFF00
> +#define XCSI_SPKTR_VC_MASK 0x000000C0
> +#define XCSI_SPKTR_DT_MASK 0x0000003F

GENMASK().

> +
> +#define XCSI_CLKINFR_OFFSET 0x0000003C
> +#define XCSI_CLKINFR_STOP_SHIFT 1
> +#define XCSI_CLKINFR_STOP_MASK BIT(XCSI_CLKINFR_STOP_SHIFT)
> +
> +#define XCSI_L0INFR_OFFSET 0x00000040
> +#define XCSI_L1INFR_OFFSET 0x00000044
> +#define XCSI_L2INFR_OFFSET 0x00000048
> +#define XCSI_L3INFR_OFFSET 0x0000004C
> +#define XCSI_LXINFR_STOP_SHIFT 5
> +#define XCSI_LXINFR_SOTERR_SHIFT 1
> +#define XCSI_LXINFR_SOTSYNCERR_SHIFT 0
> +#define XCSI_LXINFR_STOP_MASK BIT(XCSI_LXINFR_STOP_SHIFT)
> +#define XCSI_LXINFR_SOTERR_MASK BIT(XCSI_LXINFR_SOTERR_SHIFT)
> +#define XCSI_LXINFR_SOTSYNCERR_MASK BIT(XCSI_LXINFR_SOTSYNCERR_SHIFT)
> +
> +#define XCSI_VC0INF1R_OFFSET 0x00000060
> +#define XCSI_VC1INF1R_OFFSET 0x00000068
> +#define XCSI_VC2INF1R_OFFSET 0x00000070
> +#define XCSI_VC3INF1R_OFFSET 0x00000078
> +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16
> +#define XCSI_VCXINF1R_BYTECOUNT_SHIFT 0
> +#define XCSI_VCXINF1R_LINECOUNT_MASK 0xFFFF0000
> +#define XCSI_VCXINF1R_BYTECOUNT_MASK 0x0000FFFF
> +
> +#define XCSI_VC0INF2R_OFFSET 0x00000064
> +#define XCSI_VC1INF2R_OFFSET 0x0000006C
> +#define XCSI_VC2INF2R_OFFSET 0x00000074
> +#define XCSI_VC3INF2R_OFFSET 0x0000007C
> +#define XCSI_VCXINF2R_DATATYPE_SHIFT 0
> +#define XCSI_VCXINF2R_DATATYPE_MASK 0x0000003F
> +
> +#define XDPHY_CTRLREG_OFFSET 0x0
> +#define XDPHY_CTRLREG_DPHYEN_SHIFT 1
> +#define XDPHY_CTRLREG_DPHYEN_MASK BIT(XDPHY_CTRLREG_DPHYEN_SHIFT)
> +
> +#define XDPHY_CLKSTATREG_OFFSET 0x18
> +#define XDPHY_CLKSTATREG_MODE_SHIFT 0
> +#define XDPHY_CLKSTATREG_MODE_MASK 0x3
> +#define XDPHY_LOW_POWER_MODE 0x0
> +#define XDPHY_HI_SPEED_MODE 0x1
> +#define XDPHY_ESC_MODE 0x2

Can d-phy be used for dsi or as a separate IP, so shoudln't it be
a separate phy driver?

> +
> +/*
> + * Interrupt mask
> + */
> +#define XCSI_INTR_MASK (XCSI_ISR_ALLINTR_MASK & ~XCSI_ISR_STOP_MASK)

This can be grouped with interrupt register definitions.

> +/*
> + * Timeout for reset
> + */
> +#define XCSI_TIMEOUT_VAL (1000) /* us */

Where do we get this value from? If not specified in any spec, this deserves
a comment how this value is derived.

> +
> +/*
> + * Max string length for CSI Data type string
> + */
> +#define MAX_XIL_CSIDT_STR_LENGTH 64
> +
> +/*
> + * Maximum number of short packet events per file handle.
> + */
> +#define XCSI_MAX_SPKT (512)

Could you please explain how frequently the short packets can arrive?
'512' seems somewhat large, so some explanation may help.

> +
> +/* Number of media pads */
> +#define XILINX_CSI_MEDIA_PADS (2)
> +
> +#define XCSI_DEFAULT_WIDTH (1920)
> +#define XCSI_DEFAULT_HEIGHT (1080)
> +
> +/*
> + * Macro to return "true" or "false" string if bit is set
> + */
> +#define XCSI_GET_BITSET_STR(val, mask) (val) & (mask) ? "true" : "false"
> +
> +enum csi_datatypes {
> + MIPI_CSI_DT_FRAME_START_CODE = 0x00,
> + MIPI_CSI_DT_FRAME_END_CODE,
> + MIPI_CSI_DT_LINE_START_CODE,
> + MIPI_CSI_DT_LINE_END_CODE,
> + MIPI_CSI_DT_SYNC_RSVD_04,
> + MIPI_CSI_DT_SYNC_RSVD_05,
> + MIPI_CSI_DT_SYNC_RSVD_06,
> + MIPI_CSI_DT_SYNC_RSVD_07,
> + MIPI_CSI_DT_GSPKT_08,
> + MIPI_CSI_DT_GSPKT_09,
> + MIPI_CSI_DT_GSPKT_0A,
> + MIPI_CSI_DT_GSPKT_0B,
> + MIPI_CSI_DT_GSPKT_0C,
> + MIPI_CSI_DT_GSPKT_0D,
> + MIPI_CSI_DT_GSPKT_0E,
> + MIPI_CSI_DT_GSPKT_0F,
> + MIPI_CSI_DT_GLPKT_10,
> + MIPI_CSI_DT_GLPKT_11,
> + MIPI_CSI_DT_GLPKT_12,
> + MIPI_CSI_DT_GLPKT_13,
> + MIPI_CSI_DT_GLPKT_14,
> + MIPI_CSI_DT_GLPKT_15,
> + MIPI_CSI_DT_GLPKT_16,
> + MIPI_CSI_DT_GLPKT_17,
> + MIPI_CSI_DT_YUV_420_8B,
> + MIPI_CSI_DT_YUV_420_10B,
> + MIPI_CSI_DT_YUV_420_8B_LEGACY,
> + MIPI_CSI_DT_YUV_RSVD,
> + MIPI_CSI_DT_YUV_420_8B_CSPS,
> + MIPI_CSI_DT_YUV_420_10B_CSPS,
> + MIPI_CSI_DT_YUV_422_8B,
> + MIPI_CSI_DT_YUV_422_10B,
> + MIPI_CSI_DT_RGB_444,
> + MIPI_CSI_DT_RGB_555,
> + MIPI_CSI_DT_RGB_565,
> + MIPI_CSI_DT_RGB_666,
> + MIPI_CSI_DT_RGB_888,
> + MIPI_CSI_DT_RGB_RSVD_25,
> + MIPI_CSI_DT_RGB_RSVD_26,
> + MIPI_CSI_DT_RGB_RSVD_27,
> + MIPI_CSI_DT_RAW_6,
> + MIPI_CSI_DT_RAW_7,
> + MIPI_CSI_DT_RAW_8,
> + MIPI_CSI_DT_RAW_10,
> + MIPI_CSI_DT_RAW_12,
> + MIPI_CSI_DT_RAW_14,
> + MIPI_CSI_DT_RAW_RSVD_2E,
> + MIPI_CSI_DT_RAW_RSVD_2F,
> + MIPI_CSI_DT_USER_30,
> + MIPI_CSI_DT_USER_31,
> + MIPI_CSI_DT_USER_32,
> + MIPI_CSI_DT_USER_33,
> + MIPI_CSI_DT_USER_34,
> + MIPI_CSI_DT_USER_35,
> + MIPI_CSI_DT_USER_36,
> + MIPI_CSI_DT_USER_37,
> + MIPI_CSI_DT_RSVD_38,
> + MIPI_CSI_DT_RSVD_39,
> + MIPI_CSI_DT_RSVD_3A,
> + MIPI_CSI_DT_RSVD_3B,
> + MIPI_CSI_DT_RSVD_3C,
> + MIPI_CSI_DT_RSVD_3D,
> + MIPI_CSI_DT_RSVD_3E,
> + MIPI_CSI_DT_RSVD_3F
> +};

This seems to map to possible values for XCSI_VC*INF1R_OFFSET.
Then I'm not sure if enum is right type. You can have this under
the register definition. But up to you.

> +
> +/**
> + * struct pixel_format - Data Type to string name structure
> + * @pixelformat: MIPI CSI2 Data type
> + * @pixelformatstr: String name of Data Type
> + */
> +struct pixel_format {
> + enum csi_datatypes pixelformat;
> + char pixelformatstr[MAX_XIL_CSIDT_STR_LENGTH];
> +};
> +
> +/**
> + * struct xcsi2rxss_event - Event log structure
> + * @mask: Event mask
> + * @name: Name of the event
> + * @counter: Count number of events
> + */
> +struct xcsi2rxss_event {
> + u32 mask;
> + const char * const name;

Nit. Extra space.

> + unsigned int counter;
> +};
> +
> +/*
> + * struct xcsi2rxss_core - Core configuration CSI2 Rx Subsystem device structure
> + * @dev: Platform structure

Incorrect description.

> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @dphy_present: Flag for DPHY register interface presence
> + * @dphy_offset: DPHY registers offset
> + * @enable_active_lanes: If number of active lanes can be modified
> + * @max_num_lanes: Maximum number of lanes present
> + * @vfb: Video Format Bridge enabled or not
> + * @ppc: pixels per clock
> + * @vc: Virtual Channel
> + * @axis_tdata_width: AXI Stream data width
> + * @datatype: Data type filter
> + * @pxlformat: String with CSI pixel format from IP
> + * @num_lanes: Number of lanes requested from application
> + * @events: Structure to maintain event logs
> + */
> +struct xcsi2rxss_core {
> + struct device *dev;
> + void __iomem *iomem;
> + int irq;
> + u32 dphy_offset;

Is it guaranteed that the dphy register can be addressed with offset to
the csi register?

> + bool dphy_present;
> + bool enable_active_lanes;
> + u32 max_num_lanes;
> + bool vfb;
> + u32 ppc;

This is parsed but not used.

> + u32 vc;
> + u32 axis_tdata_width;

This is not used. Do you want to keep it in sync with IP configs?

> + u32 datatype;

Isn't this enum csi_datatypes?

> + const char *pxlformat;
> + u32 num_lanes;
> + struct xcsi2rxss_event *events;
> +};
> +
> +/**
> + * struct xcsi2rxss_state - CSI2 Rx Subsystem device structure
> + * @core: Core structure for MIPI CSI2 Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @vip_format: format information corresponding to the active format
> + * @event: Holds the short packet event
> + * @lock: mutex for serializing operations
> + * @pads: media pads
> + * @npads: number of pads
> + * @streaming: Flag for storing streaming state
> + * @suspended: Flag for storing suspended state
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xcsi2rxss_state {
> + struct xcsi2rxss_core core;
> + struct v4l2_subdev subdev;
> + struct v4l2_ctrl_handler ctrl_handler;
> + struct v4l2_mbus_framefmt formats[2];
> + struct v4l2_mbus_framefmt default_format;
> + const struct xvip_video_format *vip_format;
> + struct v4l2_event event;
> + /*
> + * Used to serialize access.
> + */

It's a little not clear what access. It would be better to describe
what the lock protects.

> + struct mutex lock;
> + struct media_pad pads[XILINX_CSI_MEDIA_PADS];
> + unsigned int npads;
> + bool streaming;
> + bool suspended;
> +};
> +
> +static inline struct xcsi2rxss_state *
> +to_xcsi2rxssstate(struct v4l2_subdev *subdev)
> +{
> + return container_of(subdev, struct xcsi2rxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xcsi2rxss_read(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr)
> +{
> + return ioread32(xcsi2rxss->iomem + addr);
> +}
> +
> +static inline void xcsi2rxss_write(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr, u32 value)
> +{
> + iowrite32(value, xcsi2rxss->iomem + addr);
> +}
> +
> +static inline void xcsi2rxss_clr(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr, u32 clr)
> +{
> + xcsi2rxss_write(xcsi2rxss,
> + addr,

I'd put this in the line above. But maybe it's just me.

> + xcsi2rxss_read(xcsi2rxss, addr) & ~clr);
> +}
> +
> +static inline void xcsi2rxss_set(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr, u32 set)
> +{
> + xcsi2rxss_write(xcsi2rxss,
> + addr,
> + xcsi2rxss_read(xcsi2rxss, addr) | set);
> +}
> +
> +static const struct pixel_format pixel_formats[] = {
> + { MIPI_CSI_DT_YUV_420_8B, "YUV420_8bit" },
> + { MIPI_CSI_DT_YUV_420_10B, "YUV420_10bit" },
> + { MIPI_CSI_DT_YUV_420_8B_LEGACY, "Legacy_YUV420_8bit" },
> + { MIPI_CSI_DT_YUV_420_8B_CSPS, "YUV420_8bit_CSPS" },
> + { MIPI_CSI_DT_YUV_420_10B_CSPS, "YUV420_10bit_CSPS" },
> + { MIPI_CSI_DT_YUV_422_8B, "YUV422_8bit" },
> + { MIPI_CSI_DT_YUV_422_10B, "YUV422_10bit" },
> + { MIPI_CSI_DT_RGB_444, "RGB444" },
> + { MIPI_CSI_DT_RGB_555, "RGB555" },
> + { MIPI_CSI_DT_RGB_565, "RGB565" },
> + { MIPI_CSI_DT_RGB_666, "RGB666" },
> + { MIPI_CSI_DT_RGB_888, "RGB888" },
> + { MIPI_CSI_DT_RAW_6, "RAW6" },
> + { MIPI_CSI_DT_RAW_7, "RAW7" },
> + { MIPI_CSI_DT_RAW_8, "RAW8" },
> + { MIPI_CSI_DT_RAW_10, "RAW10" },
> + { MIPI_CSI_DT_RAW_12, "RAW12" },
> + { MIPI_CSI_DT_RAW_14, "RAW14 "}
> +};
> +
> +static struct xcsi2rxss_event xcsi2rxss_events[] = {
> + { XCSI_ISR_FR_MASK, "Frame Received", 0 },
> + { XCSI_ISR_ILC_MASK, "Invalid Lane Count Error", 0 },
> + { XCSI_ISR_SPFIFOF_MASK, "Short Packet FIFO OverFlow Error", 0 },
> + { XCSI_ISR_SPFIFONE_MASK, "Short Packet FIFO Not Empty", 0 },
> + { XCSI_ISR_SLBF_MASK, "Streamline Buffer Full Error", 0 },
> + { XCSI_ISR_STOP_MASK, "Lane Stop State", 0 },
> + { XCSI_ISR_SOTERR_MASK, "SOT Error", 0 },
> + { XCSI_ISR_SOTSYNCERR_MASK, "SOT Sync Error", 0 },
> + { XCSI_ISR_ECC2BERR_MASK, "2 Bit ECC Unrecoverable Error", 0 },
> + { XCSI_ISR_ECC1BERR_MASK, "1 Bit ECC Recoverable Error", 0 },
> + { XCSI_ISR_CRCERR_MASK, "CRC Error", 0 },
> + { XCSI_ISR_DATAIDERR_MASK, "Data Id Error", 0 },
> + { XCSI_ISR_VC3FSYNCERR_MASK, "Virtual Channel 3 Frame Sync Error", 0 },
> + { XCSI_ISR_VC3FLVLERR_MASK, "Virtual Channel 3 Frame Level Error", 0 },
> + { XCSI_ISR_VC2FSYNCERR_MASK, "Virtual Channel 2 Frame Sync Error", 0 },
> + { XCSI_ISR_VC2FLVLERR_MASK, "Virtual Channel 2 Frame Level Error", 0 },
> + { XCSI_ISR_VC1FSYNCERR_MASK, "Virtual Channel 1 Frame Sync Error", 0 },
> + { XCSI_ISR_VC1FLVLERR_MASK, "Virtual Channel 1 Frame Level Error", 0 },
> + { XCSI_ISR_VC0FSYNCERR_MASK, "Virtual Channel 0 Frame Sync Error", 0 },
> + { XCSI_ISR_VC0FLVLERR_MASK, "Virtual Channel 0 Frame Level Error", 0 }
> +};
> +
> +#define XMIPICSISS_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events)
> +
> +/**
> + * xcsi2rxss_clr_and_set - Clear and set the register with a bitmask
> + * @xcsi2rxss: Xilinx MIPI CSI2 Rx Subsystem subdev core struct
> + * @addr: address of register
> + * @clr: bitmask to be cleared
> + * @set: bitmask to be set
> + *
> + * Clear a bit(s) of mask @clr in the register at address @addr, then set
> + * a bit(s) of mask @set in the register after.
> + */
> +static void xcsi2rxss_clr_and_set(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr, u32 clr, u32 set)
> +{
> + u32 reg;
> +
> + reg = xcsi2rxss_read(xcsi2rxss, addr);
> + reg &= ~clr;
> + reg |= set;
> + xcsi2rxss_write(xcsi2rxss, addr, reg);
> +}

This can be grouped with io operations above.

> +
> +/**
> + * xcsi2rxss_pxlfmtstrtodt - Convert pixel format string got from dts
> + * to data type.
> + * @pxlfmtstr: String obtained while parsing device node
> + *
> + * This function takes a CSI pixel format string obtained while parsing
> + * device tree node and converts it to data type.
> + *
> + * Eg. "RAW8" string is converted to 0x2A.
> + * Refer to MIPI CSI2 specification for details.
> + *
> + * Return: Equivalent pixel format value from table
> + */
> +static u32 xcsi2rxss_pxlfmtstrtodt(const char *pxlfmtstr)

Isn't this return type enum csi_datatypes?

> +{
> + u32 i;
> + u32 maxentries = ARRAY_SIZE(pixel_formats);

You can inline ARRAY_SIZE. Up to you.

> +
> + for (i = 0; i < maxentries; i++) {
> + if (!strncmp(pixel_formats[i].pixelformatstr,
> + pxlfmtstr, MAX_XIL_CSIDT_STR_LENGTH))
> + return pixel_formats[i].pixelformat;
> + }
> +
> + return -EINVAL;

The return type is unsigned.

> +}
> +
> +/**
> + * xcsi2rxss_pxlfmtdttostr - Convert pixel format data type to string.
> + * @datatype: MIPI CSI-2 Data Type
> + *
> + * This function takes a CSI pixel format data type and returns a
> + * pointer to the string name.
> + *
> + * Eg. 0x2A returns "RAW8" string.
> + * Refer to MIPI CSI2 specification for details.
> + *
> + * Return: Equivalent pixel format string from table
> + */
> +static const char *xcsi2rxss_pxlfmtdttostr(u32 datatype)

Isn't this argument enum csi_datatypes?

> +{
> + u32 i;
> + u32 maxentries = ARRAY_SIZE(pixel_formats);
> +
> + for (i = 0; i < maxentries; i++) {
> + if (pixel_formats[i].pixelformat == datatype)
> + return pixel_formats[i].pixelformatstr;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * xcsi2rxss_enable - Enable or disable the CSI Core
> + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> + * @flag: true for enabling, false for disabling
> + *
> + * This function enables/disables the MIPI CSI2 Rx Subsystem core.
> + * After enabling the CSI2 Rx core, the DPHY is enabled in case the
> + * register interface for it is present.
> + */
> +static void xcsi2rxss_enable(struct xcsi2rxss_core *core, bool flag)
> +{
> + u32 dphyctrlregoffset = core->dphy_offset + XDPHY_CTRLREG_OFFSET;
> +
> + if (flag) {
> + xcsi2rxss_write(core, XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
> + if (core->dphy_present)
> + xcsi2rxss_write(core, dphyctrlregoffset,
> + XDPHY_CTRLREG_DPHYEN_MASK);
> + } else {
> + xcsi2rxss_write(core, XCSI_CCR_OFFSET, 0);
> + if (core->dphy_present)
> + xcsi2rxss_write(core, dphyctrlregoffset, 0);
> + }
> +}
> +
> +/**
> + * xcsi2rxss_interrupts_enable - Enable or disable CSI interrupts
> + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> + * @flag: true for enabling, false for disabling
> + *
> + * This function enables/disables the interrupts for the MIPI CSI2
> + * Rx Subsystem.
> + */
> +static void xcsi2rxss_interrupts_enable(struct xcsi2rxss_core *core, bool flag)
> +{
> + if (flag) {
> + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> + xcsi2rxss_write(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
> + xcsi2rxss_set(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> + } else {
> + xcsi2rxss_clr(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
> + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> + }
> +}

It would be cleaner if enable / disable functions are split. But up to you.

> +
> +/**
> + * xcsi2rxss_reset - Does a soft reset of the MIPI CSI2 Rx Subsystem
> + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> + *
> + * Return: 0 - on success OR -ETIME if reset times out

Wouldn't -ETIMEDOUT be better for this? Up to you.

> + */
> +static int xcsi2rxss_reset(struct xcsi2rxss_core *core)
> +{
> + u32 timeout = XCSI_TIMEOUT_VAL;
> +
> + xcsi2rxss_set(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
> +
> + while (xcsi2rxss_read(core, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD_MASK) {
> + if (timeout == 0) {
> + dev_err(core->dev, "Xilinx CSI2 Rx Subsystem Soft Reset Timeout!\n");
> + return -ETIME;
> + }
> +
> + timeout--;
> + udelay(1);
> + }
> +
> + xcsi2rxss_clr(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
> + return 0;
> +}
> +
> +/**
> + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * In the interrupt handler, a list of event counters are updated for
> + * corresponding interrupts. This is useful to get status / debug.
> + * If the short packet FIFO not empty or overflow interrupt is received
> + * capture the short packet and notify of event occurrence
> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *dev_id)
> +{
> + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)dev_id;
> + struct xcsi2rxss_core *core = &state->core;
> + u32 status;
> +
> + status = xcsi2rxss_read(core, XCSI_ISR_OFFSET) & XCSI_INTR_MASK;
> + dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> + if (!status)
> + return IRQ_NONE;
> +
> + if (status & XCSI_ISR_SPFIFONE_MASK) {
> + memset(&state->event, 0, sizeof(state->event));
> +
> + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT;
> +
> + *((u32 *)(&state->event.u.data)) =
> + xcsi2rxss_read(core, XCSI_SPKTR_OFFSET);
> +
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }
> +
> + if (status & XCSI_ISR_SPFIFOF_MASK) {
> + dev_alert(core->dev, "Short packet FIFO overflowed\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> +
> + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT_OVF;
> +
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }
> +
> + if (status & XCSI_ISR_SLBF_MASK) {
> + dev_alert(core->dev, "Stream Line Buffer Full!\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> +
> + state->event.type = V4L2_EVENT_XLNXCSIRX_SLBF;
> +
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }
> +
> + if (status & XCSI_ISR_ALLINTR_MASK) {
> + unsigned int i;
> +
> + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
> + if (!(status & core->events[i].mask))
> + continue;
> + core->events[i].counter++;
> + dev_dbg(core->dev, "%s: %d\n", core->events[i].name,
> + core->events[i].counter);
> + }
> + }
> +
> + xcsi2rxss_write(core, XCSI_ISR_OFFSET, status);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state)
> +{
> + u32 i;
> +
> + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++)
> + state->core.events[i].counter = 0;
> +}
> +
> +/**
> + * xcsi2rxss_log_counters - Print out the event counters.
> + * @state: Pointer to device state
> + *
> + */
> +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state)
> +{
> + u32 i;
> +
> + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
> + if (state->core.events[i].counter > 0)
> + v4l2_info(&state->subdev, "%s events: %d\n",
> + state->core.events[i].name,
> + state->core.events[i].counter);
> + }
> +}
> +
> +/**
> + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx MIPI CSI-2
> + *
> + * Return: 0 on success
> + */
> +static int xcsi2rxss_log_status(struct v4l2_subdev *sd)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> + u32 reg, data, i;
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + xcsi2rxss_log_counters(xcsi2rxss);
> +
> + v4l2_info(sd, "***** Core Status *****\n");
> + data = xcsi2rxss_read(core, XCSI_CSR_OFFSET);
> + v4l2_info(sd, "Short Packet FIFO Full = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFOFULL_MASK));
> + v4l2_info(sd, "Short Packet FIFO Not Empty = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFONE_MASK));
> + v4l2_info(sd, "Stream line buffer full = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CSR_SLBF_MASK));
> + v4l2_info(sd, "Soft reset/Core disable in progress = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CSR_RIPCD_MASK));
> +
> + /* Clk & Lane Info */
> + v4l2_info(sd, "******** Clock Lane Info *********\n");
> + data = xcsi2rxss_read(core, XCSI_CLKINFR_OFFSET);
> + v4l2_info(sd, "Clock Lane in Stop State = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CLKINFR_STOP_MASK));
> +
> + v4l2_info(sd, "******** Data Lane Info *********\n");
> + v4l2_info(sd, "Lane\tSoT Error\tSoT Sync Error\tStop State\n");
> + reg = XCSI_L0INFR_OFFSET;
> + for (i = 0; i < 4; i++) {
> + data = xcsi2rxss_read(core, reg);
> +
> + v4l2_info(sd, "%d\t%s\t\t%s\t\t%s\n",
> + i,
> + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_SOTERR_MASK),
> + data & XCSI_LXINFR_SOTSYNCERR_MASK ? "true" : "false",

XCSI_GET_BITSET_STR()?

> + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_STOP_MASK));
> +
> + reg += 4;
> + }
> +
> + /* Virtual Channel Image Information */
> + v4l2_info(sd, "********** Virtual Channel Info ************\n");
> + v4l2_info(sd, "VC\tLine Count\tByte Count\tData Type\n");
> + reg = XCSI_VC0INF1R_OFFSET;
> + for (i = 0; i < 4; i++) {
> + u32 line_count, byte_count, data_type;
> + char *datatypestr;
> +
> + /* Get line and byte count from VCXINFR1 Register */
> + data = xcsi2rxss_read(core, reg);
> + byte_count = (data & XCSI_VCXINF1R_BYTECOUNT_MASK) >>
> + XCSI_VCXINF1R_BYTECOUNT_SHIFT;
> + line_count = (data & XCSI_VCXINF1R_LINECOUNT_MASK) >>
> + XCSI_VCXINF1R_LINECOUNT_SHIFT;
> +
> + /* Get data type from VCXINFR2 Register */
> + reg += 4;
> + data = xcsi2rxss_read(core, reg);
> + data_type = (data & XCSI_VCXINF2R_DATATYPE_MASK) >>
> + XCSI_VCXINF2R_DATATYPE_SHIFT;
> + datatypestr = (char *)xcsi2rxss_pxlfmtdttostr(data_type);
> +
> + v4l2_info(sd, "%d\t%d\t\t%d\t\t%s\n",
> + i, line_count, byte_count, datatypestr);
> +
> + /* Move to next pair of VC Info registers */
> + reg += 4;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +/*
> + * xcsi2rxss_subscribe_event - Subscribe to the custom short packet
> + * receive event.
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * There are two types of events to be subscribed.
> + *
> + * First is to register for receiving a short packet.
> + * The short packets received are queued up in a FIFO.
> + * On reception of a short packet, an event will be generated
> + * with the short packet contents copied to its data area.
> + * Application subscribed to this event will poll for POLLPRI.
> + * On getting the event, the app dequeues the event to get the short packet
> + * data.
> + *
> + * Second is to register for Short packet FIFO overflow
> + * In case the rate of receiving short packets is high and
> + * the short packet FIFO overflows, this event will be triggered.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xcsi2rxss_subscribe_event(struct v4l2_subdev *sd,
> + struct v4l2_fh *fh,
> + struct v4l2_event_subscription *sub)
> +{
> + int ret;
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + switch (sub->type) {
> + case V4L2_EVENT_XLNXCSIRX_SPKT:
> + case V4L2_EVENT_XLNXCSIRX_SPKT_OVF:
> + case V4L2_EVENT_XLNXCSIRX_SLBF:
> + ret = v4l2_event_subscribe(fh, sub, XCSI_MAX_SPKT, NULL);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +/**
> + * xcsi2rxss_unsubscribe_event - Unsubscribe from all events registered
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 file handle
> + * @sub: pointer to Event unsubscription structure
> + *
> + * Return: zero on success, else a negative error code.
> + */
> +static int xcsi2rxss_unsubscribe_event(struct v4l2_subdev *sd,
> + struct v4l2_fh *fh,
> + struct v4l2_event_subscription *sub)
> +{
> + int ret = 0;

No need to initialize.

> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + mutex_lock(&xcsi2rxss->lock);
> + ret = v4l2_event_unsubscribe(fh, sub);
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +/**
> + * xcsi2rxss_s_ctrl - This is used to set the Xilinx MIPI CSI-2 V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx MIPI
> + * CSI-2 Rx Subsystem. It is used to set the active lanes in the system.
> + * The event counters can be reset.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xcsi2rxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + int ret = 0;
> + u32 timeout = XCSI_TIMEOUT_VAL;
> + u32 active_lanes = 1;
> +
> + struct xcsi2rxss_state *xcsi2rxss =
> + container_of(ctrl->handler,
> + struct xcsi2rxss_state, ctrl_handler);
> + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + switch (ctrl->id) {
> + case V4L2_CID_XILINX_MIPICSISS_ACT_LANES:
> + /*
> + * This will be called only when "Enable Active Lanes" parameter
> + * is set in design
> + */

Then shouldn't it be checked here?

> + xcsi2rxss_clr_and_set(core, XCSI_PCR_OFFSET,
> + XCSI_PCR_ACTLANES_MASK, ctrl->val - 1);
> +
> + /*
> + * If the core is enabled, wait for active lanes to be
> + * set.
> + *
> + * If core is disabled or there is no clock from DPHY Tx
> + * then the read back won't reflect the updated value
> + * as the PPI clock will not be present.
> + */
> +
> + if (core->dphy_present) {
> + u32 dphyclkstatregoffset = core->dphy_offset +
> + XDPHY_CLKSTATREG_OFFSET;
> +
> + u32 dphyclkstat =
> + xcsi2rxss_read(core, dphyclkstatregoffset) &
> + XDPHY_CLKSTATREG_MODE_MASK;
> +
> + u32 coreenable =
> + xcsi2rxss_read(core, XCSI_CCR_OFFSET) &
> + XCSI_CCR_COREENB_MASK;
> +
> + char lpmstr[] = "Low Power";
> + char hsmstr[] = "High Speed";
> + char esmstr[] = "Escape";

These can be removed and inlined directly.

> + char *modestr;
> +
> + switch (dphyclkstat) {
> + case 0:
> + modestr = lpmstr;
> + break;
> + case 1:
> + modestr = hsmstr;
> + break;
> + case 2:
> + modestr = esmstr;
> + break;
> + default:
> + modestr = NULL;
> + break;
> + }
> +
> + dev_dbg(core->dev, "DPHY Clock Lane in %s mode\n",
> + modestr);

I'd do an array of strings, and do modestr[dphyclkstat]. But the dphyclkstate
should be checked to be in valid range

> +
> + if (dphyclkstat == XDPHY_HI_SPEED_MODE &&
> + coreenable) {
> + /* Wait for core to apply new active lanes */
> + while (timeout--)
> + udelay(1);

udelay(1000). Please specify where the delay is defined or from
some heuristics.

> +
> + active_lanes =
> + xcsi2rxss_read(core, XCSI_PCR_OFFSET);
> + active_lanes &= XCSI_PCR_ACTLANES_MASK;
> + active_lanes++;
> +
> + if (active_lanes != (u32)ctrl->val) {
> + dev_err(core->dev, "Failed to set active lanes!\n");
> + ret = -EAGAIN;
> + }
> + }
> + } else {
> + dev_dbg(core->dev, "No read back as no DPHY present.\n");

This should be more verbose level, ex info.

> + }
> +
> + dev_dbg(core->dev, "Set active lanes: requested = %d, active = %d\n",
> + ctrl->val, active_lanes);
> + break;
> + case V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS:
> + xcsi2rxss_reset_event_counters(xcsi2rxss);
> + break;
> + default:
> + break;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +/**
> + * xcsi2rxss_g_volatile_ctrl - get the Xilinx MIPI CSI-2 Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * This is used to get the number of frames received by the Xilinx
> + * MIPI CSI-2 Rx.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xcsi2rxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + int ret = 0;
> + struct xcsi2rxss_state *xcsi2rxss =
> + container_of(ctrl->handler,
> + struct xcsi2rxss_state, ctrl_handler);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + switch (ctrl->id) {
> + case V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER:
> + ctrl->val = xcsi2rxss->core.events[0].counter;
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *xcsi2rxss)
> +{
> + int ret;
> +
> + xcsi2rxss_enable(&xcsi2rxss->core, true);
> +
> + ret = xcsi2rxss_reset(&xcsi2rxss->core);
> + if (ret < 0)
> + return ret;
> +
> + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, true);
> +
> + return 0;
> +}
> +
> +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *xcsi2rxss)
> +{
> + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, false);
> + xcsi2rxss_enable(&xcsi2rxss->core, false);
> +}
> +
> +/**
> + * xcsi2rxss_s_stream - It is used to start/stop the streaming.
> + * @sd: V4L2 Sub device
> + * @enable: Flag (True / False)
> + *
> + * This function controls the start or stop of streaming for the
> + * Xilinx MIPI CSI-2 Rx Subsystem provided the device isn't in
> + * suspended state.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + int ret = 0;
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + if (xcsi2rxss->suspended) {
> + ret = -EBUSY;
> + goto unlock;
> + }
> +
> + if (enable) {
> + if (!xcsi2rxss->streaming) {
> + /* reset the event counters */
> + xcsi2rxss_reset_event_counters(xcsi2rxss);
> +
> + ret = xcsi2rxss_start_stream(xcsi2rxss);
> + if (ret == 0)
> + xcsi2rxss->streaming = true;


Checking and Setting this 'streaming' flag in xcsi2rxss_start_stream()
would make more sense.

> + }
> + } else {
> + if (xcsi2rxss->streaming) {
> + xcsi2rxss_stop_stream(xcsi2rxss);
> + xcsi2rxss->streaming = false;
> + }
> + }
> +unlock:
> + mutex_unlock(&xcsi2rxss->lock);
> + return ret;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss,
> + struct v4l2_subdev_pad_config *cfg,
> + unsigned int pad, u32 which)
> +{
> + switch (which) {
> + case V4L2_SUBDEV_FORMAT_TRY:
> + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad);
> + case V4L2_SUBDEV_FORMAT_ACTIVE:
> + return &xcsi2rxss->formats[pad];
> + default:
> + return NULL;
> + }
> +}
> +
> +/**
> + * xcsi2rxss_get_format - Get the pad format
> + * @sd: Pointer to V4L2 Sub device structure
> + * @cfg: Pointer to sub device pad information structure
> + * @fmt: Pointer to pad level media bus format
> + *
> + * This function is used to get the pad format information.
> + *
> + * Return: 0 on success
> + */
> +static int xcsi2rxss_get_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + mutex_lock(&xcsi2rxss->lock);
> + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
> + fmt->pad, fmt->which);
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +/**
> + * xcsi2rxss_set_format - This is used to set the pad format
> + * @sd: Pointer to V4L2 Sub device structure
> + * @cfg: Pointer to sub device pad information structure
> + * @fmt: Pointer to pad level media bus format
> + *
> + * This function is used to set the pad format.
> + * Since the pad format is fixed in hardware, it can't be
> + * modified on run time. So when a format set is requested by
> + * application, all parameters except the format type is
> + * saved for the pad and the original pad format is sent
> + * back to the application.

You can have a little more characters per line. :-)

> + *
> + * Return: 0 on success
> + */
> +static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_mbus_framefmt *__format;
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> + u32 code;
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + /*
> + * Only the format->code parameter matters for CSI as the
> + * CSI format cannot be changed at runtime.
> + * Ensure that format to set is copied to over to CSI pad format
> + */
> + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
> + fmt->pad, fmt->which);
> +
> + /* Save the pad format code */
> + code = __format->code;
> +
> + /* If the bayer pattern to be set is SXXXX8 then only 1x8 type
> + * is supported and core's data type doesn't matter.
> + * In case the bayer pattern being set is SXXX10 then only
> + * 1x10 type are supported and core should be configured for RAW10.
> + * In case the bayer pattern being set is SXXX12 then only
> + * 1x12 type are supported and core should be configured for RAW12.
> + *
> + * Otherwise don't allow change.
> + */

Nit. Mutiline comment style.

> + if ((fmt->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGBRG8_1X8 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGRBG8_1X8 ||
> + fmt->format.code == MEDIA_BUS_FMT_SRGGB8_1X8) ||
> + (core->datatype == MIPI_CSI_DT_RAW_10 &&
> + (fmt->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGBRG10_1X10 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGRBG10_1X10 ||
> + fmt->format.code == MEDIA_BUS_FMT_SRGGB10_1X10)) ||
> + (core->datatype == MIPI_CSI_DT_RAW_12 &&
> + (fmt->format.code == MEDIA_BUS_FMT_SBGGR12_1X12 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGBRG12_1X12 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGRBG12_1X12 ||
> + fmt->format.code == MEDIA_BUS_FMT_SRGGB12_1X12))) {
> + /* Copy over the format to be set */
> + *__format = fmt->format;
> + } else {
> + /* Restore the original pad format code */
> + fmt->format.code = code;
> + __format->code = code;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +/**
> + * xcsi2rxss_open - Called on v4l2_open()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_open(). It sets the default format
> + * for both pads.
> + *
> + * Return: 0 on success
> + */
> +static int xcsi2rxss_open(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh)
> +{
> + struct v4l2_mbus_framefmt *format;
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> + *format = xcsi2rxss->default_format;
> +
> + format = v4l2_subdev_get_try_format(sd, fh->pad, 1);
> + *format = xcsi2rxss->default_format;
> +
> + return 0;
> +}
> +
> +static int xcsi2rxss_close(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh)
> +{
> + return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xcsi2rxss_media_ops = {
> + .link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xcsi2rxss_ctrl_ops = {
> + .g_volatile_ctrl = xcsi2rxss_g_volatile_ctrl,
> + .s_ctrl = xcsi2rxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xcsi2rxss_ctrls[] = {
> + {
> + .ops = &xcsi2rxss_ctrl_ops,
> + .id = V4L2_CID_XILINX_MIPICSISS_ACT_LANES,
> + .name = "MIPI CSI2 Rx Subsystem: Active Lanes",
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .min = 1,
> + .max = 4,
> + .step = 1,
> + .def = 1,
> + }, {
> + .ops = &xcsi2rxss_ctrl_ops,
> + .id = V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER,
> + .name = "MIPI CSI2 Rx Subsystem: Frames Received Counter",
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .min = 0,
> + .max = 0xFFFFFFFF,
> + .step = 1,
> + .def = 0,
> + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> + }, {
> + .ops = &xcsi2rxss_ctrl_ops,
> + .id = V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS,
> + .name = "MIPI CSI2 Rx Subsystem: Reset Counters",
> + .type = V4L2_CTRL_TYPE_BUTTON,
> + .min = 0,
> + .max = 1,
> + .step = 1,
> + .def = 0,
> + .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
> + }
> +};
> +
> +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = {
> + .log_status = xcsi2rxss_log_status,
> + .subscribe_event = xcsi2rxss_subscribe_event,
> + .unsubscribe_event = xcsi2rxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xcsi2rxss_video_ops = {
> + .s_stream = xcsi2rxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = {
> + .get_fmt = xcsi2rxss_get_format,
> + .set_fmt = xcsi2rxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xcsi2rxss_ops = {
> + .core = &xcsi2rxss_core_ops,
> + .video = &xcsi2rxss_video_ops,
> + .pad = &xcsi2rxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = {
> + .open = xcsi2rxss_open,
> + .close = xcsi2rxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Power Management
> + */
> +
> +/*
> + * xcsi2rxss_pm_suspend - Function called on Power Suspend
> + * @dev: Pointer to device structure
> + *
> + * On power suspend the CSI-2 Core is disabled if the device isn't
> + * in suspended state and is streaming.
> + *
> + * Return: 0 on success
> + */
> +static int __maybe_unused xcsi2rxss_pm_suspend(struct device *dev)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + if (!xcsi2rxss->suspended && xcsi2rxss->streaming)
> + xcsi2rxss_clr(&xcsi2rxss->core,
> + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);

Isn't it needed to store and restore the register values? The registers will be
reset to default When suspending the FPGA.

> +
> + xcsi2rxss->suspended = true;
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +/*
> + * xcsi2rxss_pm_resume - Function called on Power Resume
> + * @dev: Pointer to device structure
> + *
> + * On power resume the CSI-2 Core is enabled when it is in suspended state
> + * and prior to entering suspended state it was streaming.
> + *
> + * Return: 0 on success
> + */
> +static int __maybe_unused xcsi2rxss_pm_resume(struct device *dev)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + if (xcsi2rxss->suspended && xcsi2rxss->streaming)
> + xcsi2rxss_set(&xcsi2rxss->core,
> + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
> +
> + xcsi2rxss->suspended = false;
> +
> + mutex_unlock(&xcsi2rxss->lock);
> + return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss)
> +{
> + struct device_node *node = xcsi2rxss->core.dev->of_node;
> + struct device_node *ports = NULL;
> + struct device_node *port = NULL;
> + unsigned int nports = 0;
> + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> + int ret;
> + bool iic_present;
> +
> + core->dphy_present = of_property_read_bool(node, "xlnx,dphy-present");
> + dev_dbg(core->dev, "DPHY present property = %s\n",
> + core->dphy_present ? "Present" : "Absent");
> +
> + iic_present = of_property_read_bool(node, "xlnx,iic-present");
> + dev_dbg(core->dev, "IIC present property = %s\n",
> + iic_present ? "Present" : "Absent");
> +
> + if (core->dphy_present) {
> + if (iic_present)
> + core->dphy_offset = 0x20000;
> + else
> + core->dphy_offset = 0x10000;

Could you please when this information can be found? I couldn't find any
relevant information in the product guide. For example, in the dt binding
example, this goes beyond the specified size of register, 0x20000.

> + }
> +
> + ret = of_property_read_u32(node, "xlnx,max-lanes",
> + &core->max_num_lanes);
> + if (ret < 0) {
> + dev_err(core->dev, "missing xlnx,max-lanes property\n");
> + return ret;
> + }
> +
> + if (core->max_num_lanes > 4 || core->max_num_lanes < 1) {
> + dev_err(core->dev, "%d max lanes : invalid xlnx,max-lanes property\n",
> + core->max_num_lanes);
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_u32(node, "xlnx,vc", &core->vc);
> + if (ret < 0) {
> + dev_err(core->dev, "missing xlnx,vc property\n");
> + return ret;
> + }
> + if (core->vc > 4) {
> + dev_err(core->dev, "invalid virtual channel property value.\n");
> + return -EINVAL;
> + }
> +
> + core->enable_active_lanes =
> + of_property_read_bool(node, "xlnx,en-active-lanes");
> + dev_dbg(core->dev, "Enable active lanes property = %s\n",
> + core->enable_active_lanes ? "Present" : "Absent");
> +
> + ret = of_property_read_string(node, "xlnx,csi-pxl-format",
> + &core->pxlformat);
> + if (ret < 0) {
> + dev_err(core->dev, "missing xlnx,csi-pxl-format property\n");
> + return ret;
> + }
> +
> + core->datatype = xcsi2rxss_pxlfmtstrtodt(core->pxlformat);
> + if (core->datatype < MIPI_CSI_DT_YUV_420_8B ||
> + core->datatype > MIPI_CSI_DT_RAW_14) {
> + dev_err(core->dev, "Invalid xlnx,csi-pxl-format string\n");
> + return -EINVAL;
> + }
> +
> + core->vfb = of_property_read_bool(node, "xlnx,vfb");
> + dev_dbg(core->dev, "Video Format Bridge property = %s\n",
> + core->vfb ? "Present" : "Absent");
> +
> + if (core->vfb) {
> + ret = of_property_read_u32(node, "xlnx,ppc", &core->ppc);
> + if (ret < 0 || !(core->ppc == 1 || core->ppc == 2 ||
> + core->ppc == 4)) {
> + dev_err(core->dev, "Invalid xlnx,ppc property ret = %d ppc = %d\n",
> + ret, core->ppc);
> + return -EINVAL;
> + }
> + }
> +
> + ports = of_get_child_by_name(node, "ports");
> + if (!ports)
> + ports = node;
> +
> + for_each_child_of_node(ports, port) {
> + int ret;
> + const struct xvip_video_format *format;
> + struct device_node *endpoint;
> + struct v4l2_fwnode_endpoint v4lendpoint;
> +
> + if (!port->name || of_node_cmp(port->name, "port"))
> + continue;
> +
> + /*
> + * Currently only a subset of VFB enabled formats present in
> + * xvip are supported in the driver.
> + *
> + * If the VFB is disabled, the pixels per clock don't matter.
> + * The data width is either 32 or 64 bit as selected in design.
> + *
> + * For e.g. If Data Type is RGB888, VFB is disabled and
> + * data width is 32 bits.
> + *
> + * Clk Cycle | Byte 0 | Byte 1 | Byte 2 | Byte 3
> + * -----------+----------+----------+----------+----------
> + * 1 | B0 | G0 | R0 | B1
> + * 2 | G1 | R1 | B2 | G2
> + * 3 | R2 | B3 | G3 | R3
> + */
> + format = xvip_of_get_format(port);
> + if (IS_ERR(format)) {
> + dev_err(core->dev, "invalid format in DT");
> + return PTR_ERR(format);
> + }
> +
> + if (core->vfb &&
> + format->vf_code != XVIP_VF_YUV_422 &&
> + format->vf_code != XVIP_VF_RBG &&
> + format->vf_code != XVIP_VF_MONO_SENSOR) {
> + dev_err(core->dev, "Invalid UG934 video format set.\n");
> + return -EINVAL;
> + }
> +
> + /* Get and check the format description */
> + if (!xcsi2rxss->vip_format) {
> + xcsi2rxss->vip_format = format;
> + } else if (xcsi2rxss->vip_format != format) {
> + dev_err(core->dev, "in/out format mismatch in DT");
> + return -EINVAL;
> + }
> +
> + endpoint = of_get_next_child(port, NULL);
> + if (!endpoint) {
> + dev_err(core->dev, "No port at\n");
> + return -EINVAL;
> + }
> +
> + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
> + &v4lendpoint);

You can have a single of_node_put(endpoint); here.

> + if (ret) {
> + of_node_put(endpoint);
> + return ret;
> + }
> +
> + of_node_put(endpoint);
> + dev_dbg(core->dev, "%s : port %d bus type = %d\n",
> + __func__, nports, v4lendpoint.bus_type);
> +
> + if (v4lendpoint.bus_type == V4L2_MBUS_CSI2) {
> + dev_dbg(core->dev, "%s : base.port = %d base.id = %d\n",
> + __func__, v4lendpoint.base.port,
> + v4lendpoint.base.id);
> +
> + dev_dbg(core->dev, "%s : mipi number lanes = %d\n",
> + __func__,
> + v4lendpoint.bus.mipi_csi2.num_data_lanes);
> + } else {
> + dev_dbg(core->dev, "%s : Not a CSI2 bus\n", __func__);
> + }
> +
> + /* Count the number of ports. */
> + nports++;
> + }
> +
> + if (nports != 2) {
> + dev_err(core->dev, "invalid number of ports %u\n", nports);
> + return -EINVAL;
> + }
> + xcsi2rxss->npads = nports;
> +
> + /*Register interrupt handler */

Nit. A space.

> + core->irq = irq_of_parse_and_map(node, 0);
> +
> + ret = devm_request_irq(core->dev, core->irq, xcsi2rxss_irq_handler,
> + IRQF_SHARED, "xilinx-csi2rxss", xcsi2rxss);
> + if (ret) {
> + dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> + ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int xcsi2rxss_probe(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *subdev;
> + struct xcsi2rxss_state *xcsi2rxss;
> + struct resource *res;
> +
> + u32 i;
> + int ret;
> + int num_ctrls;
> +
> + xcsi2rxss = devm_kzalloc(&pdev->dev, sizeof(*xcsi2rxss), GFP_KERNEL);
> + if (!xcsi2rxss)
> + return -ENOMEM;
> +
> + mutex_init(&xcsi2rxss->lock);
> +
> + xcsi2rxss->core.dev = &pdev->dev;
> +
> + ret = xcsi2rxss_parse_of(xcsi2rxss);
> + if (ret < 0)
> + return ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + xcsi2rxss->core.iomem = devm_ioremap_resource(xcsi2rxss->core.dev, res);
> + if (IS_ERR(xcsi2rxss->core.iomem))
> + return PTR_ERR(xcsi2rxss->core.iomem);
> +
> + /*
> + * Reset and initialize the core.
> + */
> + xcsi2rxss_reset(&xcsi2rxss->core);
> +
> + xcsi2rxss->core.events = (struct xcsi2rxss_event *)&xcsi2rxss_events;

Nit. There are double spaces here.

> +
> + /* Initialize V4L2 subdevice and media entity */
> + xcsi2rxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> + xcsi2rxss->pads[1].flags = MEDIA_PAD_FL_SINK;
> +
> + /* Initialize the default format */
> + memset(&xcsi2rxss->default_format, 0,
> + sizeof(xcsi2rxss->default_format));
> + xcsi2rxss->default_format.code = xcsi2rxss->vip_format->code;
> + xcsi2rxss->default_format.field = V4L2_FIELD_NONE;
> + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB;
> + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH;
> + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT;
> +
> + xcsi2rxss->formats[0] = xcsi2rxss->default_format;
> + xcsi2rxss->formats[1] = xcsi2rxss->default_format;
> +
> + /* Initialize V4L2 subdevice and media entity */
> + subdev = &xcsi2rxss->subdev;
> + v4l2_subdev_init(subdev, &xcsi2rxss_ops);
> +
> + subdev->dev = &pdev->dev;
> + subdev->internal_ops = &xcsi2rxss_internal_ops;
> + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
> +
> + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> + subdev->entity.ops = &xcsi2rxss_media_ops;
> +
> + v4l2_set_subdevdata(subdev, xcsi2rxss);
> +
> + ret = media_entity_pads_init(&subdev->entity, 2, xcsi2rxss->pads);
> + if (ret < 0)
> + goto error;
> +
> + /*
> + * In case the Enable Active Lanes config parameter is not set,
> + * dynamic lane reconfiguration is not allowed.
> + * So V4L2_CID_XILINX_MIPICSISS_ACT_LANES ctrl will not be registered.
> + * Accordingly allocate the number of controls
> + */
> + num_ctrls = ARRAY_SIZE(xcsi2rxss_ctrls);
> +
> + if (!xcsi2rxss->core.enable_active_lanes)
> + num_ctrls--;
> +
> + dev_dbg(xcsi2rxss->core.dev, "# of ctrls = %d\n", num_ctrls);
> +
> + v4l2_ctrl_handler_init(&xcsi2rxss->ctrl_handler, num_ctrls);
> +
> + for (i = 0; i < ARRAY_SIZE(xcsi2rxss_ctrls); i++) {
> + struct v4l2_ctrl *ctrl;
> +
> + if (xcsi2rxss_ctrls[i].id ==
> + V4L2_CID_XILINX_MIPICSISS_ACT_LANES) {
> + if (xcsi2rxss->core.enable_active_lanes) {
> + xcsi2rxss_ctrls[i].max =
> + xcsi2rxss->core.max_num_lanes;
> + } else {
> + /* Don't register control */
> + dev_dbg(xcsi2rxss->core.dev,
> + "Skip active lane control\n");
> + continue;
> + }

I'd do this without else:
if (!xcsi2rxss->core.enable_active_lanes) {
/* Don't register control */
dev_dbg(xcsi2rxss->core.dev,
"Skip active lane control\n");
continue;
}
xcsi2rxss_ctrls[i].max = xcsi2rxss->core.max_num_lanes;

> + }
> +
> + dev_dbg(xcsi2rxss->core.dev, "%d ctrl = 0x%x\n",
> + i, xcsi2rxss_ctrls[i].id);
> + ctrl = v4l2_ctrl_new_custom(&xcsi2rxss->ctrl_handler,
> + &xcsi2rxss_ctrls[i], NULL);
> + if (!ctrl) {
> + dev_err(xcsi2rxss->core.dev, "Failed for %s ctrl\n",
> + xcsi2rxss_ctrls[i].name);
> + goto error;
> + }
> + }
> +
> + dev_dbg(xcsi2rxss->core.dev, "# v4l2 ctrls registered = %d\n", i - 1);
> +
> + if (xcsi2rxss->ctrl_handler.error) {
> + dev_err(&pdev->dev, "failed to add controls\n");
> + ret = xcsi2rxss->ctrl_handler.error;
> + goto error;
> + }
> +
> + subdev->ctrl_handler = &xcsi2rxss->ctrl_handler;
> +
> + ret = v4l2_ctrl_handler_setup(&xcsi2rxss->ctrl_handler);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to set controls\n");
> + goto error;
> + }
> +
> + platform_set_drvdata(pdev, xcsi2rxss);
> +
> + dev_info(xcsi2rxss->core.dev, "Xilinx CSI2 Rx Subsystem device found!\n");
> +
> + ret = v4l2_async_register_subdev(subdev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to register subdev\n");
> + goto error;
> + }
> +
> + /* default states for streaming and suspend */
> + xcsi2rxss->streaming = false;
> + xcsi2rxss->suspended = false;

Are these needed?

> + return 0;
> +
> +error:
> + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> + mutex_destroy(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +static int xcsi2rxss_remove(struct platform_device *pdev)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev);
> + struct v4l2_subdev *subdev = &xcsi2rxss->subdev;
> +
> + v4l2_async_unregister_subdev(subdev);
> + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> + mutex_destroy(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(xcsi2rxss_pm_ops,
> + xcsi2rxss_pm_suspend, xcsi2rxss_pm_resume);
> +
> +static const struct of_device_id xcsi2rxss_of_id_table[] = {
> + { .compatible = "xlnx,mipi-csi2-rx-subsystem-2.0" },
> + { .compatible = "xlnx,mipi-csi2-rx-subsystem-3.0" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table);
> +
> +static struct platform_driver xcsi2rxss_driver = {
> + .driver = {
> + .name = "xilinx-csi2rxss",
> + .pm = &xcsi2rxss_pm_ops,
> + .of_match_table = xcsi2rxss_of_id_table,
> + },
> + .probe = xcsi2rxss_probe,
> + .remove = xcsi2rxss_remove,
> +};
> +module_platform_driver(xcsi2rxss_driver);
> +MODULE_AUTHOR("Vishal Sagar <[email protected]>");
> +MODULE_DESCRIPTION("Xilinx MIPI CSI2 Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-csi2rxss.h b/include/uapi/linux/xilinx-csi2rxss.h
> new file mode 100644
> index 0000000..973d5b46
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-csi2rxss.h

Would it make sense to create one generic file for Xilinx specific events?

Thanks,
-hyun

> @@ -0,0 +1,25 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Events from Xilinx MIPI CSI2 Rx Subsystem exposed to V4L2 application.
> + *
> + * Copyright (C) 2016 - 2018 Xilinx, Inc.
> + * Author: Vishal Sagar <[email protected]>
> + */
> +#ifndef __UAPI_XILINX_CSI2RXSS_H__
> +#define __UAPI_XILINX_CSI2RXSS_H__
> +
> +#include <linux/videodev2.h>
> +
> +/*
> + * Events
> + *
> + * V4L2_EVENT_XLNXCSIRX_SPKT: Short packet received
> + * V4L2_EVENT_XLNXCSIRX_SPKT_OVF: Short packet FIFO overflow
> + * V4L2_EVENT_XLNXCSIRX_SLBF: Stream line buffer full
> + */
> +#define V4L2_EVENT_XLNXCSIRX_CLASS (V4L2_EVENT_PRIVATE_START | 0x100)
> +#define V4L2_EVENT_XLNXCSIRX_SPKT (V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
> +#define V4L2_EVENT_XLNXCSIRX_SPKT_OVF (V4L2_EVENT_XLNXCSIRX_CLASS | 0x2)
> +#define V4L2_EVENT_XLNXCSIRX_SLBF (V4L2_EVENT_XLNXCSIRX_CLASS | 0x3)
> +
> +#endif /* __UAPI_XILINX_CSI2RXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index b6441fe..4ca3b44 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -71,4 +71,18 @@
> /* Noise level */
> #define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17)
>
> +/*
> + * Xilinx MIPI CSI2 Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_MIPICSISS (V4L2_CID_USER_BASE + 0xc080)
> +
> +/* Active Lanes */
> +#define V4L2_CID_XILINX_MIPICSISS_ACT_LANES (V4L2_CID_XILINX_MIPICSISS + 1)
> +/* Frames received since streaming is set */
> +#define V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER (V4L2_CID_XILINX_MIPICSISS + 2)
> +/* Reset all event counters */
> +#define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 3)
> +
> #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> --
> 2.7.4
>
>

2018-06-12 20:06:06

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem

On Wed, May 30, 2018 at 12:24:43AM +0530, Vishal Sagar wrote:
> Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem.
>
> The Xilinx MIPI CSI-2 Rx Subsystem consists of a DPHY, CSI-2 Rx, an
> optional I2C controller and an optional Video Format Bridge (VFB). The
> active lanes can be configured at run time if enabled in the IP. The
> DPHY register interface may also be enabled.
>
> Signed-off-by: Vishal Sagar <[email protected]>
> ---
> .../bindings/media/xilinx/xlnx,csi2rxss.txt | 117 +++++++++++++++++++++
> 1 file changed, 117 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
>
> diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> new file mode 100644
> index 0000000..31ed721
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> @@ -0,0 +1,117 @@
> +
> +Xilinx MIPI CSI2 Receiver Subsystem Device Tree Bindings
> +--------------------------------------------------------
> +
> +The Xilinx MIPI CSI2 Receiver Subsystem is used to capture MIPI CSI2 traffic
> +from compliant camera sensors and send the output as AXI4 Stream video data
> +for image processing.
> +
> +The subsystem consists of a MIPI DPHY in slave mode which captures the
> +data packets. This is passed along the MIPI CSI2 Rx IP which extracts the
> +packet data. This data is taken in by the Video Format Bridge (VFB),
> +if selected, and converted into AXI4 Stream video data at selected
> +pixels per clock as per AXI4-Stream Video IP and System Design UG934.
> +
> +For more details, please refer to PG232 MIPI CSI-2 Receiver Subsystem.
> +https://www.xilinx.com/support/documentation/ip_documentation/mipi_csi2_rx_subsystem/v3_0/pg232-mipi-csi2-rx.pdf
> +
> +Required properties:
> +
> +- compatible: Must contain "xlnx,mipi-csi2-rx-subsystem-2.0" or
> + "xlnx,mipi-csi2-rx-subsystem-3.0"
> +
> +- reg: Physical base address and length of the registers set for the device.
> +
> +- interrupt-parent: specifies the phandle to the parent interrupt controller
> +
> +- interrupts: Property with a value describing the interrupt number.
> +
> +- xlnx,max-lanes: Maximum active lanes in the design.

There's already a property defined in video-interfaces.txt to limit
lanes.

> +
> +- xlnx,vc: Virtual Channel, specifies virtual channel number to be filtered.
> + If this is 4 then all virtual channels are allowed.
> +
> +- xlnx,csi-pxl-format: This denotes the CSI Data type selected in hw design.
> + Packets other than this data type (except for RAW8 and User defined data
> + types) will be filtered out. Possible values are RAW6, RAW7, RAW8, RAW10,
> + RAW12, RAW14, RGB444, RGB555, RGB565, RGB666, RGB888 and YUV4228bit.

This should be standard property.

> +
> +- xlnx,axis-tdata-width: AXI Stream width, This denotes the AXI Stream width.
> + It depends on Data type chosen, Video Format Bridge enabled/disabled and
> + pixels per clock. If VFB is disabled then its value is either 0x20 (32 bit)
> + or 0x40(64 bit) width.
> +
> +- xlnx,video-format, xlnx,video-width: Video format and width, as defined in
> + video.txt.

This doc needs to define what are valid values.

Why do you need this on both ports? Can there be a conversion in this
block? At least for the MIPI CSI interface part, this should be a common
property. Not sure offhand if we have defined one. We have for parallel
interfaces.

And 'width' doesn't seem like the right term for what this is defined to
be.

> +
> +- port: Video port, using the DT bindings defined in ../video-interfaces.txt.

port is not a property. It goes in its own section. And port properties
should be under it.

> + The CSI 2 Rx Subsystem has a two ports, one input port for connecting to
> + camera sensor and other is output port.

Need be specific port #0 is ?? and port #1 is ??.

> +
> +- data-lanes: The number of data lanes through which CSI2 Rx Subsystem is
> + connected to the camera sensor as per video-interfaces.txt

Why do you need both this and max-lanes?

> +
> +Optional properties:
> +
> +- xlnx,en-active-lanes: Enable Active lanes configuration in Protocol
> + Configuration Register.
> +
> +- xlnx,dphy-present: This is equivalent to whether DPHY register interface is
> + enabled or not.
> +
> +- xlnx,iic-present: This shows whether subsystem's IIC is present or not. This
> + affects the base address of the DPHY.

Perhaps you should break up reg into ranges for each submodule (or make
the DPHY a separate node and use the phy binding.

> +
> +- xlnx,vfb: Video Format Bridge, Denotes if Video Format Bridge is selected
> + so that output is as per AXI stream documented in UG934.
> +
> +- xlnx,ppc: Pixels per clock, Number of pixels to be transferred per pixel
> + clock. This is valid only if xlnx,vfb property is present.
> +
> +Example:
> +
> + csiss_1: csiss@a0020000 {
> + compatible = "xlnx,mipi-csi2-rx-subsystem-3.0";
> + reg = <0x0 0xa0020000 0x0 0x20000>;
> + interrupt-parent = <&gic>;
> + interrupts = <0 95 4>;
> +
> + xlnx,max-lanes = <0x4>;
> + xlnx,en-active-lanes;
> + xlnx,dphy-present;
> + xlnx,iic-present;
> + xlnx,vc = <0x4>;
> + xlnx,csi-pxl-format = "RAW8";
> + xlnx,vfb;
> + xlnx,ppc = <0x4>;
> + xlnx,axis-tdata-width = <0x20>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> +
> + xlnx,video-format = <XVIP_VF_YUV_422>;
> + xlnx,video-width = <8>;
> + csiss_out: endpoint {
> + remote-endpoint = <&vcap_csiss_in>;
> + };
> + };
> + port@1 {
> + reg = <1>;
> +
> + xlnx,video-format = <XVIP_VF_YUV_422>;
> + xlnx,video-width = <8>;
> +
> + csiss_in: endpoint {
> + data-lanes = <1 2 3 4>;
> + /* MIPI CSI2 Camera handle */
> + remote-endpoint = <&vs2016_out>;
> + };
> +
> + };
> +
> + };
> + };
> --
> 2.7.4
>
> This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2019-01-08 11:43:37

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem

Hi Rob,

Thanks for the review.

> -----Original Message-----
> From: Rob Herring [mailto:[email protected]]
> Sent: Wednesday, June 13, 2018 1:34 AM
> To: Vishal Sagar <[email protected]>
> Cc: Hyun Kwon <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; Dinesh Kumar <[email protected]>; linux-arm-
> [email protected]
> Subject: Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2
> Rx Subsystem
>
> On Wed, May 30, 2018 at 12:24:43AM +0530, Vishal Sagar wrote:
> > Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem.
> >
> > The Xilinx MIPI CSI-2 Rx Subsystem consists of a DPHY, CSI-2 Rx, an
> > optional I2C controller and an optional Video Format Bridge (VFB). The
> > active lanes can be configured at run time if enabled in the IP. The
> > DPHY register interface may also be enabled.
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > .../bindings/media/xilinx/xlnx,csi2rxss.txt | 117
> +++++++++++++++++++++
> > 1 file changed, 117 insertions(+)
> > create mode 100644
> Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> >
> > diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > new file mode 100644
> > index 0000000..31ed721
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > @@ -0,0 +1,117 @@
> > +
> > +Xilinx MIPI CSI2 Receiver Subsystem Device Tree Bindings
> > +--------------------------------------------------------
> > +
> > +The Xilinx MIPI CSI2 Receiver Subsystem is used to capture MIPI CSI2 traffic
> > +from compliant camera sensors and send the output as AXI4 Stream video
> data
> > +for image processing.
> > +
> > +The subsystem consists of a MIPI DPHY in slave mode which captures the
> > +data packets. This is passed along the MIPI CSI2 Rx IP which extracts the
> > +packet data. This data is taken in by the Video Format Bridge (VFB),
> > +if selected, and converted into AXI4 Stream video data at selected
> > +pixels per clock as per AXI4-Stream Video IP and System Design UG934.
> > +
> > +For more details, please refer to PG232 MIPI CSI-2 Receiver Subsystem.
> >
> +https://www.xilinx.com/support/documentation/ip_documentation/mipi_csi
> 2_rx_subsystem/v3_0/pg232-mipi-csi2-rx.pdf
> > +
> > +Required properties:
> > +
> > +- compatible: Must contain "xlnx,mipi-csi2-rx-subsystem-2.0" or
> > + "xlnx,mipi-csi2-rx-subsystem-3.0"
> > +
> > +- reg: Physical base address and length of the registers set for the device.
> > +
> > +- interrupt-parent: specifies the phandle to the parent interrupt controller
> > +
> > +- interrupts: Property with a value describing the interrupt number.
> > +
> > +- xlnx,max-lanes: Maximum active lanes in the design.
>
> There's already a property defined in video-interfaces.txt to limit
> lanes.
>

Ok I will use the standard data-lanes and remove this.

> > +
> > +- xlnx,vc: Virtual Channel, specifies virtual channel number to be filtered.
> > + If this is 4 then all virtual channels are allowed.
> > +
> > +- xlnx,csi-pxl-format: This denotes the CSI Data type selected in hw design.
> > + Packets other than this data type (except for RAW8 and User defined data
> > + types) will be filtered out. Possible values are RAW6, RAW7, RAW8, RAW10,
> > + RAW12, RAW14, RGB444, RGB555, RGB565, RGB666, RGB888 and
> YUV4228bit.
>
> This should be standard property.

I don't see a standard property for the CSI pixel formats.
Do you mean media bus formats?

>
> > +
> > +- xlnx,axis-tdata-width: AXI Stream width, This denotes the AXI Stream width.
> > + It depends on Data type chosen, Video Format Bridge enabled/disabled and
> > + pixels per clock. If VFB is disabled then its value is either 0x20 (32 bit)
> > + or 0x40(64 bit) width.
> > +
> > +- xlnx,video-format, xlnx,video-width: Video format and width, as defined in
> > + video.txt.
>
> This doc needs to define what are valid values.
>

Ok. The valid values list will be added in next revision.

> Why do you need this on both ports? Can there be a conversion in this
> block? At least for the MIPI CSI interface part, this should be a common
> property. Not sure offhand if we have defined one. We have for parallel
> interfaces.
>
> And 'width' doesn't seem like the right term for what this is defined to
> be.
>

This is put on both ports to keep it consistent with other drivers.
The video-format values are specific to Xilinx.
The width here means bit per component.

> > +
> > +- port: Video port, using the DT bindings defined in ../video-interfaces.txt.
>
> port is not a property. It goes in its own section. And port properties
> should be under it.

I will update this in next version.

>
> > + The CSI 2 Rx Subsystem has a two ports, one input port for connecting to
> > + camera sensor and other is output port.
>
> Need be specific port #0 is ?? and port #1 is ??.
>

Right. I will describe them in the next version correctly.
0 will be sink pad and 1 will source pad.

> > +
> > +- data-lanes: The number of data lanes through which CSI2 Rx Subsystem is
> > + connected to the camera sensor as per video-interfaces.txt
>
> Why do you need both this and max-lanes?

I didn't know more and added both. I will keep this.

>
> > +
> > +Optional properties:
> > +
> > +- xlnx,en-active-lanes: Enable Active lanes configuration in Protocol
> > + Configuration Register.
> > +
> > +- xlnx,dphy-present: This is equivalent to whether DPHY register interface is
> > + enabled or not.
> > +
> > +- xlnx,iic-present: This shows whether subsystem's IIC is present or not. This
> > + affects the base address of the DPHY.
>
> Perhaps you should break up reg into ranges for each submodule (or make
> the DPHY a separate node and use the phy binding.
>

Ok I will break the range into 2 parts and access DPHY accordingly.

Regards
Vishal Sagar

> > +
> > +- xlnx,vfb: Video Format Bridge, Denotes if Video Format Bridge is selected
> > + so that output is as per AXI stream documented in UG934.
> > +
> > +- xlnx,ppc: Pixels per clock, Number of pixels to be transferred per pixel
> > + clock. This is valid only if xlnx,vfb property is present.
> > +
> > +Example:
> > +
> > + csiss_1: csiss@a0020000 {
> > + compatible = "xlnx,mipi-csi2-rx-subsystem-3.0";
> > + reg = <0x0 0xa0020000 0x0 0x20000>;
> > + interrupt-parent = <&gic>;
> > + interrupts = <0 95 4>;
> > +
> > + xlnx,max-lanes = <0x4>;
> > + xlnx,en-active-lanes;
> > + xlnx,dphy-present;
> > + xlnx,iic-present;
> > + xlnx,vc = <0x4>;
> > + xlnx,csi-pxl-format = "RAW8";
> > + xlnx,vfb;
> > + xlnx,ppc = <0x4>;
> > + xlnx,axis-tdata-width = <0x20>;
> > +
> > + ports {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + port@0 {
> > + reg = <0>;
> > +
> > + xlnx,video-format = <XVIP_VF_YUV_422>;
> > + xlnx,video-width = <8>;
> > + csiss_out: endpoint {
> > + remote-endpoint = <&vcap_csiss_in>;
> > + };
> > + };
> > + port@1 {
> > + reg = <1>;
> > +
> > + xlnx,video-format = <XVIP_VF_YUV_422>;
> > + xlnx,video-width = <8>;
> > +
> > + csiss_in: endpoint {
> > + data-lanes = <1 2 3 4>;
> > + /* MIPI CSI2 Camera handle */
> > + remote-endpoint = <&vs2016_out>;
> > + };
> > +
> > + };
> > +
> > + };
> > + };
> > --
> > 2.7.4
> >
> > This email and any attachments are intended for the sole use of the named
> recipient(s) and contain(s) confidential information that may be proprietary,
> privileged or copyrighted under applicable law. If you are not the intended
> recipient, do not read, copy, or forward this email message or any attachments.
> Delete this email message and any attachments immediately.
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > [email protected]
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2019-01-08 11:44:13

by Vishal Sagar

[permalink] [raw]
Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

Hi Hyun

Thanks for the review.

> -----Original Message-----
> From: Hyun Kwon <[email protected]>
> Sent: Thursday, June 01, 2018 6:46 AM
> To: Vishal Sagar <[email protected]>
> Cc: Hyun Kwon <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; Dinesh Kumar <[email protected]>; linux-arm-
> [email protected]
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver
>
> Hi Vishal,
>
> Thanks for the patch.
>
> On Tue, 2018-05-29 at 11:54:44 -0700, Vishal Sagar wrote:
> > The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images
> > from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready
> > for image processing.
> >
> > It supports upto 4 lanes, filtering based on Virtual Channel selected in
> > IP, an optional Xilinx IIC controller, optional register interface for
> > the DPHY, multiple color formats, short packet capture,
> >
> > This driver helps configure the number of active lanes to be set,
> > setting and handling interrupts and IP core enable.
> > It logs the count of events occurring according to their type between
> > streaming ON and OFF. The short packet reception is notified to
> > application via v4l2_event.
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > drivers/media/platform/xilinx/Kconfig | 12 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751
> +++++++++++++++++++++++
> > include/uapi/linux/xilinx-csi2rxss.h | 25 +
> > include/uapi/linux/xilinx-v4l2-controls.h | 14 +
> > 5 files changed, 1803 insertions(+)
> > create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
> > create mode 100644 include/uapi/linux/xilinx-csi2rxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index a5d21b7..06d5944 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -8,6 +8,18 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +config VIDEO_XILINX_CSI2RXSS
> > + tristate "Xilinx CSI2 Rx Subsystem"
> > + depends on VIDEO_XILINX
> > + help
> > + Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
> > + based driver that takes input from CSI2 Tx source and converts
> > + it into an AXI4-Stream. It has a DPHY (whose register interface
> > + can be enabled, an optional I2C controller and an optional Video
> > + Format Bridge which converts the AXI4-Stream data to Xilinx Video
> > + Bus formats based on UG934. The driver is used to set the number
> > + of active lanes and get short packet data.
> > +
> > config VIDEO_XILINX_TPG
> > tristate "Xilinx Video Test Pattern Generator"
> > depends on VIDEO_XILINX
> > diff --git a/drivers/media/platform/xilinx/Makefile
> b/drivers/media/platform/xilinx/Makefile
> > index e8a0f2a..768f079 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -1,5 +1,6 @@
> > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> >
> > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
> > +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o
> > obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
> > obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
> > diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> > new file mode 100644
> > index 0000000..03f387c
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> > @@ -0,0 +1,1751 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Driver for Xilinx MIPI CSI2 Rx Subsystem
> > + *
> > + * Copyright (C) 2016 - 2018 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + *
> > + */
> > +
> > +#include <dt-bindings/media/xilinx-vip.h>
> > +#include <linux/bitops.h>
> > +#include <linux/compiler.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/spinlock_types.h>
> > +#include <linux/types.h>
> > +#include <linux/v4l2-subdev.h>
> > +#include <linux/xilinx-csi2rxss.h>
> > +#include <linux/xilinx-v4l2-controls.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +#include "xilinx-vip.h"
> > +
> > +/*
> > + * MIPI CSI2 Rx register map, bitmask and offsets
> > + */
> > +#define XCSI_CCR_OFFSET 0x00000000
> > +#define XCSI_CCR_SOFTRESET_SHIFT 1
> > +#define XCSI_CCR_COREENB_SHIFT 0
> > +#define XCSI_CCR_SOFTRESET_MASK
> BIT(XCSI_CCR_SOFTRESET_SHIFT)
> > +#define XCSI_CCR_COREENB_MASK
> BIT(XCSI_CCR_COREENB_SHIFT)
>
> Single bit field doesn't have to be called as '_MASK', and thus the shift is not
> needed. So,
>
> #define XCSI_CCR_COREENB BIT(0)
>
> would be enough. Same for the rest.

I will update this in next version.

>
> > +
> > +#define XCSI_PCR_OFFSET 0x00000004
> > +#define XCSI_PCR_MAXLANES_MASK 0x00000018
> > +#define XCSI_PCR_ACTLANES_MASK 0x00000003
>
> You can use GENMASK().

Ok I will update this in next version

>
> > +#define XCSI_PCR_MAXLANES_SHIFT 3
> > +#define XCSI_PCR_ACTLANES_SHIFT 0
>
> These are not even used. You can remove these.
>

All these macros are coming from the bare metal driver.
I assumed it would be good to know the register offsets in the code itself.

I will remove these and other unused ones in next version.

> > +
> > +#define XCSI_CSR_OFFSET 0x00000010
> > +#define XCSI_CSR_PKTCOUNT_SHIFT 16
> > +#define XCSI_CSR_SPFIFOFULL_SHIFT 3
> > +#define XCSI_CSR_SPFIFONE_SHIFT 2
> > +#define XCSI_CSR_SLBF_SHIFT 1
> > +#define XCSI_CSR_RIPCD_SHIFT 0
> > +#define XCSI_CSR_PKTCOUNT_MASK 0xFFFF0000
>
> I prefer the low case for hex values, but maybe it's just me.
>

Ok.

> > +#define XCSI_CSR_SPFIFOFULL_MASK
> BIT(XCSI_CSR_SPFIFOFULL_SHIFT)
> > +#define XCSI_CSR_SPFIFONE_MASK
> BIT(XCSI_CSR_SPFIFONE_SHIFT)
> > +#define XCSI_CSR_SLBF_MASK BIT(XCSI_CSR_SLBF_SHIFT)
> > +#define XCSI_CSR_RIPCD_MASK BIT(XCSI_CSR_RIPCD_SHIFT)
> > +
> > +#define XCSI_GIER_OFFSET 0x00000020
> > +#define XCSI_GIER_GIE_SHIFT 0
> > +#define XCSI_GIER_GIE_MASK BIT(XCSI_GIER_GIE_SHIFT)
> > +#define XCSI_GIER_SET 1
> > +#define XCSI_GIER_RESET 0
> > +
> > +#define XCSI_ISR_OFFSET 0x00000024
> > +#define XCSI_ISR_FR_SHIFT 31
> > +#define XCSI_ISR_ILC_SHIFT 21
> > +#define XCSI_ISR_SPFIFOF_SHIFT 20
> > +#define XCSI_ISR_SPFIFONE_SHIFT 19
> > +#define XCSI_ISR_SLBF_SHIFT 18
> > +#define XCSI_ISR_STOP_SHIFT 17
> > +#define XCSI_ISR_SOTERR_SHIFT 13
> > +#define XCSI_ISR_SOTSYNCERR_SHIFT 12
> > +#define XCSI_ISR_ECC2BERR_SHIFT 11
> > +#define XCSI_ISR_ECC1BERR_SHIFT 10
> > +#define XCSI_ISR_CRCERR_SHIFT 9
> > +#define XCSI_ISR_DATAIDERR_SHIFT 8
> > +#define XCSI_ISR_VC3FSYNCERR_SHIFT 7
> > +#define XCSI_ISR_VC3FLVLERR_SHIFT 6
> > +#define XCSI_ISR_VC2FSYNCERR_SHIFT 5
> > +#define XCSI_ISR_VC2FLVLERR_SHIFT 4
> > +#define XCSI_ISR_VC1FSYNCERR_SHIFT 3
> > +#define XCSI_ISR_VC1FLVLERR_SHIFT 2
> > +#define XCSI_ISR_VC0FSYNCERR_SHIFT 1
> > +#define XCSI_ISR_VC0FLVLERR_SHIFT 0
> > +#define XCSI_ISR_FR_MASK BIT(XCSI_ISR_FR_SHIFT)
> > +#define XCSI_ISR_ILC_MASK BIT(XCSI_ISR_ILC_SHIFT)
> > +#define XCSI_ISR_SPFIFOF_MASK BIT(XCSI_ISR_SPFIFOF_SHIFT)
> > +#define XCSI_ISR_SPFIFONE_MASK BIT(XCSI_ISR_SPFIFONE_SHIFT)
> > +#define XCSI_ISR_SLBF_MASK BIT(XCSI_ISR_SLBF_SHIFT)
> > +#define XCSI_ISR_STOP_MASK BIT(XCSI_ISR_STOP_SHIFT)
> > +#define XCSI_ISR_SOTERR_MASK BIT(XCSI_ISR_SOTERR_SHIFT)
> > +#define XCSI_ISR_SOTSYNCERR_MASK
> BIT(XCSI_ISR_SOTSYNCERR_SHIFT)
> > +#define XCSI_ISR_ECC2BERR_MASK BIT(XCSI_ISR_ECC2BERR_SHIFT)
> > +#define XCSI_ISR_ECC1BERR_MASK BIT(XCSI_ISR_ECC1BERR_SHIFT)
> > +#define XCSI_ISR_CRCERR_MASK BIT(XCSI_ISR_CRCERR_SHIFT)
> > +#define XCSI_ISR_DATAIDERR_MASK
> BIT(XCSI_ISR_DATAIDERR_SHIFT)
> > +#define XCSI_ISR_VC3FSYNCERR_MASK
> BIT(XCSI_ISR_VC3FSYNCERR_SHIFT)
> > +#define XCSI_ISR_VC3FLVLERR_MASK
> BIT(XCSI_ISR_VC3FLVLERR_SHIFT)
> > +#define XCSI_ISR_VC2FSYNCERR_MASK
> BIT(XCSI_ISR_VC2FSYNCERR_SHIFT)
> > +#define XCSI_ISR_VC2FLVLERR_MASK
> BIT(XCSI_ISR_VC2FLVLERR_SHIFT)
> > +#define XCSI_ISR_VC1FSYNCERR_MASK
> BIT(XCSI_ISR_VC1FSYNCERR_SHIFT)
> > +#define XCSI_ISR_VC1FLVLERR_MASK
> BIT(XCSI_ISR_VC1FLVLERR_SHIFT)
> > +#define XCSI_ISR_VC0FSYNCERR_MASK
> BIT(XCSI_ISR_VC0FSYNCERR_SHIFT)
> > +#define XCSI_ISR_VC0FLVLERR_MASK
> BIT(XCSI_ISR_VC0FLVLERR_SHIFT)
>
> Ditto for mask / shift and unused ones.
>

Ok. Will remove the unused ones and correct the mask and shift ones.

> > +#define XCSI_ISR_ALLINTR_MASK 0x803FFFFF
>
> You can OR maskes below to get this too, at least to be consistent with below.
> But up to you.
>

Ok will use the macros as below to build the ALL Interrupts mask.

> > +
> > +#define XCSI_INTR_PROT_MASK (XCSI_ISR_VC3FSYNCERR_MASK | \
> > + XCSI_ISR_VC3FLVLERR_MASK | \
> > + XCSI_ISR_VC2FSYNCERR_MASK | \
> > + XCSI_ISR_VC2FLVLERR_MASK | \
> > + XCSI_ISR_VC1FSYNCERR_MASK | \
> > + XCSI_ISR_VC1FLVLERR_MASK | \
> > + XCSI_ISR_VC0FSYNCERR_MASK | \
> > + XCSI_ISR_VC0FLVLERR_MASK)
> > +
> > +#define XCSI_INTR_PKTLVL_MASK (XCSI_ISR_ECC2BERR_MASK | \
> > + XCSI_ISR_ECC1BERR_MASK | \
> > + XCSI_ISR_CRCERR_MASK | \
> > + XCSI_ISR_DATAIDERR_MASK)
> > +
> > +#define XCSI_INTR_DPHY_MASK (XCSI_ISR_SOTERR_MASK | \
> > + XCSI_ISR_SOTSYNCERR_MASK)
> > +
> > +#define XCSI_INTR_SPKT_MASK (XCSI_ISR_SPFIFOF_MASK | \
> > + XCSI_ISR_SPFIFONE_MASK)
> > +
> > +#define XCSI_INTR_FRAMERCVD_MASK (XCSI_ISR_FR_MASK)
> > +
> > +#define XCSI_INTR_ERR_MASK (XCSI_ISR_ILC_MASK | \
> > + XCSI_ISR_SLBF_MASK | \
> > + XCSI_ISR_STOP_MASK)
> > +
> > +#define XCSI_IER_OFFSET 0x00000028
> > +#define XCSI_IER_FR_SHIFT 31
> > +#define XCSI_IER_ILC_SHIFT 21
> > +#define XCSI_IER_SPFIFOF_SHIFT 20
> > +#define XCSI_IER_SPFIFONE_SHIFT 19
> > +#define XCSI_IER_SLBF_SHIFT 18
> > +#define XCSI_IER_STOP_SHIFT 17
> > +#define XCSI_IER_SOTERR_SHIFT 13
> > +#define XCSI_IER_SOTSYNCERR_SHIFT 12
> > +#define XCSI_IER_ECC2BERR_SHIFT 11
> > +#define XCSI_IER_ECC1BERR_SHIFT 10
> > +#define XCSI_IER_CRCERR_SHIFT 9
> > +#define XCSI_IER_DATAIDERR_SHIFT 8
> > +#define XCSI_IER_VC3FSYNCERR_SHIFT 7
> > +#define XCSI_IER_VC3FLVLERR_SHIFT 6
> > +#define XCSI_IER_VC2FSYNCERR_SHIFT 5
> > +#define XCSI_IER_VC2FLVLERR_SHIFT 4
> > +#define XCSI_IER_VC1FSYNCERR_SHIFT 3
> > +#define XCSI_IER_VC1FLVLERR_SHIFT 2
> > +#define XCSI_IER_VC0FSYNCERR_SHIFT 1
> > +#define XCSI_IER_VC0FLVLERR_SHIFT 0
> > +#define XCSI_IER_FR_MASK BIT(XCSI_IER_FR_SHIFT)
> > +#define XCSI_IER_ILC_MASK BIT(XCSI_IER_ILC_SHIFT)
> > +#define XCSI_IER_SPFIFOF_MASK BIT(XCSI_IER_SPFIFOF_SHIFT)
> > +#define XCSI_IER_SPFIFONE_MASK BIT(XCSI_IER_SPFIFONE_SHIFT)
> > +#define XCSI_IER_SLBF_MASK BIT(XCSI_IER_SLBF_SHIFT)
> > +#define XCSI_IER_STOP_MASK BIT(XCSI_IER_STOP_SHIFT)
> > +#define XCSI_IER_SOTERR_MASK BIT(XCSI_IER_SOTERR_SHIFT)
> > +#define XCSI_IER_SOTSYNCERR_MASK
> BIT(XCSI_IER_SOTSYNCERR_SHIFT)
> > +#define XCSI_IER_ECC2BERR_MASK BIT(XCSI_IER_ECC2BERR_SHIFT)
> > +#define XCSI_IER_ECC1BERR_MASK BIT(XCSI_IER_ECC1BERR_SHIFT)
> > +#define XCSI_IER_CRCERR_MASK BIT(XCSI_IER_CRCERR_SHIFT)
> > +#define XCSI_IER_DATAIDERR_MASK
> BIT(XCSI_IER_DATAIDERR_SHIFT)
> > +#define XCSI_IER_VC3FSYNCERR_MASK
> BIT(XCSI_IER_VC3FSYNCERR_SHIFT)
> > +#define XCSI_IER_VC3FLVLERR_MASK
> BIT(XCSI_IER_VC3FLVLERR_SHIFT)
> > +#define XCSI_IER_VC2FSYNCERR_MASK
> BIT(XCSI_IER_VC2FSYNCERR_SHIFT)
> > +#define XCSI_IER_VC2FLVLERR_MASK
> BIT(XCSI_IER_VC2FLVLERR_SHIFT)
> > +#define XCSI_IER_VC1FSYNCERR_MASK
> BIT(XCSI_IER_VC1FSYNCERR_SHIFT)
> > +#define XCSI_IER_VC1FLVLERR_MASK
> BIT(XCSI_IER_VC1FLVLERR_SHIFT)
> > +#define XCSI_IER_VC0FSYNCERR_MASK
> BIT(XCSI_IER_VC0FSYNCERR_SHIFT)
> > +#define XCSI_IER_VC0FLVLERR_MASK
> BIT(XCSI_IER_VC0FLVLERR_SHIFT)
> > +#define XCSI_IER_ALLINTR_MASK 0x803FFFFF
>
> This is simply repeatation of above, XCSI_ISR_*. One common one can be used.
>

Ok Will remove this.

> > +
> > +#define XCSI_SPKTR_OFFSET 0x00000030
> > +#define XCSI_SPKTR_DATA_SHIFT 8
> > +#define XCSI_SPKTR_VC_SHIFT 6
> > +#define XCSI_SPKTR_DT_SHIFT 0
> > +#define XCSI_SPKTR_DATA_MASK 0x00FFFF00
> > +#define XCSI_SPKTR_VC_MASK 0x000000C0
> > +#define XCSI_SPKTR_DT_MASK 0x0000003F
>
> GENMASK().
>

Ok will use GENMASK()

> > +
> > +#define XCSI_CLKINFR_OFFSET 0x0000003C
> > +#define XCSI_CLKINFR_STOP_SHIFT 1
> > +#define XCSI_CLKINFR_STOP_MASK
> BIT(XCSI_CLKINFR_STOP_SHIFT)
> > +
> > +#define XCSI_L0INFR_OFFSET 0x00000040
> > +#define XCSI_L1INFR_OFFSET 0x00000044
> > +#define XCSI_L2INFR_OFFSET 0x00000048
> > +#define XCSI_L3INFR_OFFSET 0x0000004C
> > +#define XCSI_LXINFR_STOP_SHIFT 5
> > +#define XCSI_LXINFR_SOTERR_SHIFT 1
> > +#define XCSI_LXINFR_SOTSYNCERR_SHIFT 0
> > +#define XCSI_LXINFR_STOP_MASK BIT(XCSI_LXINFR_STOP_SHIFT)
> > +#define XCSI_LXINFR_SOTERR_MASK
> BIT(XCSI_LXINFR_SOTERR_SHIFT)
> > +#define XCSI_LXINFR_SOTSYNCERR_MASK
> BIT(XCSI_LXINFR_SOTSYNCERR_SHIFT)
> > +
> > +#define XCSI_VC0INF1R_OFFSET 0x00000060
> > +#define XCSI_VC1INF1R_OFFSET 0x00000068
> > +#define XCSI_VC2INF1R_OFFSET 0x00000070
> > +#define XCSI_VC3INF1R_OFFSET 0x00000078
> > +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16
> > +#define XCSI_VCXINF1R_BYTECOUNT_SHIFT 0
> > +#define XCSI_VCXINF1R_LINECOUNT_MASK 0xFFFF0000
> > +#define XCSI_VCXINF1R_BYTECOUNT_MASK 0x0000FFFF
> > +
> > +#define XCSI_VC0INF2R_OFFSET 0x00000064
> > +#define XCSI_VC1INF2R_OFFSET 0x0000006C
> > +#define XCSI_VC2INF2R_OFFSET 0x00000074
> > +#define XCSI_VC3INF2R_OFFSET 0x0000007C
> > +#define XCSI_VCXINF2R_DATATYPE_SHIFT 0
> > +#define XCSI_VCXINF2R_DATATYPE_MASK 0x0000003F
> > +
> > +#define XDPHY_CTRLREG_OFFSET 0x0
> > +#define XDPHY_CTRLREG_DPHYEN_SHIFT 1
> > +#define XDPHY_CTRLREG_DPHYEN_MASK
> BIT(XDPHY_CTRLREG_DPHYEN_SHIFT)
> > +
> > +#define XDPHY_CLKSTATREG_OFFSET 0x18
> > +#define XDPHY_CLKSTATREG_MODE_SHIFT 0
> > +#define XDPHY_CLKSTATREG_MODE_MASK 0x3
> > +#define XDPHY_LOW_POWER_MODE 0x0
> > +#define XDPHY_HI_SPEED_MODE 0x1
> > +#define XDPHY_ESC_MODE 0x2
>
> Can d-phy be used for dsi or as a separate IP, so shoudln't it be
> a separate phy driver?
>

Since only 2 registers of DPHY are being used here, I would prefer to access and modify in this driver itself.

> > +
> > +/*
> > + * Interrupt mask
> > + */
> > +#define XCSI_INTR_MASK (XCSI_ISR_ALLINTR_MASK &
> ~XCSI_ISR_STOP_MASK)
>
> This can be grouped with interrupt register definitions.

Ok.

>
> > +/*
> > + * Timeout for reset
> > + */
> > +#define XCSI_TIMEOUT_VAL (1000) /* us */
>
> Where do we get this value from? If not specified in any spec, this deserves
> a comment how this value is derived.
>

The core takes less than 100 video clock cycles to reset.
So any number greater than that is ok to check for a timeout and error out.

> > +
> > +/*
> > + * Max string length for CSI Data type string
> > + */
> > +#define MAX_XIL_CSIDT_STR_LENGTH 64
> > +
> > +/*
> > + * Maximum number of short packet events per file handle.
> > + */
> > +#define XCSI_MAX_SPKT (512)
>
> Could you please explain how frequently the short packets can arrive?
> '512' seems somewhat large, so some explanation may help.
>

Short packets will contain non image data usually for a frame like timing information for
opening and closing of shutters, triggering flashes, etc. It's a 16 bit data which needs to be
sent to application layer.
I don't know how many of them come across for a frame though.
Hence used a larger number here.

> > +
> > +/* Number of media pads */
> > +#define XILINX_CSI_MEDIA_PADS (2)
> > +
> > +#define XCSI_DEFAULT_WIDTH (1920)
> > +#define XCSI_DEFAULT_HEIGHT (1080)
> > +
> > +/*
> > + * Macro to return "true" or "false" string if bit is set
> > + */
> > +#define XCSI_GET_BITSET_STR(val, mask) (val) & (mask) ? "true" : "false"
> > +
> > +enum csi_datatypes {
> > + MIPI_CSI_DT_FRAME_START_CODE = 0x00,
> > + MIPI_CSI_DT_FRAME_END_CODE,
> > + MIPI_CSI_DT_LINE_START_CODE,
> > + MIPI_CSI_DT_LINE_END_CODE,
> > + MIPI_CSI_DT_SYNC_RSVD_04,
> > + MIPI_CSI_DT_SYNC_RSVD_05,
> > + MIPI_CSI_DT_SYNC_RSVD_06,
> > + MIPI_CSI_DT_SYNC_RSVD_07,
> > + MIPI_CSI_DT_GSPKT_08,
> > + MIPI_CSI_DT_GSPKT_09,
> > + MIPI_CSI_DT_GSPKT_0A,
> > + MIPI_CSI_DT_GSPKT_0B,
> > + MIPI_CSI_DT_GSPKT_0C,
> > + MIPI_CSI_DT_GSPKT_0D,
> > + MIPI_CSI_DT_GSPKT_0E,
> > + MIPI_CSI_DT_GSPKT_0F,
> > + MIPI_CSI_DT_GLPKT_10,
> > + MIPI_CSI_DT_GLPKT_11,
> > + MIPI_CSI_DT_GLPKT_12,
> > + MIPI_CSI_DT_GLPKT_13,
> > + MIPI_CSI_DT_GLPKT_14,
> > + MIPI_CSI_DT_GLPKT_15,
> > + MIPI_CSI_DT_GLPKT_16,
> > + MIPI_CSI_DT_GLPKT_17,
> > + MIPI_CSI_DT_YUV_420_8B,
> > + MIPI_CSI_DT_YUV_420_10B,
> > + MIPI_CSI_DT_YUV_420_8B_LEGACY,
> > + MIPI_CSI_DT_YUV_RSVD,
> > + MIPI_CSI_DT_YUV_420_8B_CSPS,
> > + MIPI_CSI_DT_YUV_420_10B_CSPS,
> > + MIPI_CSI_DT_YUV_422_8B,
> > + MIPI_CSI_DT_YUV_422_10B,
> > + MIPI_CSI_DT_RGB_444,
> > + MIPI_CSI_DT_RGB_555,
> > + MIPI_CSI_DT_RGB_565,
> > + MIPI_CSI_DT_RGB_666,
> > + MIPI_CSI_DT_RGB_888,
> > + MIPI_CSI_DT_RGB_RSVD_25,
> > + MIPI_CSI_DT_RGB_RSVD_26,
> > + MIPI_CSI_DT_RGB_RSVD_27,
> > + MIPI_CSI_DT_RAW_6,
> > + MIPI_CSI_DT_RAW_7,
> > + MIPI_CSI_DT_RAW_8,
> > + MIPI_CSI_DT_RAW_10,
> > + MIPI_CSI_DT_RAW_12,
> > + MIPI_CSI_DT_RAW_14,
> > + MIPI_CSI_DT_RAW_RSVD_2E,
> > + MIPI_CSI_DT_RAW_RSVD_2F,
> > + MIPI_CSI_DT_USER_30,
> > + MIPI_CSI_DT_USER_31,
> > + MIPI_CSI_DT_USER_32,
> > + MIPI_CSI_DT_USER_33,
> > + MIPI_CSI_DT_USER_34,
> > + MIPI_CSI_DT_USER_35,
> > + MIPI_CSI_DT_USER_36,
> > + MIPI_CSI_DT_USER_37,
> > + MIPI_CSI_DT_RSVD_38,
> > + MIPI_CSI_DT_RSVD_39,
> > + MIPI_CSI_DT_RSVD_3A,
> > + MIPI_CSI_DT_RSVD_3B,
> > + MIPI_CSI_DT_RSVD_3C,
> > + MIPI_CSI_DT_RSVD_3D,
> > + MIPI_CSI_DT_RSVD_3E,
> > + MIPI_CSI_DT_RSVD_3F
> > +};
>
> This seems to map to possible values for XCSI_VC*INF1R_OFFSET.
> Then I'm not sure if enum is right type. You can have this under
> the register definition. But up to you.
>

Correct. But these are standard values from the MIPI CSI2 specification.
Hence these are kept out.

> > +
> > +/**
> > + * struct pixel_format - Data Type to string name structure
> > + * @pixelformat: MIPI CSI2 Data type
> > + * @pixelformatstr: String name of Data Type
> > + */
> > +struct pixel_format {
> > + enum csi_datatypes pixelformat;
> > + char pixelformatstr[MAX_XIL_CSIDT_STR_LENGTH];
> > +};
> > +
> > +/**
> > + * struct xcsi2rxss_event - Event log structure
> > + * @mask: Event mask
> > + * @name: Name of the event
> > + * @counter: Count number of events
> > + */
> > +struct xcsi2rxss_event {
> > + u32 mask;
> > + const char * const name;
>
> Nit. Extra space.
>

Ok will remove in next version.

> > + unsigned int counter;
> > +};
> > +
> > +/*
> > + * struct xcsi2rxss_core - Core configuration CSI2 Rx Subsystem device
> structure
> > + * @dev: Platform structure
>
> Incorrect description.

Will update in next version.

>
> > + * @iomem: Base address of subsystem
> > + * @irq: requested irq number
> > + * @dphy_present: Flag for DPHY register interface presence
> > + * @dphy_offset: DPHY registers offset
> > + * @enable_active_lanes: If number of active lanes can be modified
> > + * @max_num_lanes: Maximum number of lanes present
> > + * @vfb: Video Format Bridge enabled or not
> > + * @ppc: pixels per clock
> > + * @vc: Virtual Channel
> > + * @axis_tdata_width: AXI Stream data width
> > + * @datatype: Data type filter
> > + * @pxlformat: String with CSI pixel format from IP
> > + * @num_lanes: Number of lanes requested from application
> > + * @events: Structure to maintain event logs
> > + */
> > +struct xcsi2rxss_core {
> > + struct device *dev;
> > + void __iomem *iomem;
> > + int irq;
> > + u32 dphy_offset;
>
> Is it guaranteed that the dphy register can be addressed with offset to
> the csi register?
>

Yes it is.

> > + bool dphy_present;
> > + bool enable_active_lanes;
> > + u32 max_num_lanes;
> > + bool vfb;
> > + u32 ppc;
>
> This is parsed but not used.
>

This will be removed in next version.

> > + u32 vc;
> > + u32 axis_tdata_width;
>
> This is not used. Do you want to keep it in sync with IP configs?

This will be removed in next version.

>
> > + u32 datatype;
>
> Isn't this enum csi_datatypes?

This will be updated in next version.

>
> > + const char *pxlformat;
> > + u32 num_lanes;
> > + struct xcsi2rxss_event *events;
> > +};
> > +
> > +/**
> > + * struct xcsi2rxss_state - CSI2 Rx Subsystem device structure
> > + * @core: Core structure for MIPI CSI2 Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @formats: Active V4L2 formats on each pad
> > + * @default_format: default V4L2 media bus format
> > + * @vip_format: format information corresponding to the active format
> > + * @event: Holds the short packet event
> > + * @lock: mutex for serializing operations
> > + * @pads: media pads
> > + * @npads: number of pads
> > + * @streaming: Flag for storing streaming state
> > + * @suspended: Flag for storing suspended state
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xcsi2rxss_state {
> > + struct xcsi2rxss_core core;
> > + struct v4l2_subdev subdev;
> > + struct v4l2_ctrl_handler ctrl_handler;
> > + struct v4l2_mbus_framefmt formats[2];
> > + struct v4l2_mbus_framefmt default_format;
> > + const struct xvip_video_format *vip_format;
> > + struct v4l2_event event;
> > + /*
> > + * Used to serialize access.
> > + */
>
> It's a little not clear what access. It would be better to describe
> what the lock protects.

The lock serializes the calls to v4l2 subdev operations like start/stop streaming.

>
> > + struct mutex lock;
> > + struct media_pad pads[XILINX_CSI_MEDIA_PADS];
> > + unsigned int npads;
> > + bool streaming;
> > + bool suspended;
> > +};
> > +
> > +static inline struct xcsi2rxss_state *
> > +to_xcsi2rxssstate(struct v4l2_subdev *subdev)
> > +{
> > + return container_of(subdev, struct xcsi2rxss_state, subdev);
> > +}
> > +
> > +/*
> > + * Register related operations
> > + */
> > +static inline u32 xcsi2rxss_read(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr)
> > +{
> > + return ioread32(xcsi2rxss->iomem + addr);
> > +}
> > +
> > +static inline void xcsi2rxss_write(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr, u32 value)
> > +{
> > + iowrite32(value, xcsi2rxss->iomem + addr);
> > +}
> > +
> > +static inline void xcsi2rxss_clr(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr, u32 clr)
> > +{
> > + xcsi2rxss_write(xcsi2rxss,
> > + addr,
>
> I'd put this in the line above. But maybe it's just me.

Ok I keep it like this so that it is known that there are 3 parameters for this function.

>
> > + xcsi2rxss_read(xcsi2rxss, addr) & ~clr);
> > +}
> > +
> > +static inline void xcsi2rxss_set(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr, u32 set)
> > +{
> > + xcsi2rxss_write(xcsi2rxss,
> > + addr,
> > + xcsi2rxss_read(xcsi2rxss, addr) | set);
> > +}
> > +
> > +static const struct pixel_format pixel_formats[] = {
> > + { MIPI_CSI_DT_YUV_420_8B, "YUV420_8bit" },
> > + { MIPI_CSI_DT_YUV_420_10B, "YUV420_10bit" },
> > + { MIPI_CSI_DT_YUV_420_8B_LEGACY, "Legacy_YUV420_8bit" },
> > + { MIPI_CSI_DT_YUV_420_8B_CSPS, "YUV420_8bit_CSPS" },
> > + { MIPI_CSI_DT_YUV_420_10B_CSPS, "YUV420_10bit_CSPS" },
> > + { MIPI_CSI_DT_YUV_422_8B, "YUV422_8bit" },
> > + { MIPI_CSI_DT_YUV_422_10B, "YUV422_10bit" },
> > + { MIPI_CSI_DT_RGB_444, "RGB444" },
> > + { MIPI_CSI_DT_RGB_555, "RGB555" },
> > + { MIPI_CSI_DT_RGB_565, "RGB565" },
> > + { MIPI_CSI_DT_RGB_666, "RGB666" },
> > + { MIPI_CSI_DT_RGB_888, "RGB888" },
> > + { MIPI_CSI_DT_RAW_6, "RAW6" },
> > + { MIPI_CSI_DT_RAW_7, "RAW7" },
> > + { MIPI_CSI_DT_RAW_8, "RAW8" },
> > + { MIPI_CSI_DT_RAW_10, "RAW10" },
> > + { MIPI_CSI_DT_RAW_12, "RAW12" },
> > + { MIPI_CSI_DT_RAW_14, "RAW14 "}
> > +};
> > +
> > +static struct xcsi2rxss_event xcsi2rxss_events[] = {
> > + { XCSI_ISR_FR_MASK, "Frame Received", 0 },
> > + { XCSI_ISR_ILC_MASK, "Invalid Lane Count Error", 0 },
> > + { XCSI_ISR_SPFIFOF_MASK, "Short Packet FIFO OverFlow Error", 0 },
> > + { XCSI_ISR_SPFIFONE_MASK, "Short Packet FIFO Not Empty", 0 },
> > + { XCSI_ISR_SLBF_MASK, "Streamline Buffer Full Error", 0 },
> > + { XCSI_ISR_STOP_MASK, "Lane Stop State", 0 },
> > + { XCSI_ISR_SOTERR_MASK, "SOT Error", 0 },
> > + { XCSI_ISR_SOTSYNCERR_MASK, "SOT Sync Error", 0 },
> > + { XCSI_ISR_ECC2BERR_MASK, "2 Bit ECC Unrecoverable Error", 0 },
> > + { XCSI_ISR_ECC1BERR_MASK, "1 Bit ECC Recoverable Error", 0 },
> > + { XCSI_ISR_CRCERR_MASK, "CRC Error", 0 },
> > + { XCSI_ISR_DATAIDERR_MASK, "Data Id Error", 0 },
> > + { XCSI_ISR_VC3FSYNCERR_MASK, "Virtual Channel 3 Frame Sync Error",
> 0 },
> > + { XCSI_ISR_VC3FLVLERR_MASK, "Virtual Channel 3 Frame Level Error", 0
> },
> > + { XCSI_ISR_VC2FSYNCERR_MASK, "Virtual Channel 2 Frame Sync Error",
> 0 },
> > + { XCSI_ISR_VC2FLVLERR_MASK, "Virtual Channel 2 Frame Level Error", 0
> },
> > + { XCSI_ISR_VC1FSYNCERR_MASK, "Virtual Channel 1 Frame Sync Error",
> 0 },
> > + { XCSI_ISR_VC1FLVLERR_MASK, "Virtual Channel 1 Frame Level Error", 0
> },
> > + { XCSI_ISR_VC0FSYNCERR_MASK, "Virtual Channel 0 Frame Sync Error",
> 0 },
> > + { XCSI_ISR_VC0FLVLERR_MASK, "Virtual Channel 0 Frame Level Error", 0
> }
> > +};
> > +
> > +#define XMIPICSISS_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events)
> > +
> > +/**
> > + * xcsi2rxss_clr_and_set - Clear and set the register with a bitmask
> > + * @xcsi2rxss: Xilinx MIPI CSI2 Rx Subsystem subdev core struct
> > + * @addr: address of register
> > + * @clr: bitmask to be cleared
> > + * @set: bitmask to be set
> > + *
> > + * Clear a bit(s) of mask @clr in the register at address @addr, then set
> > + * a bit(s) of mask @set in the register after.
> > + */
> > +static void xcsi2rxss_clr_and_set(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr, u32 clr, u32 set)
> > +{
> > + u32 reg;
> > +
> > + reg = xcsi2rxss_read(xcsi2rxss, addr);
> > + reg &= ~clr;
> > + reg |= set;
> > + xcsi2rxss_write(xcsi2rxss, addr, reg);
> > +}
>
> This can be grouped with io operations above.
>

Ok. Will move with other IO operations in next revision.

> > +
> > +/**
> > + * xcsi2rxss_pxlfmtstrtodt - Convert pixel format string got from dts
> > + * to data type.
> > + * @pxlfmtstr: String obtained while parsing device node
> > + *
> > + * This function takes a CSI pixel format string obtained while parsing
> > + * device tree node and converts it to data type.
> > + *
> > + * Eg. "RAW8" string is converted to 0x2A.
> > + * Refer to MIPI CSI2 specification for details.
> > + *
> > + * Return: Equivalent pixel format value from table
> > + */
> > +static u32 xcsi2rxss_pxlfmtstrtodt(const char *pxlfmtstr)
>
> Isn't this return type enum csi_datatypes?

We need to return the pixel format enum and incase not found EINVAL.
A better way I think is to pass the pixel format through a pointer and return 0 (for Success) or EINVAL for failure.

>
> > +{
> > + u32 i;
> > + u32 maxentries = ARRAY_SIZE(pixel_formats);
>
> You can inline ARRAY_SIZE. Up to you.

Ok.

>
> > +
> > + for (i = 0; i < maxentries; i++) {
> > + if (!strncmp(pixel_formats[i].pixelformatstr,
> > + pxlfmtstr, MAX_XIL_CSIDT_STR_LENGTH))
> > + return pixel_formats[i].pixelformat;
> > + }
> > +
> > + return -EINVAL;
>
> The return type is unsigned.

Will correct in next revision.

>
> > +}
> > +
> > +/**
> > + * xcsi2rxss_pxlfmtdttostr - Convert pixel format data type to string.
> > + * @datatype: MIPI CSI-2 Data Type
> > + *
> > + * This function takes a CSI pixel format data type and returns a
> > + * pointer to the string name.
> > + *
> > + * Eg. 0x2A returns "RAW8" string.
> > + * Refer to MIPI CSI2 specification for details.
> > + *
> > + * Return: Equivalent pixel format string from table
> > + */
> > +static const char *xcsi2rxss_pxlfmtdttostr(u32 datatype)
>
> Isn't this argument enum csi_datatypes?
>

Will correct in next revision.

> > +{
> > + u32 i;
> > + u32 maxentries = ARRAY_SIZE(pixel_formats);
> > +
> > + for (i = 0; i < maxentries; i++) {
> > + if (pixel_formats[i].pixelformat == datatype)
> > + return pixel_formats[i].pixelformatstr;
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_enable - Enable or disable the CSI Core
> > + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> > + * @flag: true for enabling, false for disabling
> > + *
> > + * This function enables/disables the MIPI CSI2 Rx Subsystem core.
> > + * After enabling the CSI2 Rx core, the DPHY is enabled in case the
> > + * register interface for it is present.
> > + */
> > +static void xcsi2rxss_enable(struct xcsi2rxss_core *core, bool flag)
> > +{
> > + u32 dphyctrlregoffset = core->dphy_offset + XDPHY_CTRLREG_OFFSET;
> > +
> > + if (flag) {
> > + xcsi2rxss_write(core, XCSI_CCR_OFFSET,
> XCSI_CCR_COREENB_MASK);
> > + if (core->dphy_present)
> > + xcsi2rxss_write(core, dphyctrlregoffset,
> > + XDPHY_CTRLREG_DPHYEN_MASK);
> > + } else {
> > + xcsi2rxss_write(core, XCSI_CCR_OFFSET, 0);
> > + if (core->dphy_present)
> > + xcsi2rxss_write(core, dphyctrlregoffset, 0);
> > + }
> > +}
> > +
> > +/**
> > + * xcsi2rxss_interrupts_enable - Enable or disable CSI interrupts
> > + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> > + * @flag: true for enabling, false for disabling
> > + *
> > + * This function enables/disables the interrupts for the MIPI CSI2
> > + * Rx Subsystem.
> > + */
> > +static void xcsi2rxss_interrupts_enable(struct xcsi2rxss_core *core, bool
> flag)
> > +{
> > + if (flag) {
> > + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> > + xcsi2rxss_write(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
> > + xcsi2rxss_set(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> > + } else {
> > + xcsi2rxss_clr(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
> > + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> > + }
> > +}
>
> It would be cleaner if enable / disable functions are split. But up to you.

Ok will update with split in next revision.

>
> > +
> > +/**
> > + * xcsi2rxss_reset - Does a soft reset of the MIPI CSI2 Rx Subsystem
> > + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> > + *
> > + * Return: 0 - on success OR -ETIME if reset times out
>
> Wouldn't -ETIMEDOUT be better for this? Up to you.

ETIME is better as this function emulates a timer.
ETIMEDOUT is for connection timeout.
Will keep ETIME.

>
> > + */
> > +static int xcsi2rxss_reset(struct xcsi2rxss_core *core)
> > +{
> > + u32 timeout = XCSI_TIMEOUT_VAL;
> > +
> > + xcsi2rxss_set(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
> > +
> > + while (xcsi2rxss_read(core, XCSI_CSR_OFFSET) &
> XCSI_CSR_RIPCD_MASK) {
> > + if (timeout == 0) {
> > + dev_err(core->dev, "Xilinx CSI2 Rx Subsystem Soft Reset
> Timeout!\n");
> > + return -ETIME;
> > + }
> > +
> > + timeout--;
> > + udelay(1);
> > + }
> > +
> > + xcsi2rxss_clr(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
> > + return 0;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2
> > + * @irq: IRQ number
> > + * @dev_id: Pointer to device state
> > + *
> > + * In the interrupt handler, a list of event counters are updated for
> > + * corresponding interrupts. This is useful to get status / debug.
> > + * If the short packet FIFO not empty or overflow interrupt is received
> > + * capture the short packet and notify of event occurrence
> > + *
> > + * Return: IRQ_HANDLED after handling interrupts
> > + */
> > +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *dev_id)
> > +{
> > + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)dev_id;
> > + struct xcsi2rxss_core *core = &state->core;
> > + u32 status;
> > +
> > + status = xcsi2rxss_read(core, XCSI_ISR_OFFSET) & XCSI_INTR_MASK;
> > + dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> > +
> > + if (!status)
> > + return IRQ_NONE;
> > +
> > + if (status & XCSI_ISR_SPFIFONE_MASK) {
> > + memset(&state->event, 0, sizeof(state->event));
> > +
> > + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT;
> > +
> > + *((u32 *)(&state->event.u.data)) =
> > + xcsi2rxss_read(core, XCSI_SPKTR_OFFSET);
> > +
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XCSI_ISR_SPFIFOF_MASK) {
> > + dev_alert(core->dev, "Short packet FIFO overflowed\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > +
> > + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT_OVF;
> > +
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XCSI_ISR_SLBF_MASK) {
> > + dev_alert(core->dev, "Stream Line Buffer Full!\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > +
> > + state->event.type = V4L2_EVENT_XLNXCSIRX_SLBF;
> > +
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XCSI_ISR_ALLINTR_MASK) {
> > + unsigned int i;
> > +
> > + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
> > + if (!(status & core->events[i].mask))
> > + continue;
> > + core->events[i].counter++;
> > + dev_dbg(core->dev, "%s: %d\n", core->events[i].name,
> > + core->events[i].counter);
> > + }
> > + }
> > +
> > + xcsi2rxss_write(core, XCSI_ISR_OFFSET, status);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state)
> > +{
> > + u32 i;
> > +
> > + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++)
> > + state->core.events[i].counter = 0;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_log_counters - Print out the event counters.
> > + * @state: Pointer to device state
> > + *
> > + */
> > +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state)
> > +{
> > + u32 i;
> > +
> > + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
> > + if (state->core.events[i].counter > 0)
> > + v4l2_info(&state->subdev, "%s events: %d\n",
> > + state->core.events[i].name,
> > + state->core.events[i].counter);
> > + }
> > +}
> > +
> > +/**
> > + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver
> > + * @sd: Pointer to V4L2 subdevice structure
> > + *
> > + * This function prints the current status of Xilinx MIPI CSI-2
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xcsi2rxss_log_status(struct v4l2_subdev *sd)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> > + u32 reg, data, i;
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + xcsi2rxss_log_counters(xcsi2rxss);
> > +
> > + v4l2_info(sd, "***** Core Status *****\n");
> > + data = xcsi2rxss_read(core, XCSI_CSR_OFFSET);
> > + v4l2_info(sd, "Short Packet FIFO Full = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFOFULL_MASK));
> > + v4l2_info(sd, "Short Packet FIFO Not Empty = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFONE_MASK));
> > + v4l2_info(sd, "Stream line buffer full = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CSR_SLBF_MASK));
> > + v4l2_info(sd, "Soft reset/Core disable in progress = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CSR_RIPCD_MASK));
> > +
> > + /* Clk & Lane Info */
> > + v4l2_info(sd, "******** Clock Lane Info *********\n");
> > + data = xcsi2rxss_read(core, XCSI_CLKINFR_OFFSET);
> > + v4l2_info(sd, "Clock Lane in Stop State = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CLKINFR_STOP_MASK));
> > +
> > + v4l2_info(sd, "******** Data Lane Info *********\n");
> > + v4l2_info(sd, "Lane\tSoT Error\tSoT Sync Error\tStop State\n");
> > + reg = XCSI_L0INFR_OFFSET;
> > + for (i = 0; i < 4; i++) {
> > + data = xcsi2rxss_read(core, reg);
> > +
> > + v4l2_info(sd, "%d\t%s\t\t%s\t\t%s\n",
> > + i,
> > + XCSI_GET_BITSET_STR(data,
> XCSI_LXINFR_SOTERR_MASK),
> > + data & XCSI_LXINFR_SOTSYNCERR_MASK ? "true" :
> "false",
>
> XCSI_GET_BITSET_STR()?
>
> > + XCSI_GET_BITSET_STR(data,
> XCSI_LXINFR_STOP_MASK));
> > +
> > + reg += 4;
> > + }
> > +
> > + /* Virtual Channel Image Information */
> > + v4l2_info(sd, "********** Virtual Channel Info ************\n");
> > + v4l2_info(sd, "VC\tLine Count\tByte Count\tData Type\n");
> > + reg = XCSI_VC0INF1R_OFFSET;
> > + for (i = 0; i < 4; i++) {
> > + u32 line_count, byte_count, data_type;
> > + char *datatypestr;
> > +
> > + /* Get line and byte count from VCXINFR1 Register */
> > + data = xcsi2rxss_read(core, reg);
> > + byte_count = (data & XCSI_VCXINF1R_BYTECOUNT_MASK) >>
> > + XCSI_VCXINF1R_BYTECOUNT_SHIFT;
> > + line_count = (data & XCSI_VCXINF1R_LINECOUNT_MASK) >>
> > + XCSI_VCXINF1R_LINECOUNT_SHIFT;
> > +
> > + /* Get data type from VCXINFR2 Register */
> > + reg += 4;
> > + data = xcsi2rxss_read(core, reg);
> > + data_type = (data & XCSI_VCXINF2R_DATATYPE_MASK) >>
> > + XCSI_VCXINF2R_DATATYPE_SHIFT;
> > + datatypestr = (char *)xcsi2rxss_pxlfmtdttostr(data_type);
> > +
> > + v4l2_info(sd, "%d\t%d\t\t%d\t\t%s\n",
> > + i, line_count, byte_count, datatypestr);
> > +
> > + /* Move to next pair of VC Info registers */
> > + reg += 4;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * xcsi2rxss_subscribe_event - Subscribe to the custom short packet
> > + * receive event.
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 File Handle
> > + * @sub: Subcribe event structure
> > + *
> > + * There are two types of events to be subscribed.
> > + *
> > + * First is to register for receiving a short packet.
> > + * The short packets received are queued up in a FIFO.
> > + * On reception of a short packet, an event will be generated
> > + * with the short packet contents copied to its data area.
> > + * Application subscribed to this event will poll for POLLPRI.
> > + * On getting the event, the app dequeues the event to get the short packet
> > + * data.
> > + *
> > + * Second is to register for Short packet FIFO overflow
> > + * In case the rate of receiving short packets is high and
> > + * the short packet FIFO overflows, this event will be triggered.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xcsi2rxss_subscribe_event(struct v4l2_subdev *sd,
> > + struct v4l2_fh *fh,
> > + struct v4l2_event_subscription *sub)
> > +{
> > + int ret;
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + switch (sub->type) {
> > + case V4L2_EVENT_XLNXCSIRX_SPKT:
> > + case V4L2_EVENT_XLNXCSIRX_SPKT_OVF:
> > + case V4L2_EVENT_XLNXCSIRX_SLBF:
> > + ret = v4l2_event_subscribe(fh, sub, XCSI_MAX_SPKT, NULL);
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_unsubscribe_event - Unsubscribe from all events registered
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 file handle
> > + * @sub: pointer to Event unsubscription structure
> > + *
> > + * Return: zero on success, else a negative error code.
> > + */
> > +static int xcsi2rxss_unsubscribe_event(struct v4l2_subdev *sd,
> > + struct v4l2_fh *fh,
> > + struct v4l2_event_subscription *sub)
> > +{
> > + int ret = 0;
>
> No need to initialize.

Ok. Will remove in next version.

>
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > + ret = v4l2_event_unsubscribe(fh, sub);
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_s_ctrl - This is used to set the Xilinx MIPI CSI-2 V4L2 controls
> > + * @ctrl: V4L2 control to be set
> > + *
> > + * This function is used to set the V4L2 controls for the Xilinx MIPI
> > + * CSI-2 Rx Subsystem. It is used to set the active lanes in the system.
> > + * The event counters can be reset.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xcsi2rxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + int ret = 0;
> > + u32 timeout = XCSI_TIMEOUT_VAL;
> > + u32 active_lanes = 1;
> > +
> > + struct xcsi2rxss_state *xcsi2rxss =
> > + container_of(ctrl->handler,
> > + struct xcsi2rxss_state, ctrl_handler);
> > + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_MIPICSISS_ACT_LANES:
> > + /*
> > + * This will be called only when "Enable Active Lanes" parameter
> > + * is set in design
> > + */
>
> Then shouldn't it be checked here?

This control is created only if the enable_active_lanes parameter is set in design.
So no need to check.

>
> > + xcsi2rxss_clr_and_set(core, XCSI_PCR_OFFSET,
> > + XCSI_PCR_ACTLANES_MASK, ctrl->val - 1);
> > +
> > + /*
> > + * If the core is enabled, wait for active lanes to be
> > + * set.
> > + *
> > + * If core is disabled or there is no clock from DPHY Tx
> > + * then the read back won't reflect the updated value
> > + * as the PPI clock will not be present.
> > + */
> > +
> > + if (core->dphy_present) {
> > + u32 dphyclkstatregoffset = core->dphy_offset +
> > +
> XDPHY_CLKSTATREG_OFFSET;
> > +
> > + u32 dphyclkstat =
> > + xcsi2rxss_read(core, dphyclkstatregoffset) &
> > + XDPHY_CLKSTATREG_MODE_MASK;
> > +
> > + u32 coreenable =
> > + xcsi2rxss_read(core, XCSI_CCR_OFFSET) &
> > + XCSI_CCR_COREENB_MASK;
> > +
> > + char lpmstr[] = "Low Power";
> > + char hsmstr[] = "High Speed";
> > + char esmstr[] = "Escape";
>
> These can be removed and inlined directly.
Ok
>
> > + char *modestr;
> > +
> > + switch (dphyclkstat) {
> > + case 0:
> > + modestr = lpmstr;
> > + break;
> > + case 1:
> > + modestr = hsmstr;
> > + break;
> > + case 2:
> > + modestr = esmstr;
> > + break;
> > + default:
> > + modestr = NULL;
> > + break;
> > + }
> > +
> > + dev_dbg(core->dev, "DPHY Clock Lane in %s mode\n",
> > + modestr);
>
> I'd do an array of strings, and do modestr[dphyclkstat]. But the dphyclkstate
> should be checked to be in valid range

Ok will update as suggested in next version.

>
> > +
> > + if (dphyclkstat == XDPHY_HI_SPEED_MODE &&
> > + coreenable) {
> > + /* Wait for core to apply new active lanes */
> > + while (timeout--)
> > + udelay(1);
>
> udelay(1000). Please specify where the delay is defined or from
> some heuristics.

The write and read paths to the number of active lanes is different in the HW.
It takes 3 PPI clocks + 3 register clocks.

>
> > +
> > + active_lanes =
> > + xcsi2rxss_read(core, XCSI_PCR_OFFSET);
> > + active_lanes &=
> XCSI_PCR_ACTLANES_MASK;
> > + active_lanes++;
> > +
> > + if (active_lanes != (u32)ctrl->val) {
> > + dev_err(core->dev, "Failed to set active
> lanes!\n");
> > + ret = -EAGAIN;
> > + }
> > + }
> > + } else {
> > + dev_dbg(core->dev, "No read back as no DPHY
> present.\n");
>
> This should be more verbose level, ex info.
>
Ok will change to dev_info.

> > + }
> > +
> > + dev_dbg(core->dev, "Set active lanes: requested = %d, active =
> %d\n",
> > + ctrl->val, active_lanes);
> > + break;
> > + case V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS:
> > + xcsi2rxss_reset_event_counters(xcsi2rxss);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_g_volatile_ctrl - get the Xilinx MIPI CSI-2 Rx controls
> > + * @ctrl: Pointer to V4L2 control
> > + *
> > + * This is used to get the number of frames received by the Xilinx
> > + * MIPI CSI-2 Rx.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xcsi2rxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + int ret = 0;
> > + struct xcsi2rxss_state *xcsi2rxss =
> > + container_of(ctrl->handler,
> > + struct xcsi2rxss_state, ctrl_handler);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER:
> > + ctrl->val = xcsi2rxss->core.events[0].counter;
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *xcsi2rxss)
> > +{
> > + int ret;
> > +
> > + xcsi2rxss_enable(&xcsi2rxss->core, true);
> > +
> > + ret = xcsi2rxss_reset(&xcsi2rxss->core);
> > + if (ret < 0)
> > + return ret;
> > +
> > + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, true);
> > +
> > + return 0;
> > +}
> > +
> > +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *xcsi2rxss)
> > +{
> > + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, false);
> > + xcsi2rxss_enable(&xcsi2rxss->core, false);
> > +}
> > +
> > +/**
> > + * xcsi2rxss_s_stream - It is used to start/stop the streaming.
> > + * @sd: V4L2 Sub device
> > + * @enable: Flag (True / False)
> > + *
> > + * This function controls the start or stop of streaming for the
> > + * Xilinx MIPI CSI-2 Rx Subsystem provided the device isn't in
> > + * suspended state.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable)
> > +{
> > + int ret = 0;
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + if (xcsi2rxss->suspended) {
> > + ret = -EBUSY;
> > + goto unlock;
> > + }
> > +
> > + if (enable) {
> > + if (!xcsi2rxss->streaming) {
> > + /* reset the event counters */
> > + xcsi2rxss_reset_event_counters(xcsi2rxss);
> > +
> > + ret = xcsi2rxss_start_stream(xcsi2rxss);
> > + if (ret == 0)
> > + xcsi2rxss->streaming = true;
>
>
> Checking and Setting this 'streaming' flag in xcsi2rxss_start_stream()
> would make more sense.
>

The event counters are to be reset before starting stream.
The assignment will be moved inside the start_stream().

> > + }
> > + } else {
> > + if (xcsi2rxss->streaming) {
> > + xcsi2rxss_stop_stream(xcsi2rxss);
> > + xcsi2rxss->streaming = false;
> > + }
> > + }
> > +unlock:
> > + mutex_unlock(&xcsi2rxss->lock);
> > + return ret;
> > +}
> > +
> > +static struct v4l2_mbus_framefmt *
> > +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss,
> > + struct v4l2_subdev_pad_config *cfg,
> > + unsigned int pad, u32 which)
> > +{
> > + switch (which) {
> > + case V4L2_SUBDEV_FORMAT_TRY:
> > + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg,
> pad);
> > + case V4L2_SUBDEV_FORMAT_ACTIVE:
> > + return &xcsi2rxss->formats[pad];
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +/**
> > + * xcsi2rxss_get_format - Get the pad format
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + * @fmt: Pointer to pad level media bus format
> > + *
> > + * This function is used to get the pad format information.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xcsi2rxss_get_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
> > + fmt->pad, fmt->which);
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_set_format - This is used to set the pad format
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + * @fmt: Pointer to pad level media bus format
> > + *
> > + * This function is used to set the pad format.
> > + * Since the pad format is fixed in hardware, it can't be
> > + * modified on run time. So when a format set is requested by
> > + * application, all parameters except the format type is
> > + * saved for the pad and the original pad format is sent
> > + * back to the application.
>
> You can have a little more characters per line. :-)
>
Ok. Will update in next revision.

> > + *
> > + * Return: 0 on success
> > + */
> > +static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct v4l2_mbus_framefmt *__format;
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> > + u32 code;
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + /*
> > + * Only the format->code parameter matters for CSI as the
> > + * CSI format cannot be changed at runtime.
> > + * Ensure that format to set is copied to over to CSI pad format
> > + */
> > + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
> > + fmt->pad, fmt->which);
> > +
> > + /* Save the pad format code */
> > + code = __format->code;
> > +
> > + /* If the bayer pattern to be set is SXXXX8 then only 1x8 type
> > + * is supported and core's data type doesn't matter.
> > + * In case the bayer pattern being set is SXXX10 then only
> > + * 1x10 type are supported and core should be configured for RAW10.
> > + * In case the bayer pattern being set is SXXX12 then only
> > + * 1x12 type are supported and core should be configured for RAW12.
> > + *
> > + * Otherwise don't allow change.
> > + */
>
> Nit. Mutiline comment style.
>

Ok will update in next revision.

> > + if ((fmt->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGBRG8_1X8 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGRBG8_1X8 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SRGGB8_1X8) ||
> > + (core->datatype == MIPI_CSI_DT_RAW_10 &&
> > + (fmt->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGBRG10_1X10 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGRBG10_1X10 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SRGGB10_1X10)) ||
> > + (core->datatype == MIPI_CSI_DT_RAW_12 &&
> > + (fmt->format.code == MEDIA_BUS_FMT_SBGGR12_1X12 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGBRG12_1X12 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGRBG12_1X12 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SRGGB12_1X12))) {
> > + /* Copy over the format to be set */
> > + *__format = fmt->format;
> > + } else {
> > + /* Restore the original pad format code */
> > + fmt->format.code = code;
> > + __format->code = code;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_open - Called on v4l2_open()
> > + * @sd: Pointer to V4L2 sub device structure
> > + * @fh: Pointer to V4L2 File handle
> > + *
> > + * This function is called on v4l2_open(). It sets the default format
> > + * for both pads.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xcsi2rxss_open(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_fh *fh)
> > +{
> > + struct v4l2_mbus_framefmt *format;
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> > + *format = xcsi2rxss->default_format;
> > +
> > + format = v4l2_subdev_get_try_format(sd, fh->pad, 1);
> > + *format = xcsi2rxss->default_format;
> > +
> > + return 0;
> > +}
> > +
> > +static int xcsi2rxss_close(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_fh *fh)
> > +{
> > + return 0;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Media Operations
> > + */
> > +
> > +static const struct media_entity_operations xcsi2rxss_media_ops = {
> > + .link_validate = v4l2_subdev_link_validate
> > +};
> > +
> > +static const struct v4l2_ctrl_ops xcsi2rxss_ctrl_ops = {
> > + .g_volatile_ctrl = xcsi2rxss_g_volatile_ctrl,
> > + .s_ctrl = xcsi2rxss_s_ctrl
> > +};
> > +
> > +static struct v4l2_ctrl_config xcsi2rxss_ctrls[] = {
> > + {
> > + .ops = &xcsi2rxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_MIPICSISS_ACT_LANES,
> > + .name = "MIPI CSI2 Rx Subsystem: Active Lanes",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 1,
> > + .max = 4,
> > + .step = 1,
> > + .def = 1,
> > + }, {
> > + .ops = &xcsi2rxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER,
> > + .name = "MIPI CSI2 Rx Subsystem: Frames Received Counter",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFFFFFF,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xcsi2rxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS,
> > + .name = "MIPI CSI2 Rx Subsystem: Reset Counters",
> > + .type = V4L2_CTRL_TYPE_BUTTON,
> > + .min = 0,
> > + .max = 1,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
> > + }
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = {
> > + .log_status = xcsi2rxss_log_status,
> > + .subscribe_event = xcsi2rxss_subscribe_event,
> > + .unsubscribe_event = xcsi2rxss_unsubscribe_event
> > +};
> > +
> > +static struct v4l2_subdev_video_ops xcsi2rxss_video_ops = {
> > + .s_stream = xcsi2rxss_s_stream
> > +};
> > +
> > +static struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = {
> > + .get_fmt = xcsi2rxss_get_format,
> > + .set_fmt = xcsi2rxss_set_format,
> > +};
> > +
> > +static struct v4l2_subdev_ops xcsi2rxss_ops = {
> > + .core = &xcsi2rxss_core_ops,
> > + .video = &xcsi2rxss_video_ops,
> > + .pad = &xcsi2rxss_pad_ops
> > +};
> > +
> > +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = {
> > + .open = xcsi2rxss_open,
> > + .close = xcsi2rxss_close
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Power Management
> > + */
> > +
> > +/*
> > + * xcsi2rxss_pm_suspend - Function called on Power Suspend
> > + * @dev: Pointer to device structure
> > + *
> > + * On power suspend the CSI-2 Core is disabled if the device isn't
> > + * in suspended state and is streaming.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int __maybe_unused xcsi2rxss_pm_suspend(struct device *dev)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + if (!xcsi2rxss->suspended && xcsi2rxss->streaming)
> > + xcsi2rxss_clr(&xcsi2rxss->core,
> > + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
>
> Isn't it needed to store and restore the register values? The registers will be
> reset to default When suspending the FPGA.
>

Ok. Will add this in the next version.

> > +
> > + xcsi2rxss->suspended = true;
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * xcsi2rxss_pm_resume - Function called on Power Resume
> > + * @dev: Pointer to device structure
> > + *
> > + * On power resume the CSI-2 Core is enabled when it is in suspended state
> > + * and prior to entering suspended state it was streaming.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int __maybe_unused xcsi2rxss_pm_resume(struct device *dev)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + if (xcsi2rxss->suspended && xcsi2rxss->streaming)
> > + xcsi2rxss_set(&xcsi2rxss->core,
> > + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
> > +
> > + xcsi2rxss->suspended = false;
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > + return 0;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss)
> > +{
> > + struct device_node *node = xcsi2rxss->core.dev->of_node;
> > + struct device_node *ports = NULL;
> > + struct device_node *port = NULL;
> > + unsigned int nports = 0;
> > + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> > + int ret;
> > + bool iic_present;
> > +
> > + core->dphy_present = of_property_read_bool(node, "xlnx,dphy-
> present");
> > + dev_dbg(core->dev, "DPHY present property = %s\n",
> > + core->dphy_present ? "Present" : "Absent");
> > +
> > + iic_present = of_property_read_bool(node, "xlnx,iic-present");
> > + dev_dbg(core->dev, "IIC present property = %s\n",
> > + iic_present ? "Present" : "Absent");
> > +
> > + if (core->dphy_present) {
> > + if (iic_present)
> > + core->dphy_offset = 0x20000;
> > + else
> > + core->dphy_offset = 0x10000;
>
> Could you please when this information can be found? I couldn't find any
> relevant information in the product guide. For example, in the dt binding
> example, this goes beyond the specified size of register, 0x20000.
>

Table 2-2 Sub-Core Address Offsets in Section Register Space page 18 mentions this.

> > + }
> > +
> > + ret = of_property_read_u32(node, "xlnx,max-lanes",
> > + &core->max_num_lanes);
> > + if (ret < 0) {
> > + dev_err(core->dev, "missing xlnx,max-lanes property\n");
> > + return ret;
> > + }
> > +
> > + if (core->max_num_lanes > 4 || core->max_num_lanes < 1) {
> > + dev_err(core->dev, "%d max lanes : invalid xlnx,max-lanes
> property\n",
> > + core->max_num_lanes);
> > + return -EINVAL;
> > + }
> > +
> > + ret = of_property_read_u32(node, "xlnx,vc", &core->vc);
> > + if (ret < 0) {
> > + dev_err(core->dev, "missing xlnx,vc property\n");
> > + return ret;
> > + }
> > + if (core->vc > 4) {
> > + dev_err(core->dev, "invalid virtual channel property value.\n");
> > + return -EINVAL;
> > + }
> > +
> > + core->enable_active_lanes =
> > + of_property_read_bool(node, "xlnx,en-active-lanes");
> > + dev_dbg(core->dev, "Enable active lanes property = %s\n",
> > + core->enable_active_lanes ? "Present" : "Absent");
> > +
> > + ret = of_property_read_string(node, "xlnx,csi-pxl-format",
> > + &core->pxlformat);
> > + if (ret < 0) {
> > + dev_err(core->dev, "missing xlnx,csi-pxl-format property\n");
> > + return ret;
> > + }
> > +
> > + core->datatype = xcsi2rxss_pxlfmtstrtodt(core->pxlformat);
> > + if (core->datatype < MIPI_CSI_DT_YUV_420_8B ||
> > + core->datatype > MIPI_CSI_DT_RAW_14) {
> > + dev_err(core->dev, "Invalid xlnx,csi-pxl-format string\n");
> > + return -EINVAL;
> > + }
> > +
> > + core->vfb = of_property_read_bool(node, "xlnx,vfb");
> > + dev_dbg(core->dev, "Video Format Bridge property = %s\n",
> > + core->vfb ? "Present" : "Absent");
> > +
> > + if (core->vfb) {
> > + ret = of_property_read_u32(node, "xlnx,ppc", &core->ppc);
> > + if (ret < 0 || !(core->ppc == 1 || core->ppc == 2 ||
> > + core->ppc == 4)) {
> > + dev_err(core->dev, "Invalid xlnx,ppc property ret = %d
> ppc = %d\n",
> > + ret, core->ppc);
> > + return -EINVAL;
> > + }
> > + }
> > +
> > + ports = of_get_child_by_name(node, "ports");
> > + if (!ports)
> > + ports = node;
> > +
> > + for_each_child_of_node(ports, port) {
> > + int ret;
> > + const struct xvip_video_format *format;
> > + struct device_node *endpoint;
> > + struct v4l2_fwnode_endpoint v4lendpoint;
> > +
> > + if (!port->name || of_node_cmp(port->name, "port"))
> > + continue;
> > +
> > + /*
> > + * Currently only a subset of VFB enabled formats present in
> > + * xvip are supported in the driver.
> > + *
> > + * If the VFB is disabled, the pixels per clock don't matter.
> > + * The data width is either 32 or 64 bit as selected in design.
> > + *
> > + * For e.g. If Data Type is RGB888, VFB is disabled and
> > + * data width is 32 bits.
> > + *
> > + * Clk Cycle | Byte 0 | Byte 1 | Byte 2 | Byte 3
> > + * -----------+----------+----------+----------+----------
> > + * 1 | B0 | G0 | R0 | B1
> > + * 2 | G1 | R1 | B2 | G2
> > + * 3 | R2 | B3 | G3 | R3
> > + */
> > + format = xvip_of_get_format(port);
> > + if (IS_ERR(format)) {
> > + dev_err(core->dev, "invalid format in DT");
> > + return PTR_ERR(format);
> > + }
> > +
> > + if (core->vfb &&
> > + format->vf_code != XVIP_VF_YUV_422 &&
> > + format->vf_code != XVIP_VF_RBG &&
> > + format->vf_code != XVIP_VF_MONO_SENSOR) {
> > + dev_err(core->dev, "Invalid UG934 video format
> set.\n");
> > + return -EINVAL;
> > + }
> > +
> > + /* Get and check the format description */
> > + if (!xcsi2rxss->vip_format) {
> > + xcsi2rxss->vip_format = format;
> > + } else if (xcsi2rxss->vip_format != format) {
> > + dev_err(core->dev, "in/out format mismatch in DT");
> > + return -EINVAL;
> > + }
> > +
> > + endpoint = of_get_next_child(port, NULL);
> > + if (!endpoint) {
> > + dev_err(core->dev, "No port at\n");
> > + return -EINVAL;
> > + }
> > +
> > + ret =
> v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
> > + &v4lendpoint);
>
> You can have a single of_node_put(endpoint); here.

Right. Will update it in next revision.

>
> > + if (ret) {
> > + of_node_put(endpoint);
> > + return ret;
> > + }
> > +
> > + of_node_put(endpoint);
> > + dev_dbg(core->dev, "%s : port %d bus type = %d\n",
> > + __func__, nports, v4lendpoint.bus_type);
> > +
> > + if (v4lendpoint.bus_type == V4L2_MBUS_CSI2) {
> > + dev_dbg(core->dev, "%s : base.port = %d base.id =
> %d\n",
> > + __func__, v4lendpoint.base.port,
> > + v4lendpoint.base.id);
> > +
> > + dev_dbg(core->dev, "%s : mipi number lanes = %d\n",
> > + __func__,
> > + v4lendpoint.bus.mipi_csi2.num_data_lanes);
> > + } else {
> > + dev_dbg(core->dev, "%s : Not a CSI2 bus\n", __func__);
> > + }
> > +
> > + /* Count the number of ports. */
> > + nports++;
> > + }
> > +
> > + if (nports != 2) {
> > + dev_err(core->dev, "invalid number of ports %u\n", nports);
> > + return -EINVAL;
> > + }
> > + xcsi2rxss->npads = nports;
> > +
> > + /*Register interrupt handler */
>
> Nit. A space.

Noted and will be updated.

>
> > + core->irq = irq_of_parse_and_map(node, 0);
> > +
> > + ret = devm_request_irq(core->dev, core->irq, xcsi2rxss_irq_handler,
> > + IRQF_SHARED, "xilinx-csi2rxss", xcsi2rxss);
> > + if (ret) {
> > + dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int xcsi2rxss_probe(struct platform_device *pdev)
> > +{
> > + struct v4l2_subdev *subdev;
> > + struct xcsi2rxss_state *xcsi2rxss;
> > + struct resource *res;
> > +
> > + u32 i;
> > + int ret;
> > + int num_ctrls;
> > +
> > + xcsi2rxss = devm_kzalloc(&pdev->dev, sizeof(*xcsi2rxss), GFP_KERNEL);
> > + if (!xcsi2rxss)
> > + return -ENOMEM;
> > +
> > + mutex_init(&xcsi2rxss->lock);
> > +
> > + xcsi2rxss->core.dev = &pdev->dev;
> > +
> > + ret = xcsi2rxss_parse_of(xcsi2rxss);
> > + if (ret < 0)
> > + return ret;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + xcsi2rxss->core.iomem = devm_ioremap_resource(xcsi2rxss->core.dev,
> res);
> > + if (IS_ERR(xcsi2rxss->core.iomem))
> > + return PTR_ERR(xcsi2rxss->core.iomem);
> > +
> > + /*
> > + * Reset and initialize the core.
> > + */
> > + xcsi2rxss_reset(&xcsi2rxss->core);
> > +
> > + xcsi2rxss->core.events = (struct xcsi2rxss_event *)&xcsi2rxss_events;
>
> Nit. There are double spaces here.

Noted and will be updated.

>
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + xcsi2rxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> > + xcsi2rxss->pads[1].flags = MEDIA_PAD_FL_SINK;
> > +
> > + /* Initialize the default format */
> > + memset(&xcsi2rxss->default_format, 0,
> > + sizeof(xcsi2rxss->default_format));
> > + xcsi2rxss->default_format.code = xcsi2rxss->vip_format->code;
> > + xcsi2rxss->default_format.field = V4L2_FIELD_NONE;
> > + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB;
> > + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH;
> > + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT;
> > +
> > + xcsi2rxss->formats[0] = xcsi2rxss->default_format;
> > + xcsi2rxss->formats[1] = xcsi2rxss->default_format;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + subdev = &xcsi2rxss->subdev;
> > + v4l2_subdev_init(subdev, &xcsi2rxss_ops);
> > +
> > + subdev->dev = &pdev->dev;
> > + subdev->internal_ops = &xcsi2rxss_internal_ops;
> > + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
> > +
> > + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS |
> V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +
> > + subdev->entity.ops = &xcsi2rxss_media_ops;
> > +
> > + v4l2_set_subdevdata(subdev, xcsi2rxss);
> > +
> > + ret = media_entity_pads_init(&subdev->entity, 2, xcsi2rxss->pads);
> > + if (ret < 0)
> > + goto error;
> > +
> > + /*
> > + * In case the Enable Active Lanes config parameter is not set,
> > + * dynamic lane reconfiguration is not allowed.
> > + * So V4L2_CID_XILINX_MIPICSISS_ACT_LANES ctrl will not be
> registered.
> > + * Accordingly allocate the number of controls
> > + */
> > + num_ctrls = ARRAY_SIZE(xcsi2rxss_ctrls);
> > +
> > + if (!xcsi2rxss->core.enable_active_lanes)
> > + num_ctrls--;
> > +
> > + dev_dbg(xcsi2rxss->core.dev, "# of ctrls = %d\n", num_ctrls);
> > +
> > + v4l2_ctrl_handler_init(&xcsi2rxss->ctrl_handler, num_ctrls);
> > +
> > + for (i = 0; i < ARRAY_SIZE(xcsi2rxss_ctrls); i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + if (xcsi2rxss_ctrls[i].id ==
> > + V4L2_CID_XILINX_MIPICSISS_ACT_LANES) {
> > + if (xcsi2rxss->core.enable_active_lanes) {
> > + xcsi2rxss_ctrls[i].max =
> > + xcsi2rxss->core.max_num_lanes;
> > + } else {
> > + /* Don't register control */
> > + dev_dbg(xcsi2rxss->core.dev,
> > + "Skip active lane control\n");
> > + continue;
> > + }
>
> I'd do this without else:
> if (!xcsi2rxss->core.enable_active_lanes) {
> /* Don't register control */
> dev_dbg(xcsi2rxss->core.dev,
> "Skip active lane control\n");
> continue;
> }
> xcsi2rxss_ctrls[i].max = xcsi2rxss->core.max_num_lanes;
>
> > + }
> > +
> > + dev_dbg(xcsi2rxss->core.dev, "%d ctrl = 0x%x\n",
> > + i, xcsi2rxss_ctrls[i].id);
> > + ctrl = v4l2_ctrl_new_custom(&xcsi2rxss->ctrl_handler,
> > + &xcsi2rxss_ctrls[i], NULL);
> > + if (!ctrl) {
> > + dev_err(xcsi2rxss->core.dev, "Failed for %s ctrl\n",
> > + xcsi2rxss_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > +
> > + dev_dbg(xcsi2rxss->core.dev, "# v4l2 ctrls registered = %d\n", i - 1);
> > +
> > + if (xcsi2rxss->ctrl_handler.error) {
> > + dev_err(&pdev->dev, "failed to add controls\n");
> > + ret = xcsi2rxss->ctrl_handler.error;
> > + goto error;
> > + }
> > +
> > + subdev->ctrl_handler = &xcsi2rxss->ctrl_handler;
> > +
> > + ret = v4l2_ctrl_handler_setup(&xcsi2rxss->ctrl_handler);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "failed to set controls\n");
> > + goto error;
> > + }
> > +
> > + platform_set_drvdata(pdev, xcsi2rxss);
> > +
> > + dev_info(xcsi2rxss->core.dev, "Xilinx CSI2 Rx Subsystem device
> found!\n");
> > +
> > + ret = v4l2_async_register_subdev(subdev);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "failed to register subdev\n");
> > + goto error;
> > + }
> > +
> > + /* default states for streaming and suspend */
> > + xcsi2rxss->streaming = false;
> > + xcsi2rxss->suspended = false;
>
> Are these needed?

Not needed. Will remove.

>
> > + return 0;
> > +
> > +error:
> > + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + mutex_destroy(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +static int xcsi2rxss_remove(struct platform_device *pdev)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev);
> > + struct v4l2_subdev *subdev = &xcsi2rxss->subdev;
> > +
> > + v4l2_async_unregister_subdev(subdev);
> > + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + mutex_destroy(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(xcsi2rxss_pm_ops,
> > + xcsi2rxss_pm_suspend, xcsi2rxss_pm_resume);
> > +
> > +static const struct of_device_id xcsi2rxss_of_id_table[] = {
> > + { .compatible = "xlnx,mipi-csi2-rx-subsystem-2.0" },
> > + { .compatible = "xlnx,mipi-csi2-rx-subsystem-3.0" },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table);
> > +
> > +static struct platform_driver xcsi2rxss_driver = {
> > + .driver = {
> > + .name = "xilinx-csi2rxss",
> > + .pm = &xcsi2rxss_pm_ops,
> > + .of_match_table = xcsi2rxss_of_id_table,
> > + },
> > + .probe = xcsi2rxss_probe,
> > + .remove = xcsi2rxss_remove,
> > +};
> > +module_platform_driver(xcsi2rxss_driver);
> > +MODULE_AUTHOR("Vishal Sagar <[email protected]>");
> > +MODULE_DESCRIPTION("Xilinx MIPI CSI2 Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-csi2rxss.h b/include/uapi/linux/xilinx-
> csi2rxss.h
> > new file mode 100644
> > index 0000000..973d5b46
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-csi2rxss.h
>
> Would it make sense to create one generic file for Xilinx specific events?
>

Ok will add the events in a separate Xilinx specific event header file.
Thanks for the review.

Regards
Vishal Sagar

> Thanks,
> -hyun
>
> > @@ -0,0 +1,25 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > +/*
> > + * Events from Xilinx MIPI CSI2 Rx Subsystem exposed to V4L2 application.
> > + *
> > + * Copyright (C) 2016 - 2018 Xilinx, Inc.
> > + * Author: Vishal Sagar <[email protected]>
> > + */
> > +#ifndef __UAPI_XILINX_CSI2RXSS_H__
> > +#define __UAPI_XILINX_CSI2RXSS_H__
> > +
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * Events
> > + *
> > + * V4L2_EVENT_XLNXCSIRX_SPKT: Short packet received
> > + * V4L2_EVENT_XLNXCSIRX_SPKT_OVF: Short packet FIFO overflow
> > + * V4L2_EVENT_XLNXCSIRX_SLBF: Stream line buffer full
> > + */
> > +#define V4L2_EVENT_XLNXCSIRX_CLASS (V4L2_EVENT_PRIVATE_START |
> 0x100)
> > +#define V4L2_EVENT_XLNXCSIRX_SPKT (V4L2_EVENT_XLNXCSIRX_CLASS
> | 0x1)
> > +#define V4L2_EVENT_XLNXCSIRX_SPKT_OVF
> (V4L2_EVENT_XLNXCSIRX_CLASS | 0x2)
> > +#define V4L2_EVENT_XLNXCSIRX_SLBF (V4L2_EVENT_XLNXCSIRX_CLASS
> | 0x3)
> > +
> > +#endif /* __UAPI_XILINX_CSI2RXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index b6441fe..4ca3b44 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -71,4 +71,18 @@
> > /* Noise level */
> > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> (V4L2_CID_XILINX_TPG + 17)
> >
> > +/*
> > + * Xilinx MIPI CSI2 Rx Subsystem
> > + */
> > +
> > +/* Base ID */
> > +#define V4L2_CID_XILINX_MIPICSISS (V4L2_CID_USER_BASE +
> 0xc080)
> > +
> > +/* Active Lanes */
> > +#define V4L2_CID_XILINX_MIPICSISS_ACT_LANES
> (V4L2_CID_XILINX_MIPICSISS + 1)
> > +/* Frames received since streaming is set */
> > +#define V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER
> (V4L2_CID_XILINX_MIPICSISS + 2)
> > +/* Reset all event counters */
> > +#define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS
> (V4L2_CID_XILINX_MIPICSISS + 3)
> > +
> > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > --
> > 2.7.4
> >
> >
>



2019-01-08 13:38:09

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem

Hi Vishal,

The patchset hard escaped me somehow earlier and your reply to Rob made me
notice it again. Thanks. :-)

On Wed, May 30, 2018 at 12:24:43AM +0530, Vishal Sagar wrote:
> Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem.
>
> The Xilinx MIPI CSI-2 Rx Subsystem consists of a DPHY, CSI-2 Rx, an
> optional I2C controller and an optional Video Format Bridge (VFB). The
> active lanes can be configured at run time if enabled in the IP. The
> DPHY register interface may also be enabled.
>
> Signed-off-by: Vishal Sagar <[email protected]>
> ---
> .../bindings/media/xilinx/xlnx,csi2rxss.txt | 117 +++++++++++++++++++++
> 1 file changed, 117 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
>
> diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> new file mode 100644
> index 0000000..31ed721
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> @@ -0,0 +1,117 @@
> +

Extra newline.

> +Xilinx MIPI CSI2 Receiver Subsystem Device Tree Bindings
> +--------------------------------------------------------
> +
> +The Xilinx MIPI CSI2 Receiver Subsystem is used to capture MIPI CSI2 traffic
> +from compliant camera sensors and send the output as AXI4 Stream video data
> +for image processing.
> +
> +The subsystem consists of a MIPI DPHY in slave mode which captures the
> +data packets. This is passed along the MIPI CSI2 Rx IP which extracts the
> +packet data. This data is taken in by the Video Format Bridge (VFB),
> +if selected, and converted into AXI4 Stream video data at selected
> +pixels per clock as per AXI4-Stream Video IP and System Design UG934.
> +
> +For more details, please refer to PG232 MIPI CSI-2 Receiver Subsystem.
> +https://www.xilinx.com/support/documentation/ip_documentation/mipi_csi2_rx_subsystem/v3_0/pg232-mipi-csi2-rx.pdf
> +
> +Required properties:
> +
> +- compatible: Must contain "xlnx,mipi-csi2-rx-subsystem-2.0" or
> + "xlnx,mipi-csi2-rx-subsystem-3.0"
> +
> +- reg: Physical base address and length of the registers set for the device.
> +
> +- interrupt-parent: specifies the phandle to the parent interrupt controller
> +
> +- interrupts: Property with a value describing the interrupt number.
> +
> +- xlnx,max-lanes: Maximum active lanes in the design.
> +
> +- xlnx,vc: Virtual Channel, specifies virtual channel number to be filtered.
> + If this is 4 then all virtual channels are allowed.

This seems like something a driver should configure, based on the
configuration of the connected device.

> +
> +- xlnx,csi-pxl-format: This denotes the CSI Data type selected in hw design.
> + Packets other than this data type (except for RAW8 and User defined data
> + types) will be filtered out. Possible values are RAW6, RAW7, RAW8, RAW10,
> + RAW12, RAW14, RGB444, RGB555, RGB565, RGB666, RGB888 and YUV4228bit.

This should be configured at runtime instead through V4L2 sub-device
interface; it's not a property of the hardware.

> +
> +- xlnx,axis-tdata-width: AXI Stream width, This denotes the AXI Stream width.
> + It depends on Data type chosen, Video Format Bridge enabled/disabled and
> + pixels per clock. If VFB is disabled then its value is either 0x20 (32 bit)
> + or 0x40(64 bit) width.
> +
> +- xlnx,video-format, xlnx,video-width: Video format and width, as defined in
> + video.txt.

Ditto.

> +
> +- port: Video port, using the DT bindings defined in ../video-interfaces.txt.
> + The CSI 2 Rx Subsystem has a two ports, one input port for connecting to
> + camera sensor and other is output port.
> +
> +- data-lanes: The number of data lanes through which CSI2 Rx Subsystem is
> + connected to the camera sensor as per video-interfaces.txt

This is somewhat different from the documentation in video-interfaces.txt.
Could you align the two? I don't think there's a need to document standard
properties in device binding files elaborately; rather just the hardware
specific bits.

> +
> +Optional properties:
> +
> +- xlnx,en-active-lanes: Enable Active lanes configuration in Protocol
> + Configuration Register.
> +
> +- xlnx,dphy-present: This is equivalent to whether DPHY register interface is
> + enabled or not.
> +
> +- xlnx,iic-present: This shows whether subsystem's IIC is present or not. This
> + affects the base address of the DPHY.
> +
> +- xlnx,vfb: Video Format Bridge, Denotes if Video Format Bridge is selected
> + so that output is as per AXI stream documented in UG934.
> +
> +- xlnx,ppc: Pixels per clock, Number of pixels to be transferred per pixel
> + clock. This is valid only if xlnx,vfb property is present.
> +
> +Example:
> +
> + csiss_1: csiss@a0020000 {
> + compatible = "xlnx,mipi-csi2-rx-subsystem-3.0";
> + reg = <0x0 0xa0020000 0x0 0x20000>;
> + interrupt-parent = <&gic>;
> + interrupts = <0 95 4>;
> +
> + xlnx,max-lanes = <0x4>;
> + xlnx,en-active-lanes;
> + xlnx,dphy-present;
> + xlnx,iic-present;
> + xlnx,vc = <0x4>;
> + xlnx,csi-pxl-format = "RAW8";
> + xlnx,vfb;
> + xlnx,ppc = <0x4>;
> + xlnx,axis-tdata-width = <0x20>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> +
> + xlnx,video-format = <XVIP_VF_YUV_422>;
> + xlnx,video-width = <8>;
> + csiss_out: endpoint {
> + remote-endpoint = <&vcap_csiss_in>;
> + };
> + };
> + port@1 {
> + reg = <1>;
> +
> + xlnx,video-format = <XVIP_VF_YUV_422>;
> + xlnx,video-width = <8>;
> +
> + csiss_in: endpoint {
> + data-lanes = <1 2 3 4>;
> + /* MIPI CSI2 Camera handle */
> + remote-endpoint = <&vs2016_out>;
> + };
> +
> + };
> +
> + };
> + };
> --
> 2.7.4
>
> This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

Could you drop this from v2?

--
Regards,

Sakari Ailus
[email protected]

2019-01-09 11:54:51

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

Hi Vishal,

Please see my comments below.

On Wed, May 30, 2018 at 12:24:44AM +0530, Vishal Sagar wrote:
> The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images
> from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready
> for image processing.
>
> It supports upto 4 lanes, filtering based on Virtual Channel selected in
> IP, an optional Xilinx IIC controller, optional register interface for
> the DPHY, multiple color formats, short packet capture,
>
> This driver helps configure the number of active lanes to be set,
> setting and handling interrupts and IP core enable.
> It logs the count of events occurring according to their type between
> streaming ON and OFF. The short packet reception is notified to
> application via v4l2_event.
>
> Signed-off-by: Vishal Sagar <[email protected]>
> ---
> drivers/media/platform/xilinx/Kconfig | 12 +
> drivers/media/platform/xilinx/Makefile | 1 +
> drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751 +++++++++++++++++++++++
> include/uapi/linux/xilinx-csi2rxss.h | 25 +
> include/uapi/linux/xilinx-v4l2-controls.h | 14 +
> 5 files changed, 1803 insertions(+)
> create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
> create mode 100644 include/uapi/linux/xilinx-csi2rxss.h
>
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index a5d21b7..06d5944 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -8,6 +8,18 @@ config VIDEO_XILINX
>
> if VIDEO_XILINX
>
> +config VIDEO_XILINX_CSI2RXSS
> + tristate "Xilinx CSI2 Rx Subsystem"
> + depends on VIDEO_XILINX

I'm no Kconfig expert, but I'd think that you don't need to depend on
things that the option already depends on if (above).

> + help
> + Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
> + based driver that takes input from CSI2 Tx source and converts
> + it into an AXI4-Stream. It has a DPHY (whose register interface
> + can be enabled, an optional I2C controller and an optional Video
> + Format Bridge which converts the AXI4-Stream data to Xilinx Video
> + Bus formats based on UG934. The driver is used to set the number
> + of active lanes and get short packet data.
> +
> config VIDEO_XILINX_TPG
> tristate "Xilinx Video Test Pattern Generator"
> depends on VIDEO_XILINX
> diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile
> index e8a0f2a..768f079 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -1,5 +1,6 @@
> xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
>
> obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
> +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o
> obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
> obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
> diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> new file mode 100644
> index 0000000..03f387c
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> @@ -0,0 +1,1751 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx MIPI CSI2 Rx Subsystem
> + *
> + * Copyright (C) 2016 - 2018 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <[email protected]>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/spinlock_types.h>
> +#include <linux/types.h>
> +#include <linux/v4l2-subdev.h>
> +#include <linux/xilinx-csi2rxss.h>
> +#include <linux/xilinx-v4l2-controls.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +#include "xilinx-vip.h"
> +
> +/*
> + * MIPI CSI2 Rx register map, bitmask and offsets
> + */
> +#define XCSI_CCR_OFFSET 0x00000000
> +#define XCSI_CCR_SOFTRESET_SHIFT 1
> +#define XCSI_CCR_COREENB_SHIFT 0
> +#define XCSI_CCR_SOFTRESET_MASK BIT(XCSI_CCR_SOFTRESET_SHIFT)
> +#define XCSI_CCR_COREENB_MASK BIT(XCSI_CCR_COREENB_SHIFT)
> +
> +#define XCSI_PCR_OFFSET 0x00000004
> +#define XCSI_PCR_MAXLANES_MASK 0x00000018
> +#define XCSI_PCR_ACTLANES_MASK 0x00000003
> +#define XCSI_PCR_MAXLANES_SHIFT 3
> +#define XCSI_PCR_ACTLANES_SHIFT 0
> +
> +#define XCSI_CSR_OFFSET 0x00000010
> +#define XCSI_CSR_PKTCOUNT_SHIFT 16
> +#define XCSI_CSR_SPFIFOFULL_SHIFT 3
> +#define XCSI_CSR_SPFIFONE_SHIFT 2
> +#define XCSI_CSR_SLBF_SHIFT 1
> +#define XCSI_CSR_RIPCD_SHIFT 0
> +#define XCSI_CSR_PKTCOUNT_MASK 0xFFFF0000
> +#define XCSI_CSR_SPFIFOFULL_MASK BIT(XCSI_CSR_SPFIFOFULL_SHIFT)
> +#define XCSI_CSR_SPFIFONE_MASK BIT(XCSI_CSR_SPFIFONE_SHIFT)
> +#define XCSI_CSR_SLBF_MASK BIT(XCSI_CSR_SLBF_SHIFT)
> +#define XCSI_CSR_RIPCD_MASK BIT(XCSI_CSR_RIPCD_SHIFT)
> +
> +#define XCSI_GIER_OFFSET 0x00000020
> +#define XCSI_GIER_GIE_SHIFT 0
> +#define XCSI_GIER_GIE_MASK BIT(XCSI_GIER_GIE_SHIFT)
> +#define XCSI_GIER_SET 1
> +#define XCSI_GIER_RESET 0
> +
> +#define XCSI_ISR_OFFSET 0x00000024
> +#define XCSI_ISR_FR_SHIFT 31
> +#define XCSI_ISR_ILC_SHIFT 21
> +#define XCSI_ISR_SPFIFOF_SHIFT 20
> +#define XCSI_ISR_SPFIFONE_SHIFT 19
> +#define XCSI_ISR_SLBF_SHIFT 18
> +#define XCSI_ISR_STOP_SHIFT 17
> +#define XCSI_ISR_SOTERR_SHIFT 13
> +#define XCSI_ISR_SOTSYNCERR_SHIFT 12
> +#define XCSI_ISR_ECC2BERR_SHIFT 11
> +#define XCSI_ISR_ECC1BERR_SHIFT 10
> +#define XCSI_ISR_CRCERR_SHIFT 9
> +#define XCSI_ISR_DATAIDERR_SHIFT 8
> +#define XCSI_ISR_VC3FSYNCERR_SHIFT 7
> +#define XCSI_ISR_VC3FLVLERR_SHIFT 6
> +#define XCSI_ISR_VC2FSYNCERR_SHIFT 5
> +#define XCSI_ISR_VC2FLVLERR_SHIFT 4
> +#define XCSI_ISR_VC1FSYNCERR_SHIFT 3
> +#define XCSI_ISR_VC1FLVLERR_SHIFT 2
> +#define XCSI_ISR_VC0FSYNCERR_SHIFT 1
> +#define XCSI_ISR_VC0FLVLERR_SHIFT 0
> +#define XCSI_ISR_FR_MASK BIT(XCSI_ISR_FR_SHIFT)
> +#define XCSI_ISR_ILC_MASK BIT(XCSI_ISR_ILC_SHIFT)
> +#define XCSI_ISR_SPFIFOF_MASK BIT(XCSI_ISR_SPFIFOF_SHIFT)
> +#define XCSI_ISR_SPFIFONE_MASK BIT(XCSI_ISR_SPFIFONE_SHIFT)
> +#define XCSI_ISR_SLBF_MASK BIT(XCSI_ISR_SLBF_SHIFT)
> +#define XCSI_ISR_STOP_MASK BIT(XCSI_ISR_STOP_SHIFT)
> +#define XCSI_ISR_SOTERR_MASK BIT(XCSI_ISR_SOTERR_SHIFT)
> +#define XCSI_ISR_SOTSYNCERR_MASK BIT(XCSI_ISR_SOTSYNCERR_SHIFT)
> +#define XCSI_ISR_ECC2BERR_MASK BIT(XCSI_ISR_ECC2BERR_SHIFT)
> +#define XCSI_ISR_ECC1BERR_MASK BIT(XCSI_ISR_ECC1BERR_SHIFT)
> +#define XCSI_ISR_CRCERR_MASK BIT(XCSI_ISR_CRCERR_SHIFT)
> +#define XCSI_ISR_DATAIDERR_MASK BIT(XCSI_ISR_DATAIDERR_SHIFT)
> +#define XCSI_ISR_VC3FSYNCERR_MASK BIT(XCSI_ISR_VC3FSYNCERR_SHIFT)
> +#define XCSI_ISR_VC3FLVLERR_MASK BIT(XCSI_ISR_VC3FLVLERR_SHIFT)
> +#define XCSI_ISR_VC2FSYNCERR_MASK BIT(XCSI_ISR_VC2FSYNCERR_SHIFT)
> +#define XCSI_ISR_VC2FLVLERR_MASK BIT(XCSI_ISR_VC2FLVLERR_SHIFT)
> +#define XCSI_ISR_VC1FSYNCERR_MASK BIT(XCSI_ISR_VC1FSYNCERR_SHIFT)
> +#define XCSI_ISR_VC1FLVLERR_MASK BIT(XCSI_ISR_VC1FLVLERR_SHIFT)
> +#define XCSI_ISR_VC0FSYNCERR_MASK BIT(XCSI_ISR_VC0FSYNCERR_SHIFT)
> +#define XCSI_ISR_VC0FLVLERR_MASK BIT(XCSI_ISR_VC0FLVLERR_SHIFT)
> +#define XCSI_ISR_ALLINTR_MASK 0x803FFFFF
> +
> +#define XCSI_INTR_PROT_MASK (XCSI_ISR_VC3FSYNCERR_MASK | \
> + XCSI_ISR_VC3FLVLERR_MASK | \
> + XCSI_ISR_VC2FSYNCERR_MASK | \
> + XCSI_ISR_VC2FLVLERR_MASK | \
> + XCSI_ISR_VC1FSYNCERR_MASK | \
> + XCSI_ISR_VC1FLVLERR_MASK | \
> + XCSI_ISR_VC0FSYNCERR_MASK | \
> + XCSI_ISR_VC0FLVLERR_MASK)
> +
> +#define XCSI_INTR_PKTLVL_MASK (XCSI_ISR_ECC2BERR_MASK | \
> + XCSI_ISR_ECC1BERR_MASK | \
> + XCSI_ISR_CRCERR_MASK | \
> + XCSI_ISR_DATAIDERR_MASK)
> +
> +#define XCSI_INTR_DPHY_MASK (XCSI_ISR_SOTERR_MASK | \
> + XCSI_ISR_SOTSYNCERR_MASK)
> +
> +#define XCSI_INTR_SPKT_MASK (XCSI_ISR_SPFIFOF_MASK | \
> + XCSI_ISR_SPFIFONE_MASK)
> +
> +#define XCSI_INTR_FRAMERCVD_MASK (XCSI_ISR_FR_MASK)
> +
> +#define XCSI_INTR_ERR_MASK (XCSI_ISR_ILC_MASK | \
> + XCSI_ISR_SLBF_MASK | \
> + XCSI_ISR_STOP_MASK)
> +
> +#define XCSI_IER_OFFSET 0x00000028
> +#define XCSI_IER_FR_SHIFT 31
> +#define XCSI_IER_ILC_SHIFT 21
> +#define XCSI_IER_SPFIFOF_SHIFT 20
> +#define XCSI_IER_SPFIFONE_SHIFT 19
> +#define XCSI_IER_SLBF_SHIFT 18
> +#define XCSI_IER_STOP_SHIFT 17
> +#define XCSI_IER_SOTERR_SHIFT 13
> +#define XCSI_IER_SOTSYNCERR_SHIFT 12
> +#define XCSI_IER_ECC2BERR_SHIFT 11
> +#define XCSI_IER_ECC1BERR_SHIFT 10
> +#define XCSI_IER_CRCERR_SHIFT 9
> +#define XCSI_IER_DATAIDERR_SHIFT 8
> +#define XCSI_IER_VC3FSYNCERR_SHIFT 7
> +#define XCSI_IER_VC3FLVLERR_SHIFT 6
> +#define XCSI_IER_VC2FSYNCERR_SHIFT 5
> +#define XCSI_IER_VC2FLVLERR_SHIFT 4
> +#define XCSI_IER_VC1FSYNCERR_SHIFT 3
> +#define XCSI_IER_VC1FLVLERR_SHIFT 2
> +#define XCSI_IER_VC0FSYNCERR_SHIFT 1
> +#define XCSI_IER_VC0FLVLERR_SHIFT 0
> +#define XCSI_IER_FR_MASK BIT(XCSI_IER_FR_SHIFT)
> +#define XCSI_IER_ILC_MASK BIT(XCSI_IER_ILC_SHIFT)
> +#define XCSI_IER_SPFIFOF_MASK BIT(XCSI_IER_SPFIFOF_SHIFT)
> +#define XCSI_IER_SPFIFONE_MASK BIT(XCSI_IER_SPFIFONE_SHIFT)
> +#define XCSI_IER_SLBF_MASK BIT(XCSI_IER_SLBF_SHIFT)
> +#define XCSI_IER_STOP_MASK BIT(XCSI_IER_STOP_SHIFT)
> +#define XCSI_IER_SOTERR_MASK BIT(XCSI_IER_SOTERR_SHIFT)
> +#define XCSI_IER_SOTSYNCERR_MASK BIT(XCSI_IER_SOTSYNCERR_SHIFT)
> +#define XCSI_IER_ECC2BERR_MASK BIT(XCSI_IER_ECC2BERR_SHIFT)
> +#define XCSI_IER_ECC1BERR_MASK BIT(XCSI_IER_ECC1BERR_SHIFT)
> +#define XCSI_IER_CRCERR_MASK BIT(XCSI_IER_CRCERR_SHIFT)
> +#define XCSI_IER_DATAIDERR_MASK BIT(XCSI_IER_DATAIDERR_SHIFT)
> +#define XCSI_IER_VC3FSYNCERR_MASK BIT(XCSI_IER_VC3FSYNCERR_SHIFT)
> +#define XCSI_IER_VC3FLVLERR_MASK BIT(XCSI_IER_VC3FLVLERR_SHIFT)
> +#define XCSI_IER_VC2FSYNCERR_MASK BIT(XCSI_IER_VC2FSYNCERR_SHIFT)
> +#define XCSI_IER_VC2FLVLERR_MASK BIT(XCSI_IER_VC2FLVLERR_SHIFT)
> +#define XCSI_IER_VC1FSYNCERR_MASK BIT(XCSI_IER_VC1FSYNCERR_SHIFT)
> +#define XCSI_IER_VC1FLVLERR_MASK BIT(XCSI_IER_VC1FLVLERR_SHIFT)
> +#define XCSI_IER_VC0FSYNCERR_MASK BIT(XCSI_IER_VC0FSYNCERR_SHIFT)
> +#define XCSI_IER_VC0FLVLERR_MASK BIT(XCSI_IER_VC0FLVLERR_SHIFT)
> +#define XCSI_IER_ALLINTR_MASK 0x803FFFFF
> +
> +#define XCSI_SPKTR_OFFSET 0x00000030
> +#define XCSI_SPKTR_DATA_SHIFT 8
> +#define XCSI_SPKTR_VC_SHIFT 6
> +#define XCSI_SPKTR_DT_SHIFT 0
> +#define XCSI_SPKTR_DATA_MASK 0x00FFFF00
> +#define XCSI_SPKTR_VC_MASK 0x000000C0
> +#define XCSI_SPKTR_DT_MASK 0x0000003F
> +
> +#define XCSI_CLKINFR_OFFSET 0x0000003C
> +#define XCSI_CLKINFR_STOP_SHIFT 1
> +#define XCSI_CLKINFR_STOP_MASK BIT(XCSI_CLKINFR_STOP_SHIFT)
> +
> +#define XCSI_L0INFR_OFFSET 0x00000040
> +#define XCSI_L1INFR_OFFSET 0x00000044
> +#define XCSI_L2INFR_OFFSET 0x00000048
> +#define XCSI_L3INFR_OFFSET 0x0000004C
> +#define XCSI_LXINFR_STOP_SHIFT 5
> +#define XCSI_LXINFR_SOTERR_SHIFT 1
> +#define XCSI_LXINFR_SOTSYNCERR_SHIFT 0
> +#define XCSI_LXINFR_STOP_MASK BIT(XCSI_LXINFR_STOP_SHIFT)
> +#define XCSI_LXINFR_SOTERR_MASK BIT(XCSI_LXINFR_SOTERR_SHIFT)
> +#define XCSI_LXINFR_SOTSYNCERR_MASK BIT(XCSI_LXINFR_SOTSYNCERR_SHIFT)
> +
> +#define XCSI_VC0INF1R_OFFSET 0x00000060
> +#define XCSI_VC1INF1R_OFFSET 0x00000068
> +#define XCSI_VC2INF1R_OFFSET 0x00000070
> +#define XCSI_VC3INF1R_OFFSET 0x00000078
> +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16
> +#define XCSI_VCXINF1R_BYTECOUNT_SHIFT 0
> +#define XCSI_VCXINF1R_LINECOUNT_MASK 0xFFFF0000
> +#define XCSI_VCXINF1R_BYTECOUNT_MASK 0x0000FFFF
> +
> +#define XCSI_VC0INF2R_OFFSET 0x00000064
> +#define XCSI_VC1INF2R_OFFSET 0x0000006C
> +#define XCSI_VC2INF2R_OFFSET 0x00000074
> +#define XCSI_VC3INF2R_OFFSET 0x0000007C
> +#define XCSI_VCXINF2R_DATATYPE_SHIFT 0
> +#define XCSI_VCXINF2R_DATATYPE_MASK 0x0000003F
> +
> +#define XDPHY_CTRLREG_OFFSET 0x0
> +#define XDPHY_CTRLREG_DPHYEN_SHIFT 1
> +#define XDPHY_CTRLREG_DPHYEN_MASK BIT(XDPHY_CTRLREG_DPHYEN_SHIFT)
> +
> +#define XDPHY_CLKSTATREG_OFFSET 0x18
> +#define XDPHY_CLKSTATREG_MODE_SHIFT 0
> +#define XDPHY_CLKSTATREG_MODE_MASK 0x3
> +#define XDPHY_LOW_POWER_MODE 0x0
> +#define XDPHY_HI_SPEED_MODE 0x1
> +#define XDPHY_ESC_MODE 0x2
> +
> +/*
> + * Interrupt mask
> + */
> +#define XCSI_INTR_MASK (XCSI_ISR_ALLINTR_MASK & ~XCSI_ISR_STOP_MASK)
> +/*
> + * Timeout for reset
> + */
> +#define XCSI_TIMEOUT_VAL (1000) /* us */

No need for parentheses here or below.

> +
> +/*
> + * Max string length for CSI Data type string
> + */
> +#define MAX_XIL_CSIDT_STR_LENGTH 64
> +
> +/*
> + * Maximum number of short packet events per file handle.
> + */
> +#define XCSI_MAX_SPKT (512)
> +
> +/* Number of media pads */
> +#define XILINX_CSI_MEDIA_PADS (2)
> +
> +#define XCSI_DEFAULT_WIDTH (1920)
> +#define XCSI_DEFAULT_HEIGHT (1080)
> +
> +/*
> + * Macro to return "true" or "false" string if bit is set
> + */
> +#define XCSI_GET_BITSET_STR(val, mask) (val) & (mask) ? "true" : "false"
> +
> +enum csi_datatypes {
> + MIPI_CSI_DT_FRAME_START_CODE = 0x00,
> + MIPI_CSI_DT_FRAME_END_CODE,
> + MIPI_CSI_DT_LINE_START_CODE,
> + MIPI_CSI_DT_LINE_END_CODE,
> + MIPI_CSI_DT_SYNC_RSVD_04,
> + MIPI_CSI_DT_SYNC_RSVD_05,
> + MIPI_CSI_DT_SYNC_RSVD_06,
> + MIPI_CSI_DT_SYNC_RSVD_07,
> + MIPI_CSI_DT_GSPKT_08,
> + MIPI_CSI_DT_GSPKT_09,
> + MIPI_CSI_DT_GSPKT_0A,
> + MIPI_CSI_DT_GSPKT_0B,
> + MIPI_CSI_DT_GSPKT_0C,
> + MIPI_CSI_DT_GSPKT_0D,
> + MIPI_CSI_DT_GSPKT_0E,
> + MIPI_CSI_DT_GSPKT_0F,
> + MIPI_CSI_DT_GLPKT_10,
> + MIPI_CSI_DT_GLPKT_11,
> + MIPI_CSI_DT_GLPKT_12,
> + MIPI_CSI_DT_GLPKT_13,
> + MIPI_CSI_DT_GLPKT_14,
> + MIPI_CSI_DT_GLPKT_15,
> + MIPI_CSI_DT_GLPKT_16,
> + MIPI_CSI_DT_GLPKT_17,
> + MIPI_CSI_DT_YUV_420_8B,
> + MIPI_CSI_DT_YUV_420_10B,
> + MIPI_CSI_DT_YUV_420_8B_LEGACY,
> + MIPI_CSI_DT_YUV_RSVD,
> + MIPI_CSI_DT_YUV_420_8B_CSPS,
> + MIPI_CSI_DT_YUV_420_10B_CSPS,
> + MIPI_CSI_DT_YUV_422_8B,
> + MIPI_CSI_DT_YUV_422_10B,
> + MIPI_CSI_DT_RGB_444,
> + MIPI_CSI_DT_RGB_555,
> + MIPI_CSI_DT_RGB_565,
> + MIPI_CSI_DT_RGB_666,
> + MIPI_CSI_DT_RGB_888,
> + MIPI_CSI_DT_RGB_RSVD_25,
> + MIPI_CSI_DT_RGB_RSVD_26,
> + MIPI_CSI_DT_RGB_RSVD_27,
> + MIPI_CSI_DT_RAW_6,
> + MIPI_CSI_DT_RAW_7,
> + MIPI_CSI_DT_RAW_8,
> + MIPI_CSI_DT_RAW_10,
> + MIPI_CSI_DT_RAW_12,
> + MIPI_CSI_DT_RAW_14,
> + MIPI_CSI_DT_RAW_RSVD_2E,
> + MIPI_CSI_DT_RAW_RSVD_2F,
> + MIPI_CSI_DT_USER_30,
> + MIPI_CSI_DT_USER_31,
> + MIPI_CSI_DT_USER_32,
> + MIPI_CSI_DT_USER_33,
> + MIPI_CSI_DT_USER_34,
> + MIPI_CSI_DT_USER_35,
> + MIPI_CSI_DT_USER_36,
> + MIPI_CSI_DT_USER_37,
> + MIPI_CSI_DT_RSVD_38,
> + MIPI_CSI_DT_RSVD_39,
> + MIPI_CSI_DT_RSVD_3A,
> + MIPI_CSI_DT_RSVD_3B,
> + MIPI_CSI_DT_RSVD_3C,
> + MIPI_CSI_DT_RSVD_3D,
> + MIPI_CSI_DT_RSVD_3E,
> + MIPI_CSI_DT_RSVD_3F
> +};
> +
> +/**
> + * struct pixel_format - Data Type to string name structure
> + * @pixelformat: MIPI CSI2 Data type
> + * @pixelformatstr: String name of Data Type
> + */
> +struct pixel_format {
> + enum csi_datatypes pixelformat;
> + char pixelformatstr[MAX_XIL_CSIDT_STR_LENGTH];
> +};
> +
> +/**
> + * struct xcsi2rxss_event - Event log structure
> + * @mask: Event mask
> + * @name: Name of the event
> + * @counter: Count number of events
> + */
> +struct xcsi2rxss_event {
> + u32 mask;
> + const char * const name;
> + unsigned int counter;
> +};
> +
> +/*
> + * struct xcsi2rxss_core - Core configuration CSI2 Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @dphy_present: Flag for DPHY register interface presence
> + * @dphy_offset: DPHY registers offset

Is the D-PHY a part of the same device, or is it effectively a separate
device? Maxime has posted a patchset for PHY control so this could be a
candidate user for that:

<URL:https://lkml.org/lkml/2019/1/9/224>

> + * @enable_active_lanes: If number of active lanes can be modified
> + * @max_num_lanes: Maximum number of lanes present
> + * @vfb: Video Format Bridge enabled or not
> + * @ppc: pixels per clock
> + * @vc: Virtual Channel
> + * @axis_tdata_width: AXI Stream data width
> + * @datatype: Data type filter
> + * @pxlformat: String with CSI pixel format from IP
> + * @num_lanes: Number of lanes requested from application
> + * @events: Structure to maintain event logs
> + */
> +struct xcsi2rxss_core {
> + struct device *dev;
> + void __iomem *iomem;
> + int irq;
> + u32 dphy_offset;
> + bool dphy_present;
> + bool enable_active_lanes;
> + u32 max_num_lanes;
> + bool vfb;
> + u32 ppc;
> + u32 vc;
> + u32 axis_tdata_width;
> + u32 datatype;
> + const char *pxlformat;
> + u32 num_lanes;
> + struct xcsi2rxss_event *events;
> +};
> +
> +/**
> + * struct xcsi2rxss_state - CSI2 Rx Subsystem device structure
> + * @core: Core structure for MIPI CSI2 Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @vip_format: format information corresponding to the active format
> + * @event: Holds the short packet event
> + * @lock: mutex for serializing operations
> + * @pads: media pads
> + * @npads: number of pads
> + * @streaming: Flag for storing streaming state
> + * @suspended: Flag for storing suspended state
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xcsi2rxss_state {
> + struct xcsi2rxss_core core;
> + struct v4l2_subdev subdev;
> + struct v4l2_ctrl_handler ctrl_handler;
> + struct v4l2_mbus_framefmt formats[2];
> + struct v4l2_mbus_framefmt default_format;
> + const struct xvip_video_format *vip_format;
> + struct v4l2_event event;
> + /*
> + * Used to serialize access.
> + */

This comment seems redundant.

> + struct mutex lock;
> + struct media_pad pads[XILINX_CSI_MEDIA_PADS];
> + unsigned int npads;
> + bool streaming;
> + bool suspended;
> +};
> +
> +static inline struct xcsi2rxss_state *
> +to_xcsi2rxssstate(struct v4l2_subdev *subdev)
> +{
> + return container_of(subdev, struct xcsi2rxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xcsi2rxss_read(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr)
> +{
> + return ioread32(xcsi2rxss->iomem + addr);
> +}
> +
> +static inline void xcsi2rxss_write(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr, u32 value)
> +{
> + iowrite32(value, xcsi2rxss->iomem + addr);
> +}
> +
> +static inline void xcsi2rxss_clr(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr, u32 clr)
> +{
> + xcsi2rxss_write(xcsi2rxss,
> + addr,

Fits on previous line.

> + xcsi2rxss_read(xcsi2rxss, addr) & ~clr);
> +}
> +
> +static inline void xcsi2rxss_set(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr, u32 set)
> +{
> + xcsi2rxss_write(xcsi2rxss,
> + addr,

Ditto.

> + xcsi2rxss_read(xcsi2rxss, addr) | set);
> +}
> +
> +static const struct pixel_format pixel_formats[] = {
> + { MIPI_CSI_DT_YUV_420_8B, "YUV420_8bit" },
> + { MIPI_CSI_DT_YUV_420_10B, "YUV420_10bit" },
> + { MIPI_CSI_DT_YUV_420_8B_LEGACY, "Legacy_YUV420_8bit" },
> + { MIPI_CSI_DT_YUV_420_8B_CSPS, "YUV420_8bit_CSPS" },
> + { MIPI_CSI_DT_YUV_420_10B_CSPS, "YUV420_10bit_CSPS" },
> + { MIPI_CSI_DT_YUV_422_8B, "YUV422_8bit" },
> + { MIPI_CSI_DT_YUV_422_10B, "YUV422_10bit" },
> + { MIPI_CSI_DT_RGB_444, "RGB444" },
> + { MIPI_CSI_DT_RGB_555, "RGB555" },
> + { MIPI_CSI_DT_RGB_565, "RGB565" },
> + { MIPI_CSI_DT_RGB_666, "RGB666" },
> + { MIPI_CSI_DT_RGB_888, "RGB888" },
> + { MIPI_CSI_DT_RAW_6, "RAW6" },
> + { MIPI_CSI_DT_RAW_7, "RAW7" },
> + { MIPI_CSI_DT_RAW_8, "RAW8" },
> + { MIPI_CSI_DT_RAW_10, "RAW10" },
> + { MIPI_CSI_DT_RAW_12, "RAW12" },
> + { MIPI_CSI_DT_RAW_14, "RAW14 "}
> +};
> +
> +static struct xcsi2rxss_event xcsi2rxss_events[] = {
> + { XCSI_ISR_FR_MASK, "Frame Received", 0 },
> + { XCSI_ISR_ILC_MASK, "Invalid Lane Count Error", 0 },
> + { XCSI_ISR_SPFIFOF_MASK, "Short Packet FIFO OverFlow Error", 0 },
> + { XCSI_ISR_SPFIFONE_MASK, "Short Packet FIFO Not Empty", 0 },
> + { XCSI_ISR_SLBF_MASK, "Streamline Buffer Full Error", 0 },
> + { XCSI_ISR_STOP_MASK, "Lane Stop State", 0 },
> + { XCSI_ISR_SOTERR_MASK, "SOT Error", 0 },
> + { XCSI_ISR_SOTSYNCERR_MASK, "SOT Sync Error", 0 },
> + { XCSI_ISR_ECC2BERR_MASK, "2 Bit ECC Unrecoverable Error", 0 },
> + { XCSI_ISR_ECC1BERR_MASK, "1 Bit ECC Recoverable Error", 0 },
> + { XCSI_ISR_CRCERR_MASK, "CRC Error", 0 },
> + { XCSI_ISR_DATAIDERR_MASK, "Data Id Error", 0 },
> + { XCSI_ISR_VC3FSYNCERR_MASK, "Virtual Channel 3 Frame Sync Error", 0 },
> + { XCSI_ISR_VC3FLVLERR_MASK, "Virtual Channel 3 Frame Level Error", 0 },
> + { XCSI_ISR_VC2FSYNCERR_MASK, "Virtual Channel 2 Frame Sync Error", 0 },
> + { XCSI_ISR_VC2FLVLERR_MASK, "Virtual Channel 2 Frame Level Error", 0 },
> + { XCSI_ISR_VC1FSYNCERR_MASK, "Virtual Channel 1 Frame Sync Error", 0 },
> + { XCSI_ISR_VC1FLVLERR_MASK, "Virtual Channel 1 Frame Level Error", 0 },
> + { XCSI_ISR_VC0FSYNCERR_MASK, "Virtual Channel 0 Frame Sync Error", 0 },
> + { XCSI_ISR_VC0FLVLERR_MASK, "Virtual Channel 0 Frame Level Error", 0 }
> +};
> +
> +#define XMIPICSISS_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events)
> +
> +/**
> + * xcsi2rxss_clr_and_set - Clear and set the register with a bitmask
> + * @xcsi2rxss: Xilinx MIPI CSI2 Rx Subsystem subdev core struct
> + * @addr: address of register
> + * @clr: bitmask to be cleared
> + * @set: bitmask to be set
> + *
> + * Clear a bit(s) of mask @clr in the register at address @addr, then set
> + * a bit(s) of mask @set in the register after.
> + */
> +static void xcsi2rxss_clr_and_set(struct xcsi2rxss_core *xcsi2rxss,
> + u32 addr, u32 clr, u32 set)
> +{
> + u32 reg;
> +
> + reg = xcsi2rxss_read(xcsi2rxss, addr);
> + reg &= ~clr;
> + reg |= set;
> + xcsi2rxss_write(xcsi2rxss, addr, reg);
> +}
> +
> +/**
> + * xcsi2rxss_pxlfmtstrtodt - Convert pixel format string got from dts
> + * to data type.
> + * @pxlfmtstr: String obtained while parsing device node
> + *
> + * This function takes a CSI pixel format string obtained while parsing
> + * device tree node and converts it to data type.
> + *
> + * Eg. "RAW8" string is converted to 0x2A.
> + * Refer to MIPI CSI2 specification for details.
> + *
> + * Return: Equivalent pixel format value from table
> + */
> +static u32 xcsi2rxss_pxlfmtstrtodt(const char *pxlfmtstr)
> +{
> + u32 i;
> + u32 maxentries = ARRAY_SIZE(pixel_formats);
> +
> + for (i = 0; i < maxentries; i++) {
> + if (!strncmp(pixel_formats[i].pixelformatstr,
> + pxlfmtstr, MAX_XIL_CSIDT_STR_LENGTH))
> + return pixel_formats[i].pixelformat;
> + }
> +
> + return -EINVAL;
> +}
> +
> +/**
> + * xcsi2rxss_pxlfmtdttostr - Convert pixel format data type to string.
> + * @datatype: MIPI CSI-2 Data Type
> + *
> + * This function takes a CSI pixel format data type and returns a
> + * pointer to the string name.
> + *
> + * Eg. 0x2A returns "RAW8" string.
> + * Refer to MIPI CSI2 specification for details.
> + *
> + * Return: Equivalent pixel format string from table
> + */
> +static const char *xcsi2rxss_pxlfmtdttostr(u32 datatype)
> +{
> + u32 i;
> + u32 maxentries = ARRAY_SIZE(pixel_formats);
> +
> + for (i = 0; i < maxentries; i++) {
> + if (pixel_formats[i].pixelformat == datatype)
> + return pixel_formats[i].pixelformatstr;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * xcsi2rxss_enable - Enable or disable the CSI Core
> + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> + * @flag: true for enabling, false for disabling
> + *
> + * This function enables/disables the MIPI CSI2 Rx Subsystem core.
> + * After enabling the CSI2 Rx core, the DPHY is enabled in case the
> + * register interface for it is present.
> + */
> +static void xcsi2rxss_enable(struct xcsi2rxss_core *core, bool flag)
> +{
> + u32 dphyctrlregoffset = core->dphy_offset + XDPHY_CTRLREG_OFFSET;
> +
> + if (flag) {
> + xcsi2rxss_write(core, XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
> + if (core->dphy_present)
> + xcsi2rxss_write(core, dphyctrlregoffset,
> + XDPHY_CTRLREG_DPHYEN_MASK);
> + } else {
> + xcsi2rxss_write(core, XCSI_CCR_OFFSET, 0);
> + if (core->dphy_present)
> + xcsi2rxss_write(core, dphyctrlregoffset, 0);
> + }
> +}
> +
> +/**
> + * xcsi2rxss_interrupts_enable - Enable or disable CSI interrupts
> + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> + * @flag: true for enabling, false for disabling
> + *
> + * This function enables/disables the interrupts for the MIPI CSI2
> + * Rx Subsystem.
> + */
> +static void xcsi2rxss_interrupts_enable(struct xcsi2rxss_core *core, bool flag)
> +{
> + if (flag) {
> + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> + xcsi2rxss_write(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
> + xcsi2rxss_set(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> + } else {
> + xcsi2rxss_clr(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
> + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> + }
> +}
> +
> +/**
> + * xcsi2rxss_reset - Does a soft reset of the MIPI CSI2 Rx Subsystem
> + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> + *
> + * Return: 0 - on success OR -ETIME if reset times out
> + */
> +static int xcsi2rxss_reset(struct xcsi2rxss_core *core)
> +{
> + u32 timeout = XCSI_TIMEOUT_VAL;
> +
> + xcsi2rxss_set(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
> +
> + while (xcsi2rxss_read(core, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD_MASK) {
> + if (timeout == 0) {
> + dev_err(core->dev, "Xilinx CSI2 Rx Subsystem Soft Reset Timeout!\n");
> + return -ETIME;
> + }
> +
> + timeout--;
> + udelay(1);
> + }
> +
> + xcsi2rxss_clr(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
> + return 0;
> +}
> +
> +/**
> + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * In the interrupt handler, a list of event counters are updated for
> + * corresponding interrupts. This is useful to get status / debug.
> + * If the short packet FIFO not empty or overflow interrupt is received
> + * capture the short packet and notify of event occurrence
> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *dev_id)
> +{
> + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)dev_id;
> + struct xcsi2rxss_core *core = &state->core;
> + u32 status;
> +
> + status = xcsi2rxss_read(core, XCSI_ISR_OFFSET) & XCSI_INTR_MASK;
> + dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> + if (!status)
> + return IRQ_NONE;
> +
> + if (status & XCSI_ISR_SPFIFONE_MASK) {
> + memset(&state->event, 0, sizeof(state->event));
> +
> + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT;
> +
> + *((u32 *)(&state->event.u.data)) =
> + xcsi2rxss_read(core, XCSI_SPKTR_OFFSET);
> +
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }
> +
> + if (status & XCSI_ISR_SPFIFOF_MASK) {
> + dev_alert(core->dev, "Short packet FIFO overflowed\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> +
> + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT_OVF;
> +
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }
> +
> + if (status & XCSI_ISR_SLBF_MASK) {
> + dev_alert(core->dev, "Stream Line Buffer Full!\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> +
> + state->event.type = V4L2_EVENT_XLNXCSIRX_SLBF;
> +
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }

This is really low level stuff. What would be the purpose of informing the
user space on individual short or long packets? Other CSI-2 receiver
drivers don't do it either.

> +
> + if (status & XCSI_ISR_ALLINTR_MASK) {
> + unsigned int i;
> +
> + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
> + if (!(status & core->events[i].mask))
> + continue;
> + core->events[i].counter++;
> + dev_dbg(core->dev, "%s: %d\n", core->events[i].name,
> + core->events[i].counter);
> + }
> + }
> +
> + xcsi2rxss_write(core, XCSI_ISR_OFFSET, status);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state)
> +{
> + u32 i;
> +
> + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++)
> + state->core.events[i].counter = 0;
> +}
> +
> +/**
> + * xcsi2rxss_log_counters - Print out the event counters.
> + * @state: Pointer to device state
> + *
> + */
> +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state)
> +{
> + u32 i;
> +
> + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
> + if (state->core.events[i].counter > 0)
> + v4l2_info(&state->subdev, "%s events: %d\n",
> + state->core.events[i].name,
> + state->core.events[i].counter);
> + }
> +}
> +
> +/**
> + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx MIPI CSI-2
> + *
> + * Return: 0 on success
> + */
> +static int xcsi2rxss_log_status(struct v4l2_subdev *sd)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> + u32 reg, data, i;

unsigned int is fine for counters unless you specifically need a type such
as u32.

> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + xcsi2rxss_log_counters(xcsi2rxss);
> +
> + v4l2_info(sd, "***** Core Status *****\n");

Please use either v4l2_*() or dev_*() macros to print stuff but not both.

> + data = xcsi2rxss_read(core, XCSI_CSR_OFFSET);
> + v4l2_info(sd, "Short Packet FIFO Full = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFOFULL_MASK));
> + v4l2_info(sd, "Short Packet FIFO Not Empty = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFONE_MASK));
> + v4l2_info(sd, "Stream line buffer full = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CSR_SLBF_MASK));
> + v4l2_info(sd, "Soft reset/Core disable in progress = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CSR_RIPCD_MASK));
> +
> + /* Clk & Lane Info */
> + v4l2_info(sd, "******** Clock Lane Info *********\n");
> + data = xcsi2rxss_read(core, XCSI_CLKINFR_OFFSET);
> + v4l2_info(sd, "Clock Lane in Stop State = %s\n",
> + XCSI_GET_BITSET_STR(data, XCSI_CLKINFR_STOP_MASK));
> +
> + v4l2_info(sd, "******** Data Lane Info *********\n");
> + v4l2_info(sd, "Lane\tSoT Error\tSoT Sync Error\tStop State\n");
> + reg = XCSI_L0INFR_OFFSET;
> + for (i = 0; i < 4; i++) {

It'd be nice to have a humna-readable name for 4?

> + data = xcsi2rxss_read(core, reg);
> +
> + v4l2_info(sd, "%d\t%s\t\t%s\t\t%s\n",
> + i,
> + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_SOTERR_MASK),
> + data & XCSI_LXINFR_SOTSYNCERR_MASK ? "true" : "false",
> + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_STOP_MASK));
> +
> + reg += 4;
> + }
> +
> + /* Virtual Channel Image Information */
> + v4l2_info(sd, "********** Virtual Channel Info ************\n");
> + v4l2_info(sd, "VC\tLine Count\tByte Count\tData Type\n");
> + reg = XCSI_VC0INF1R_OFFSET;
> + for (i = 0; i < 4; i++) {
> + u32 line_count, byte_count, data_type;
> + char *datatypestr;
> +
> + /* Get line and byte count from VCXINFR1 Register */
> + data = xcsi2rxss_read(core, reg);
> + byte_count = (data & XCSI_VCXINF1R_BYTECOUNT_MASK) >>
> + XCSI_VCXINF1R_BYTECOUNT_SHIFT;
> + line_count = (data & XCSI_VCXINF1R_LINECOUNT_MASK) >>
> + XCSI_VCXINF1R_LINECOUNT_SHIFT;
> +
> + /* Get data type from VCXINFR2 Register */
> + reg += 4;
> + data = xcsi2rxss_read(core, reg);
> + data_type = (data & XCSI_VCXINF2R_DATATYPE_MASK) >>
> + XCSI_VCXINF2R_DATATYPE_SHIFT;
> + datatypestr = (char *)xcsi2rxss_pxlfmtdttostr(data_type);
> +
> + v4l2_info(sd, "%d\t%d\t\t%d\t\t%s\n",
> + i, line_count, byte_count, datatypestr);
> +
> + /* Move to next pair of VC Info registers */
> + reg += 4;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +/*
> + * xcsi2rxss_subscribe_event - Subscribe to the custom short packet
> + * receive event.
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * There are two types of events to be subscribed.
> + *
> + * First is to register for receiving a short packet.
> + * The short packets received are queued up in a FIFO.
> + * On reception of a short packet, an event will be generated
> + * with the short packet contents copied to its data area.
> + * Application subscribed to this event will poll for POLLPRI.
> + * On getting the event, the app dequeues the event to get the short packet
> + * data.
> + *
> + * Second is to register for Short packet FIFO overflow
> + * In case the rate of receiving short packets is high and
> + * the short packet FIFO overflows, this event will be triggered.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xcsi2rxss_subscribe_event(struct v4l2_subdev *sd,
> + struct v4l2_fh *fh,
> + struct v4l2_event_subscription *sub)
> +{
> + int ret;

Please declare return values, temporary variables etc. as last.

> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + switch (sub->type) {
> + case V4L2_EVENT_XLNXCSIRX_SPKT:
> + case V4L2_EVENT_XLNXCSIRX_SPKT_OVF:
> + case V4L2_EVENT_XLNXCSIRX_SLBF:
> + ret = v4l2_event_subscribe(fh, sub, XCSI_MAX_SPKT, NULL);
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +/**
> + * xcsi2rxss_unsubscribe_event - Unsubscribe from all events registered
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 file handle
> + * @sub: pointer to Event unsubscription structure
> + *
> + * Return: zero on success, else a negative error code.
> + */
> +static int xcsi2rxss_unsubscribe_event(struct v4l2_subdev *sd,
> + struct v4l2_fh *fh,
> + struct v4l2_event_subscription *sub)
> +{
> + int ret = 0;
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + mutex_lock(&xcsi2rxss->lock);
> + ret = v4l2_event_unsubscribe(fh, sub);
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +/**
> + * xcsi2rxss_s_ctrl - This is used to set the Xilinx MIPI CSI-2 V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx MIPI
> + * CSI-2 Rx Subsystem. It is used to set the active lanes in the system.
> + * The event counters can be reset.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xcsi2rxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + int ret = 0;
> + u32 timeout = XCSI_TIMEOUT_VAL;
> + u32 active_lanes = 1;
> +
> + struct xcsi2rxss_state *xcsi2rxss =
> + container_of(ctrl->handler,
> + struct xcsi2rxss_state, ctrl_handler);
> + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + switch (ctrl->id) {
> + case V4L2_CID_XILINX_MIPICSISS_ACT_LANES:
> + /*
> + * This will be called only when "Enable Active Lanes" parameter
> + * is set in design
> + */
> + xcsi2rxss_clr_and_set(core, XCSI_PCR_OFFSET,
> + XCSI_PCR_ACTLANES_MASK, ctrl->val - 1);

Controls is not the mechanism to choose the number of active lanes --- at
least not on the receiver side. Please stick to the number of connected
lanes (data-lanes property); an alternative is to use frame descriptors.
There's a patchset around; I can find a pointer if you're interested.

> +
> + /*
> + * If the core is enabled, wait for active lanes to be
> + * set.
> + *
> + * If core is disabled or there is no clock from DPHY Tx
> + * then the read back won't reflect the updated value
> + * as the PPI clock will not be present.
> + */
> +
> + if (core->dphy_present) {
> + u32 dphyclkstatregoffset = core->dphy_offset +
> + XDPHY_CLKSTATREG_OFFSET;
> +
> + u32 dphyclkstat =
> + xcsi2rxss_read(core, dphyclkstatregoffset) &
> + XDPHY_CLKSTATREG_MODE_MASK;
> +
> + u32 coreenable =
> + xcsi2rxss_read(core, XCSI_CCR_OFFSET) &
> + XCSI_CCR_COREENB_MASK;
> +
> + char lpmstr[] = "Low Power";
> + char hsmstr[] = "High Speed";
> + char esmstr[] = "Escape";
> + char *modestr;
> +
> + switch (dphyclkstat) {
> + case 0:
> + modestr = lpmstr;
> + break;
> + case 1:
> + modestr = hsmstr;
> + break;
> + case 2:
> + modestr = esmstr;
> + break;
> + default:
> + modestr = NULL;
> + break;
> + }
> +
> + dev_dbg(core->dev, "DPHY Clock Lane in %s mode\n",
> + modestr);
> +
> + if (dphyclkstat == XDPHY_HI_SPEED_MODE &&
> + coreenable) {
> + /* Wait for core to apply new active lanes */
> + while (timeout--)
> + udelay(1);
> +
> + active_lanes =
> + xcsi2rxss_read(core, XCSI_PCR_OFFSET);
> + active_lanes &= XCSI_PCR_ACTLANES_MASK;
> + active_lanes++;
> +
> + if (active_lanes != (u32)ctrl->val) {
> + dev_err(core->dev, "Failed to set active lanes!\n");
> + ret = -EAGAIN;
> + }
> + }
> + } else {
> + dev_dbg(core->dev, "No read back as no DPHY present.\n");
> + }
> +
> + dev_dbg(core->dev, "Set active lanes: requested = %d, active = %d\n",
> + ctrl->val, active_lanes);
> + break;
> + case V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS:
> + xcsi2rxss_reset_event_counters(xcsi2rxss);
> + break;
> + default:
> + break;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +/**
> + * xcsi2rxss_g_volatile_ctrl - get the Xilinx MIPI CSI-2 Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * This is used to get the number of frames received by the Xilinx
> + * MIPI CSI-2 Rx.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xcsi2rxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + int ret = 0;
> + struct xcsi2rxss_state *xcsi2rxss =
> + container_of(ctrl->handler,
> + struct xcsi2rxss_state, ctrl_handler);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + switch (ctrl->id) {
> + case V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER:
> + ctrl->val = xcsi2rxss->core.events[0].counter;
> + break;
> + default:
> + ret = -EINVAL;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *xcsi2rxss)
> +{
> + int ret;
> +
> + xcsi2rxss_enable(&xcsi2rxss->core, true);
> +
> + ret = xcsi2rxss_reset(&xcsi2rxss->core);
> + if (ret < 0)
> + return ret;
> +
> + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, true);
> +
> + return 0;
> +}
> +
> +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *xcsi2rxss)
> +{
> + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, false);
> + xcsi2rxss_enable(&xcsi2rxss->core, false);
> +}
> +
> +/**
> + * xcsi2rxss_s_stream - It is used to start/stop the streaming.
> + * @sd: V4L2 Sub device
> + * @enable: Flag (True / False)
> + *
> + * This function controls the start or stop of streaming for the
> + * Xilinx MIPI CSI-2 Rx Subsystem provided the device isn't in
> + * suspended state.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + int ret = 0;
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + if (xcsi2rxss->suspended) {
> + ret = -EBUSY;
> + goto unlock;
> + }
> +
> + if (enable) {
> + if (!xcsi2rxss->streaming) {
> + /* reset the event counters */
> + xcsi2rxss_reset_event_counters(xcsi2rxss);
> +
> + ret = xcsi2rxss_start_stream(xcsi2rxss);
> + if (ret == 0)
> + xcsi2rxss->streaming = true;
> + }
> + } else {
> + if (xcsi2rxss->streaming) {
> + xcsi2rxss_stop_stream(xcsi2rxss);
> + xcsi2rxss->streaming = false;
> + }
> + }
> +unlock:
> + mutex_unlock(&xcsi2rxss->lock);
> + return ret;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss,
> + struct v4l2_subdev_pad_config *cfg,
> + unsigned int pad, u32 which)
> +{
> + switch (which) {
> + case V4L2_SUBDEV_FORMAT_TRY:
> + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad);
> + case V4L2_SUBDEV_FORMAT_ACTIVE:
> + return &xcsi2rxss->formats[pad];
> + default:
> + return NULL;
> + }
> +}
> +
> +/**
> + * xcsi2rxss_get_format - Get the pad format
> + * @sd: Pointer to V4L2 Sub device structure
> + * @cfg: Pointer to sub device pad information structure
> + * @fmt: Pointer to pad level media bus format
> + *
> + * This function is used to get the pad format information.
> + *
> + * Return: 0 on success
> + */
> +static int xcsi2rxss_get_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + mutex_lock(&xcsi2rxss->lock);
> + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
> + fmt->pad, fmt->which);
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +/**
> + * xcsi2rxss_set_format - This is used to set the pad format
> + * @sd: Pointer to V4L2 Sub device structure
> + * @cfg: Pointer to sub device pad information structure
> + * @fmt: Pointer to pad level media bus format
> + *
> + * This function is used to set the pad format.
> + * Since the pad format is fixed in hardware, it can't be
> + * modified on run time. So when a format set is requested by
> + * application, all parameters except the format type is
> + * saved for the pad and the original pad format is sent
> + * back to the application.
> + *
> + * Return: 0 on success
> + */
> +static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_mbus_framefmt *__format;
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> + u32 code;
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + /*
> + * Only the format->code parameter matters for CSI as the
> + * CSI format cannot be changed at runtime.
> + * Ensure that format to set is copied to over to CSI pad format
> + */
> + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
> + fmt->pad, fmt->which);
> +
> + /* Save the pad format code */
> + code = __format->code;
> +
> + /* If the bayer pattern to be set is SXXXX8 then only 1x8 type
> + * is supported and core's data type doesn't matter.
> + * In case the bayer pattern being set is SXXX10 then only
> + * 1x10 type are supported and core should be configured for RAW10.
> + * In case the bayer pattern being set is SXXX12 then only
> + * 1x12 type are supported and core should be configured for RAW12.
> + *
> + * Otherwise don't allow change.
> + */
> + if ((fmt->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGBRG8_1X8 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGRBG8_1X8 ||
> + fmt->format.code == MEDIA_BUS_FMT_SRGGB8_1X8) ||
> + (core->datatype == MIPI_CSI_DT_RAW_10 &&
> + (fmt->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGBRG10_1X10 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGRBG10_1X10 ||
> + fmt->format.code == MEDIA_BUS_FMT_SRGGB10_1X10)) ||
> + (core->datatype == MIPI_CSI_DT_RAW_12 &&
> + (fmt->format.code == MEDIA_BUS_FMT_SBGGR12_1X12 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGBRG12_1X12 ||
> + fmt->format.code == MEDIA_BUS_FMT_SGRBG12_1X12 ||
> + fmt->format.code == MEDIA_BUS_FMT_SRGGB12_1X12))) {
> + /* Copy over the format to be set */
> + *__format = fmt->format;
> + } else {
> + /* Restore the original pad format code */
> + fmt->format.code = code;
> + __format->code = code;
> + }
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +/**
> + * xcsi2rxss_open - Called on v4l2_open()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_open(). It sets the default format
> + * for both pads.
> + *
> + * Return: 0 on success
> + */
> +static int xcsi2rxss_open(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh)
> +{
> + struct v4l2_mbus_framefmt *format;
> + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> +
> + format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> + *format = xcsi2rxss->default_format;
> +
> + format = v4l2_subdev_get_try_format(sd, fh->pad, 1);
> + *format = xcsi2rxss->default_format;
> +
> + return 0;
> +}
> +
> +static int xcsi2rxss_close(struct v4l2_subdev *sd,
> + struct v4l2_subdev_fh *fh)
> +{
> + return 0;
> +}

You can drop the close callback if there's nothing to do there.

> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xcsi2rxss_media_ops = {
> + .link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xcsi2rxss_ctrl_ops = {
> + .g_volatile_ctrl = xcsi2rxss_g_volatile_ctrl,
> + .s_ctrl = xcsi2rxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xcsi2rxss_ctrls[] = {
> + {
> + .ops = &xcsi2rxss_ctrl_ops,
> + .id = V4L2_CID_XILINX_MIPICSISS_ACT_LANES,
> + .name = "MIPI CSI2 Rx Subsystem: Active Lanes",
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .min = 1,
> + .max = 4,
> + .step = 1,
> + .def = 1,
> + }, {
> + .ops = &xcsi2rxss_ctrl_ops,
> + .id = V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER,
> + .name = "MIPI CSI2 Rx Subsystem: Frames Received Counter",
> + .type = V4L2_CTRL_TYPE_INTEGER,
> + .min = 0,
> + .max = 0xFFFFFFFF,
> + .step = 1,
> + .def = 0,
> + .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> + }, {
> + .ops = &xcsi2rxss_ctrl_ops,
> + .id = V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS,
> + .name = "MIPI CSI2 Rx Subsystem: Reset Counters",
> + .type = V4L2_CTRL_TYPE_BUTTON,
> + .min = 0,
> + .max = 1,
> + .step = 1,
> + .def = 0,
> + .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
> + }
> +};
> +
> +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = {
> + .log_status = xcsi2rxss_log_status,
> + .subscribe_event = xcsi2rxss_subscribe_event,
> + .unsubscribe_event = xcsi2rxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xcsi2rxss_video_ops = {
> + .s_stream = xcsi2rxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = {
> + .get_fmt = xcsi2rxss_get_format,
> + .set_fmt = xcsi2rxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xcsi2rxss_ops = {
> + .core = &xcsi2rxss_core_ops,
> + .video = &xcsi2rxss_video_ops,
> + .pad = &xcsi2rxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = {
> + .open = xcsi2rxss_open,
> + .close = xcsi2rxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Power Management
> + */
> +
> +/*
> + * xcsi2rxss_pm_suspend - Function called on Power Suspend
> + * @dev: Pointer to device structure
> + *
> + * On power suspend the CSI-2 Core is disabled if the device isn't
> + * in suspended state and is streaming.
> + *
> + * Return: 0 on success
> + */
> +static int __maybe_unused xcsi2rxss_pm_suspend(struct device *dev)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + if (!xcsi2rxss->suspended && xcsi2rxss->streaming)
> + xcsi2rxss_clr(&xcsi2rxss->core,
> + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
> +
> + xcsi2rxss->suspended = true;
> +
> + mutex_unlock(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +/*
> + * xcsi2rxss_pm_resume - Function called on Power Resume
> + * @dev: Pointer to device structure
> + *
> + * On power resume the CSI-2 Core is enabled when it is in suspended state
> + * and prior to entering suspended state it was streaming.
> + *
> + * Return: 0 on success
> + */
> +static int __maybe_unused xcsi2rxss_pm_resume(struct device *dev)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
> +
> + mutex_lock(&xcsi2rxss->lock);
> +
> + if (xcsi2rxss->suspended && xcsi2rxss->streaming)
> + xcsi2rxss_set(&xcsi2rxss->core,
> + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
> +
> + xcsi2rxss->suspended = false;
> +
> + mutex_unlock(&xcsi2rxss->lock);
> + return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss)
> +{
> + struct device_node *node = xcsi2rxss->core.dev->of_node;
> + struct device_node *ports = NULL;
> + struct device_node *port = NULL;
> + unsigned int nports = 0;
> + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> + int ret;
> + bool iic_present;
> +
> + core->dphy_present = of_property_read_bool(node, "xlnx,dphy-present");
> + dev_dbg(core->dev, "DPHY present property = %s\n",
> + core->dphy_present ? "Present" : "Absent");
> +
> + iic_present = of_property_read_bool(node, "xlnx,iic-present");
> + dev_dbg(core->dev, "IIC present property = %s\n",
> + iic_present ? "Present" : "Absent");
> +
> + if (core->dphy_present) {
> + if (iic_present)
> + core->dphy_offset = 0x20000;
> + else
> + core->dphy_offset = 0x10000;
> + }
> +
> + ret = of_property_read_u32(node, "xlnx,max-lanes",
> + &core->max_num_lanes);
> + if (ret < 0) {
> + dev_err(core->dev, "missing xlnx,max-lanes property\n");
> + return ret;
> + }
> +
> + if (core->max_num_lanes > 4 || core->max_num_lanes < 1) {
> + dev_err(core->dev, "%d max lanes : invalid xlnx,max-lanes property\n",
> + core->max_num_lanes);
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_u32(node, "xlnx,vc", &core->vc);
> + if (ret < 0) {
> + dev_err(core->dev, "missing xlnx,vc property\n");
> + return ret;
> + }
> + if (core->vc > 4) {
> + dev_err(core->dev, "invalid virtual channel property value.\n");
> + return -EINVAL;
> + }
> +
> + core->enable_active_lanes =
> + of_property_read_bool(node, "xlnx,en-active-lanes");
> + dev_dbg(core->dev, "Enable active lanes property = %s\n",
> + core->enable_active_lanes ? "Present" : "Absent");
> +
> + ret = of_property_read_string(node, "xlnx,csi-pxl-format",
> + &core->pxlformat);
> + if (ret < 0) {
> + dev_err(core->dev, "missing xlnx,csi-pxl-format property\n");
> + return ret;
> + }
> +
> + core->datatype = xcsi2rxss_pxlfmtstrtodt(core->pxlformat);
> + if (core->datatype < MIPI_CSI_DT_YUV_420_8B ||
> + core->datatype > MIPI_CSI_DT_RAW_14) {
> + dev_err(core->dev, "Invalid xlnx,csi-pxl-format string\n");
> + return -EINVAL;
> + }
> +
> + core->vfb = of_property_read_bool(node, "xlnx,vfb");
> + dev_dbg(core->dev, "Video Format Bridge property = %s\n",
> + core->vfb ? "Present" : "Absent");
> +
> + if (core->vfb) {
> + ret = of_property_read_u32(node, "xlnx,ppc", &core->ppc);
> + if (ret < 0 || !(core->ppc == 1 || core->ppc == 2 ||
> + core->ppc == 4)) {
> + dev_err(core->dev, "Invalid xlnx,ppc property ret = %d ppc = %d\n",
> + ret, core->ppc);
> + return -EINVAL;
> + }
> + }
> +
> + ports = of_get_child_by_name(node, "ports");
> + if (!ports)
> + ports = node;
> +
> + for_each_child_of_node(ports, port) {
> + int ret;
> + const struct xvip_video_format *format;
> + struct device_node *endpoint;
> + struct v4l2_fwnode_endpoint v4lendpoint;
> +
> + if (!port->name || of_node_cmp(port->name, "port"))
> + continue;
> +
> + /*
> + * Currently only a subset of VFB enabled formats present in
> + * xvip are supported in the driver.
> + *
> + * If the VFB is disabled, the pixels per clock don't matter.
> + * The data width is either 32 or 64 bit as selected in design.
> + *
> + * For e.g. If Data Type is RGB888, VFB is disabled and
> + * data width is 32 bits.
> + *
> + * Clk Cycle | Byte 0 | Byte 1 | Byte 2 | Byte 3
> + * -----------+----------+----------+----------+----------
> + * 1 | B0 | G0 | R0 | B1
> + * 2 | G1 | R1 | B2 | G2
> + * 3 | R2 | B3 | G3 | R3
> + */
> + format = xvip_of_get_format(port);
> + if (IS_ERR(format)) {
> + dev_err(core->dev, "invalid format in DT");
> + return PTR_ERR(format);
> + }
> +
> + if (core->vfb &&
> + format->vf_code != XVIP_VF_YUV_422 &&
> + format->vf_code != XVIP_VF_RBG &&
> + format->vf_code != XVIP_VF_MONO_SENSOR) {
> + dev_err(core->dev, "Invalid UG934 video format set.\n");
> + return -EINVAL;
> + }
> +
> + /* Get and check the format description */
> + if (!xcsi2rxss->vip_format) {
> + xcsi2rxss->vip_format = format;
> + } else if (xcsi2rxss->vip_format != format) {
> + dev_err(core->dev, "in/out format mismatch in DT");
> + return -EINVAL;
> + }
> +
> + endpoint = of_get_next_child(port, NULL);
> + if (!endpoint) {
> + dev_err(core->dev, "No port at\n");
> + return -EINVAL;
> + }
> +
> + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
> + &v4lendpoint);
> + if (ret) {
> + of_node_put(endpoint);
> + return ret;
> + }
> +
> + of_node_put(endpoint);
> + dev_dbg(core->dev, "%s : port %d bus type = %d\n",
> + __func__, nports, v4lendpoint.bus_type);
> +
> + if (v4lendpoint.bus_type == V4L2_MBUS_CSI2) {
> + dev_dbg(core->dev, "%s : base.port = %d base.id = %d\n",
> + __func__, v4lendpoint.base.port,
> + v4lendpoint.base.id);
> +
> + dev_dbg(core->dev, "%s : mipi number lanes = %d\n",
> + __func__,
> + v4lendpoint.bus.mipi_csi2.num_data_lanes);
> + } else {
> + dev_dbg(core->dev, "%s : Not a CSI2 bus\n", __func__);
> + }
> +
> + /* Count the number of ports. */
> + nports++;
> + }
> +
> + if (nports != 2) {
> + dev_err(core->dev, "invalid number of ports %u\n", nports);
> + return -EINVAL;
> + }
> + xcsi2rxss->npads = nports;
> +
> + /*Register interrupt handler */
> + core->irq = irq_of_parse_and_map(node, 0);
> +
> + ret = devm_request_irq(core->dev, core->irq, xcsi2rxss_irq_handler,
> + IRQF_SHARED, "xilinx-csi2rxss", xcsi2rxss);
> + if (ret) {
> + dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> + ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int xcsi2rxss_probe(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *subdev;
> + struct xcsi2rxss_state *xcsi2rxss;
> + struct resource *res;
> +
> + u32 i;
> + int ret;
> + int num_ctrls;
> +
> + xcsi2rxss = devm_kzalloc(&pdev->dev, sizeof(*xcsi2rxss), GFP_KERNEL);
> + if (!xcsi2rxss)
> + return -ENOMEM;
> +
> + mutex_init(&xcsi2rxss->lock);
> +
> + xcsi2rxss->core.dev = &pdev->dev;
> +
> + ret = xcsi2rxss_parse_of(xcsi2rxss);
> + if (ret < 0)
> + return ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + xcsi2rxss->core.iomem = devm_ioremap_resource(xcsi2rxss->core.dev, res);
> + if (IS_ERR(xcsi2rxss->core.iomem))
> + return PTR_ERR(xcsi2rxss->core.iomem);
> +
> + /*
> + * Reset and initialize the core.
> + */
> + xcsi2rxss_reset(&xcsi2rxss->core);
> +
> + xcsi2rxss->core.events = (struct xcsi2rxss_event *)&xcsi2rxss_events;
> +
> + /* Initialize V4L2 subdevice and media entity */
> + xcsi2rxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> + xcsi2rxss->pads[1].flags = MEDIA_PAD_FL_SINK;
> +
> + /* Initialize the default format */
> + memset(&xcsi2rxss->default_format, 0,
> + sizeof(xcsi2rxss->default_format));
> + xcsi2rxss->default_format.code = xcsi2rxss->vip_format->code;
> + xcsi2rxss->default_format.field = V4L2_FIELD_NONE;
> + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB;
> + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH;
> + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT;
> +
> + xcsi2rxss->formats[0] = xcsi2rxss->default_format;
> + xcsi2rxss->formats[1] = xcsi2rxss->default_format;
> +
> + /* Initialize V4L2 subdevice and media entity */
> + subdev = &xcsi2rxss->subdev;
> + v4l2_subdev_init(subdev, &xcsi2rxss_ops);
> +
> + subdev->dev = &pdev->dev;
> + subdev->internal_ops = &xcsi2rxss_internal_ops;
> + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
> +
> + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> + subdev->entity.ops = &xcsi2rxss_media_ops;
> +
> + v4l2_set_subdevdata(subdev, xcsi2rxss);
> +
> + ret = media_entity_pads_init(&subdev->entity, 2, xcsi2rxss->pads);
> + if (ret < 0)
> + goto error;
> +
> + /*
> + * In case the Enable Active Lanes config parameter is not set,
> + * dynamic lane reconfiguration is not allowed.
> + * So V4L2_CID_XILINX_MIPICSISS_ACT_LANES ctrl will not be registered.
> + * Accordingly allocate the number of controls
> + */
> + num_ctrls = ARRAY_SIZE(xcsi2rxss_ctrls);
> +
> + if (!xcsi2rxss->core.enable_active_lanes)
> + num_ctrls--;
> +
> + dev_dbg(xcsi2rxss->core.dev, "# of ctrls = %d\n", num_ctrls);
> +
> + v4l2_ctrl_handler_init(&xcsi2rxss->ctrl_handler, num_ctrls);
> +
> + for (i = 0; i < ARRAY_SIZE(xcsi2rxss_ctrls); i++) {
> + struct v4l2_ctrl *ctrl;
> +
> + if (xcsi2rxss_ctrls[i].id ==
> + V4L2_CID_XILINX_MIPICSISS_ACT_LANES) {
> + if (xcsi2rxss->core.enable_active_lanes) {
> + xcsi2rxss_ctrls[i].max =
> + xcsi2rxss->core.max_num_lanes;
> + } else {
> + /* Don't register control */
> + dev_dbg(xcsi2rxss->core.dev,
> + "Skip active lane control\n");
> + continue;
> + }
> + }
> +
> + dev_dbg(xcsi2rxss->core.dev, "%d ctrl = 0x%x\n",
> + i, xcsi2rxss_ctrls[i].id);
> + ctrl = v4l2_ctrl_new_custom(&xcsi2rxss->ctrl_handler,
> + &xcsi2rxss_ctrls[i], NULL);
> + if (!ctrl) {
> + dev_err(xcsi2rxss->core.dev, "Failed for %s ctrl\n",
> + xcsi2rxss_ctrls[i].name);
> + goto error;
> + }
> + }
> +
> + dev_dbg(xcsi2rxss->core.dev, "# v4l2 ctrls registered = %d\n", i - 1);
> +
> + if (xcsi2rxss->ctrl_handler.error) {
> + dev_err(&pdev->dev, "failed to add controls\n");
> + ret = xcsi2rxss->ctrl_handler.error;
> + goto error;
> + }
> +
> + subdev->ctrl_handler = &xcsi2rxss->ctrl_handler;
> +
> + ret = v4l2_ctrl_handler_setup(&xcsi2rxss->ctrl_handler);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to set controls\n");
> + goto error;
> + }
> +
> + platform_set_drvdata(pdev, xcsi2rxss);
> +
> + dev_info(xcsi2rxss->core.dev, "Xilinx CSI2 Rx Subsystem device found!\n");
> +
> + ret = v4l2_async_register_subdev(subdev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "failed to register subdev\n");
> + goto error;
> + }
> +
> + /* default states for streaming and suspend */
> + xcsi2rxss->streaming = false;
> + xcsi2rxss->suspended = false;
> + return 0;
> +
> +error:
> + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> + mutex_destroy(&xcsi2rxss->lock);
> +
> + return ret;
> +}
> +
> +static int xcsi2rxss_remove(struct platform_device *pdev)
> +{
> + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev);
> + struct v4l2_subdev *subdev = &xcsi2rxss->subdev;
> +
> + v4l2_async_unregister_subdev(subdev);
> + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> + mutex_destroy(&xcsi2rxss->lock);
> +
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(xcsi2rxss_pm_ops,
> + xcsi2rxss_pm_suspend, xcsi2rxss_pm_resume);
> +
> +static const struct of_device_id xcsi2rxss_of_id_table[] = {
> + { .compatible = "xlnx,mipi-csi2-rx-subsystem-2.0" },
> + { .compatible = "xlnx,mipi-csi2-rx-subsystem-3.0" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table);
> +
> +static struct platform_driver xcsi2rxss_driver = {
> + .driver = {
> + .name = "xilinx-csi2rxss",
> + .pm = &xcsi2rxss_pm_ops,
> + .of_match_table = xcsi2rxss_of_id_table,
> + },
> + .probe = xcsi2rxss_probe,
> + .remove = xcsi2rxss_remove,
> +};
> +module_platform_driver(xcsi2rxss_driver);

A newline here would be nice.

> +MODULE_AUTHOR("Vishal Sagar <[email protected]>");
> +MODULE_DESCRIPTION("Xilinx MIPI CSI2 Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-csi2rxss.h b/include/uapi/linux/xilinx-csi2rxss.h
> new file mode 100644
> index 0000000..973d5b46
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-csi2rxss.h
> @@ -0,0 +1,25 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/*
> + * Events from Xilinx MIPI CSI2 Rx Subsystem exposed to V4L2 application.
> + *
> + * Copyright (C) 2016 - 2018 Xilinx, Inc.
> + * Author: Vishal Sagar <[email protected]>
> + */
> +#ifndef __UAPI_XILINX_CSI2RXSS_H__
> +#define __UAPI_XILINX_CSI2RXSS_H__
> +
> +#include <linux/videodev2.h>
> +
> +/*
> + * Events
> + *
> + * V4L2_EVENT_XLNXCSIRX_SPKT: Short packet received
> + * V4L2_EVENT_XLNXCSIRX_SPKT_OVF: Short packet FIFO overflow
> + * V4L2_EVENT_XLNXCSIRX_SLBF: Stream line buffer full
> + */
> +#define V4L2_EVENT_XLNXCSIRX_CLASS (V4L2_EVENT_PRIVATE_START | 0x100)
> +#define V4L2_EVENT_XLNXCSIRX_SPKT (V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
> +#define V4L2_EVENT_XLNXCSIRX_SPKT_OVF (V4L2_EVENT_XLNXCSIRX_CLASS | 0x2)
> +#define V4L2_EVENT_XLNXCSIRX_SLBF (V4L2_EVENT_XLNXCSIRX_CLASS | 0x3)
> +
> +#endif /* __UAPI_XILINX_CSI2RXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index b6441fe..4ca3b44 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -71,4 +71,18 @@
> /* Noise level */
> #define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17)
>
> +/*
> + * Xilinx MIPI CSI2 Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_MIPICSISS (V4L2_CID_USER_BASE + 0xc080)
> +
> +/* Active Lanes */
> +#define V4L2_CID_XILINX_MIPICSISS_ACT_LANES (V4L2_CID_XILINX_MIPICSISS + 1)
> +/* Frames received since streaming is set */
> +#define V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER (V4L2_CID_XILINX_MIPICSISS + 2)
> +/* Reset all event counters */
> +#define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 3)
> +
> #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> --
> 2.7.4
>
> This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

--
Regards,

Sakari Ailus

2019-01-14 09:49:16

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem

Hi Sakari,

Thanks for reviewing this.

> -----Original Message-----
> From: Sakari Ailus [mailto:[email protected]]
> Sent: Tuesday, January 08, 2019 6:35 PM
> To: Vishal Sagar <[email protected]>
> Cc: Hyun Kwon <[email protected]>; [email protected];
> Michal Simek <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected]; Dinesh Kumar
> <[email protected]>; [email protected]; linux-
> [email protected]
> Subject: Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2
> Rx Subsystem
>
> EXTERNAL EMAIL
>
> Hi Vishal,
>
> The patchset hard escaped me somehow earlier and your reply to Rob made me
> notice it again. Thanks. :-)
>
> On Wed, May 30, 2018 at 12:24:43AM +0530, Vishal Sagar wrote:
> > Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem.
> >
> > The Xilinx MIPI CSI-2 Rx Subsystem consists of a DPHY, CSI-2 Rx, an
> > optional I2C controller and an optional Video Format Bridge (VFB). The
> > active lanes can be configured at run time if enabled in the IP. The
> > DPHY register interface may also be enabled.
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > .../bindings/media/xilinx/xlnx,csi2rxss.txt | 117
> +++++++++++++++++++++
> > 1 file changed, 117 insertions(+)
> > create mode 100644
> Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> >
> > diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > new file mode 100644
> > index 0000000..31ed721
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > @@ -0,0 +1,117 @@
> > +
>
> Extra newline.
>

Will remove it in next version.

> > +Xilinx MIPI CSI2 Receiver Subsystem Device Tree Bindings
> > +--------------------------------------------------------
> > +
> > +The Xilinx MIPI CSI2 Receiver Subsystem is used to capture MIPI CSI2 traffic
> > +from compliant camera sensors and send the output as AXI4 Stream video
> data
> > +for image processing.
> > +
> > +The subsystem consists of a MIPI DPHY in slave mode which captures the
> > +data packets. This is passed along the MIPI CSI2 Rx IP which extracts the
> > +packet data. This data is taken in by the Video Format Bridge (VFB),
> > +if selected, and converted into AXI4 Stream video data at selected
> > +pixels per clock as per AXI4-Stream Video IP and System Design UG934.
> > +
> > +For more details, please refer to PG232 MIPI CSI-2 Receiver Subsystem.
> >
> +https://www.xilinx.com/support/documentation/ip_documentation/mipi_csi
> 2_rx_subsystem/v3_0/pg232-mipi-csi2-rx.pdf
> > +
> > +Required properties:
> > +
> > +- compatible: Must contain "xlnx,mipi-csi2-rx-subsystem-2.0" or
> > + "xlnx,mipi-csi2-rx-subsystem-3.0"
> > +
> > +- reg: Physical base address and length of the registers set for the device.
> > +
> > +- interrupt-parent: specifies the phandle to the parent interrupt controller
> > +
> > +- interrupts: Property with a value describing the interrupt number.
> > +
> > +- xlnx,max-lanes: Maximum active lanes in the design.
> > +
> > +- xlnx,vc: Virtual Channel, specifies virtual channel number to be filtered.
> > + If this is 4 then all virtual channels are allowed.
>
> This seems like something a driver should configure, based on the
> configuration of the connected device.
>

The filtering of the Virtual channels is property of the hardware IP and is fixed in design.
This is not software controlled.

> > +
> > +- xlnx,csi-pxl-format: This denotes the CSI Data type selected in hw design.
> > + Packets other than this data type (except for RAW8 and User defined data
> > + types) will be filtered out. Possible values are RAW6, RAW7, RAW8, RAW10,
> > + RAW12, RAW14, RGB444, RGB555, RGB565, RGB666, RGB888 and
> YUV4228bit.
>
> This should be configured at runtime instead through V4L2 sub-device
> interface; it's not a property of the hardware.
>

This too is a property of the hardware IP and is fixed to one data type during design to reduce gate count.
So for e.g. if RGB888 is selected during design, then the hardware will only pass across RGB888 packet data to output.
(RAW8 packets are also allowed to pass through for all data types selected)
This is used in the driver to determine the media bus format of the connected pads.

> > +
> > +- xlnx,axis-tdata-width: AXI Stream width, This denotes the AXI Stream width.
> > + It depends on Data type chosen, Video Format Bridge enabled/disabled and
> > + pixels per clock. If VFB is disabled then its value is either 0x20 (32 bit)
> > + or 0x40(64 bit) width.
> > +
> > +- xlnx,video-format, xlnx,video-width: Video format and width, as defined in
> > + video.txt.
>
> Ditto.
>
Again these are fixed values and can't be changed at run time.
These are used to determine the media bus format.

> > +
> > +- port: Video port, using the DT bindings defined in ../video-interfaces.txt.
> > + The CSI 2 Rx Subsystem has a two ports, one input port for connecting to
> > + camera sensor and other is output port.
> > +
> > +- data-lanes: The number of data lanes through which CSI2 Rx Subsystem is
> > + connected to the camera sensor as per video-interfaces.txt
>
> This is somewhat different from the documentation in video-interfaces.txt.
> Could you align the two? I don't think there's a need to document standard
> properties in device binding files elaborately; rather just the hardware
> specific bits.
>

Agree. In this current IP there is no way to re-order the lanes which are set at design time.
So physical and logical lanes are at same index. This could only be used to determine how many lanes are allowed to be programmed.
For e.g. if design has set the number of lanes as 4 and xlnx,en-active-lanes is present, then the number of lanes
can be set from 1 to 4.

> > +
> > +Optional properties:
> > +
> > +- xlnx,en-active-lanes: Enable Active lanes configuration in Protocol
> > + Configuration Register.
> > +
> > +- xlnx,dphy-present: This is equivalent to whether DPHY register interface is
> > + enabled or not.
> > +
> > +- xlnx,iic-present: This shows whether subsystem's IIC is present or not. This
> > + affects the base address of the DPHY.
> > +
> > +- xlnx,vfb: Video Format Bridge, Denotes if Video Format Bridge is selected
> > + so that output is as per AXI stream documented in UG934.
> > +
> > +- xlnx,ppc: Pixels per clock, Number of pixels to be transferred per pixel
> > + clock. This is valid only if xlnx,vfb property is present.
> > +
> > +Example:
> > +
> > + csiss_1: csiss@a0020000 {
> > + compatible = "xlnx,mipi-csi2-rx-subsystem-3.0";
> > + reg = <0x0 0xa0020000 0x0 0x20000>;
> > + interrupt-parent = <&gic>;
> > + interrupts = <0 95 4>;
> > +
> > + xlnx,max-lanes = <0x4>;
> > + xlnx,en-active-lanes;
> > + xlnx,dphy-present;
> > + xlnx,iic-present;
> > + xlnx,vc = <0x4>;
> > + xlnx,csi-pxl-format = "RAW8";
> > + xlnx,vfb;
> > + xlnx,ppc = <0x4>;
> > + xlnx,axis-tdata-width = <0x20>;
> > +
> > + ports {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + port@0 {
> > + reg = <0>;
> > +
> > + xlnx,video-format = <XVIP_VF_YUV_422>;
> > + xlnx,video-width = <8>;
> > + csiss_out: endpoint {
> > + remote-endpoint = <&vcap_csiss_in>;
> > + };
> > + };
> > + port@1 {
> > + reg = <1>;
> > +
> > + xlnx,video-format = <XVIP_VF_YUV_422>;
> > + xlnx,video-width = <8>;
> > +
> > + csiss_in: endpoint {
> > + data-lanes = <1 2 3 4>;
> > + /* MIPI CSI2 Camera handle */
> > + remote-endpoint = <&vs2016_out>;
> > + };
> > +
> > + };
> > +
> > + };
> > + };
> > --
> > 2.7.4
> >
> > This email and any attachments are intended for the sole use of the named
> recipient(s) and contain(s) confidential information that may be proprietary,
> privileged or copyrighted under applicable law. If you are not the intended
> recipient, do not read, copy, or forward this email message or any attachments.
> Delete this email message and any attachments immediately.
>
> Could you drop this from v2?
>

Ok.

Regards
Vishal Sagar

> --
> Regards,
>
> Sakari Ailus
> [email protected]

2019-01-14 09:52:21

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

Hi Sakari,

Thanks for reviewing my patch.

> -----Original Message-----
> From: [email protected] [mailto:linux-media-
> [email protected]] On Behalf Of Sakari Ailus
> Sent: Wednesday, January 09, 2019 5:22 PM
> To: Vishal Sagar <[email protected]>
> Cc: Hyun Kwon <[email protected]>; [email protected];
> Michal Simek <[email protected]>; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; Dinesh Kumar <[email protected]>; linux-arm-
> [email protected]; [email protected]
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem
> driver
>
> EXTERNAL EMAIL
>
> Hi Vishal,
>
> Please see my comments below.
>
> On Wed, May 30, 2018 at 12:24:44AM +0530, Vishal Sagar wrote:
> > The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images
> > from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready
> > for image processing.
> >
> > It supports upto 4 lanes, filtering based on Virtual Channel selected in
> > IP, an optional Xilinx IIC controller, optional register interface for
> > the DPHY, multiple color formats, short packet capture,
> >
> > This driver helps configure the number of active lanes to be set,
> > setting and handling interrupts and IP core enable.
> > It logs the count of events occurring according to their type between
> > streaming ON and OFF. The short packet reception is notified to
> > application via v4l2_event.
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > drivers/media/platform/xilinx/Kconfig | 12 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751
> +++++++++++++++++++++++
> > include/uapi/linux/xilinx-csi2rxss.h | 25 +
> > include/uapi/linux/xilinx-v4l2-controls.h | 14 +
> > 5 files changed, 1803 insertions(+)
> > create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
> > create mode 100644 include/uapi/linux/xilinx-csi2rxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index a5d21b7..06d5944 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -8,6 +8,18 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +config VIDEO_XILINX_CSI2RXSS
> > + tristate "Xilinx CSI2 Rx Subsystem"
> > + depends on VIDEO_XILINX
>
> I'm no Kconfig expert, but I'd think that you don't need to depend on
> things that the option already depends on if (above).
>

Ok. I will check and remove this if ok in next version.

> > + help
> > + Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
> > + based driver that takes input from CSI2 Tx source and converts
> > + it into an AXI4-Stream. It has a DPHY (whose register interface
> > + can be enabled, an optional I2C controller and an optional Video
> > + Format Bridge which converts the AXI4-Stream data to Xilinx Video
> > + Bus formats based on UG934. The driver is used to set the number
> > + of active lanes and get short packet data.
> > +
> > config VIDEO_XILINX_TPG
> > tristate "Xilinx Video Test Pattern Generator"
> > depends on VIDEO_XILINX
> > diff --git a/drivers/media/platform/xilinx/Makefile
> b/drivers/media/platform/xilinx/Makefile
> > index e8a0f2a..768f079 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -1,5 +1,6 @@
> > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> >
> > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
> > +obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o
> > obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
> > obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
> > diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> > new file mode 100644
> > index 0000000..03f387c
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
> > @@ -0,0 +1,1751 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Driver for Xilinx MIPI CSI2 Rx Subsystem
> > + *
> > + * Copyright (C) 2016 - 2018 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + *
> > + */
> > +
> > +#include <dt-bindings/media/xilinx-vip.h>
> > +#include <linux/bitops.h>
> > +#include <linux/compiler.h>
> > +#include <linux/delay.h>
> > +#include <linux/device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/spinlock_types.h>
> > +#include <linux/types.h>
> > +#include <linux/v4l2-subdev.h>
> > +#include <linux/xilinx-csi2rxss.h>
> > +#include <linux/xilinx-v4l2-controls.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +#include "xilinx-vip.h"
> > +
> > +/*
> > + * MIPI CSI2 Rx register map, bitmask and offsets
> > + */
> > +#define XCSI_CCR_OFFSET 0x00000000
> > +#define XCSI_CCR_SOFTRESET_SHIFT 1
> > +#define XCSI_CCR_COREENB_SHIFT 0
> > +#define XCSI_CCR_SOFTRESET_MASK
> BIT(XCSI_CCR_SOFTRESET_SHIFT)
> > +#define XCSI_CCR_COREENB_MASK BIT(XCSI_CCR_COREENB_SHIFT)
> > +
> > +#define XCSI_PCR_OFFSET 0x00000004
> > +#define XCSI_PCR_MAXLANES_MASK 0x00000018
> > +#define XCSI_PCR_ACTLANES_MASK 0x00000003
> > +#define XCSI_PCR_MAXLANES_SHIFT 3
> > +#define XCSI_PCR_ACTLANES_SHIFT 0
> > +
> > +#define XCSI_CSR_OFFSET 0x00000010
> > +#define XCSI_CSR_PKTCOUNT_SHIFT 16
> > +#define XCSI_CSR_SPFIFOFULL_SHIFT 3
> > +#define XCSI_CSR_SPFIFONE_SHIFT 2
> > +#define XCSI_CSR_SLBF_SHIFT 1
> > +#define XCSI_CSR_RIPCD_SHIFT 0
> > +#define XCSI_CSR_PKTCOUNT_MASK 0xFFFF0000
> > +#define XCSI_CSR_SPFIFOFULL_MASK BIT(XCSI_CSR_SPFIFOFULL_SHIFT)
> > +#define XCSI_CSR_SPFIFONE_MASK BIT(XCSI_CSR_SPFIFONE_SHIFT)
> > +#define XCSI_CSR_SLBF_MASK BIT(XCSI_CSR_SLBF_SHIFT)
> > +#define XCSI_CSR_RIPCD_MASK BIT(XCSI_CSR_RIPCD_SHIFT)
> > +
> > +#define XCSI_GIER_OFFSET 0x00000020
> > +#define XCSI_GIER_GIE_SHIFT 0
> > +#define XCSI_GIER_GIE_MASK BIT(XCSI_GIER_GIE_SHIFT)
> > +#define XCSI_GIER_SET 1
> > +#define XCSI_GIER_RESET 0
> > +
> > +#define XCSI_ISR_OFFSET 0x00000024
> > +#define XCSI_ISR_FR_SHIFT 31
> > +#define XCSI_ISR_ILC_SHIFT 21
> > +#define XCSI_ISR_SPFIFOF_SHIFT 20
> > +#define XCSI_ISR_SPFIFONE_SHIFT 19
> > +#define XCSI_ISR_SLBF_SHIFT 18
> > +#define XCSI_ISR_STOP_SHIFT 17
> > +#define XCSI_ISR_SOTERR_SHIFT 13
> > +#define XCSI_ISR_SOTSYNCERR_SHIFT 12
> > +#define XCSI_ISR_ECC2BERR_SHIFT 11
> > +#define XCSI_ISR_ECC1BERR_SHIFT 10
> > +#define XCSI_ISR_CRCERR_SHIFT 9
> > +#define XCSI_ISR_DATAIDERR_SHIFT 8
> > +#define XCSI_ISR_VC3FSYNCERR_SHIFT 7
> > +#define XCSI_ISR_VC3FLVLERR_SHIFT 6
> > +#define XCSI_ISR_VC2FSYNCERR_SHIFT 5
> > +#define XCSI_ISR_VC2FLVLERR_SHIFT 4
> > +#define XCSI_ISR_VC1FSYNCERR_SHIFT 3
> > +#define XCSI_ISR_VC1FLVLERR_SHIFT 2
> > +#define XCSI_ISR_VC0FSYNCERR_SHIFT 1
> > +#define XCSI_ISR_VC0FLVLERR_SHIFT 0
> > +#define XCSI_ISR_FR_MASK BIT(XCSI_ISR_FR_SHIFT)
> > +#define XCSI_ISR_ILC_MASK BIT(XCSI_ISR_ILC_SHIFT)
> > +#define XCSI_ISR_SPFIFOF_MASK BIT(XCSI_ISR_SPFIFOF_SHIFT)
> > +#define XCSI_ISR_SPFIFONE_MASK BIT(XCSI_ISR_SPFIFONE_SHIFT)
> > +#define XCSI_ISR_SLBF_MASK BIT(XCSI_ISR_SLBF_SHIFT)
> > +#define XCSI_ISR_STOP_MASK BIT(XCSI_ISR_STOP_SHIFT)
> > +#define XCSI_ISR_SOTERR_MASK BIT(XCSI_ISR_SOTERR_SHIFT)
> > +#define XCSI_ISR_SOTSYNCERR_MASK BIT(XCSI_ISR_SOTSYNCERR_SHIFT)
> > +#define XCSI_ISR_ECC2BERR_MASK BIT(XCSI_ISR_ECC2BERR_SHIFT)
> > +#define XCSI_ISR_ECC1BERR_MASK BIT(XCSI_ISR_ECC1BERR_SHIFT)
> > +#define XCSI_ISR_CRCERR_MASK BIT(XCSI_ISR_CRCERR_SHIFT)
> > +#define XCSI_ISR_DATAIDERR_MASK
> BIT(XCSI_ISR_DATAIDERR_SHIFT)
> > +#define XCSI_ISR_VC3FSYNCERR_MASK
> BIT(XCSI_ISR_VC3FSYNCERR_SHIFT)
> > +#define XCSI_ISR_VC3FLVLERR_MASK BIT(XCSI_ISR_VC3FLVLERR_SHIFT)
> > +#define XCSI_ISR_VC2FSYNCERR_MASK
> BIT(XCSI_ISR_VC2FSYNCERR_SHIFT)
> > +#define XCSI_ISR_VC2FLVLERR_MASK BIT(XCSI_ISR_VC2FLVLERR_SHIFT)
> > +#define XCSI_ISR_VC1FSYNCERR_MASK
> BIT(XCSI_ISR_VC1FSYNCERR_SHIFT)
> > +#define XCSI_ISR_VC1FLVLERR_MASK BIT(XCSI_ISR_VC1FLVLERR_SHIFT)
> > +#define XCSI_ISR_VC0FSYNCERR_MASK
> BIT(XCSI_ISR_VC0FSYNCERR_SHIFT)
> > +#define XCSI_ISR_VC0FLVLERR_MASK BIT(XCSI_ISR_VC0FLVLERR_SHIFT)
> > +#define XCSI_ISR_ALLINTR_MASK 0x803FFFFF
> > +
> > +#define XCSI_INTR_PROT_MASK (XCSI_ISR_VC3FSYNCERR_MASK | \
> > + XCSI_ISR_VC3FLVLERR_MASK | \
> > + XCSI_ISR_VC2FSYNCERR_MASK | \
> > + XCSI_ISR_VC2FLVLERR_MASK | \
> > + XCSI_ISR_VC1FSYNCERR_MASK | \
> > + XCSI_ISR_VC1FLVLERR_MASK | \
> > + XCSI_ISR_VC0FSYNCERR_MASK | \
> > + XCSI_ISR_VC0FLVLERR_MASK)
> > +
> > +#define XCSI_INTR_PKTLVL_MASK (XCSI_ISR_ECC2BERR_MASK | \
> > + XCSI_ISR_ECC1BERR_MASK | \
> > + XCSI_ISR_CRCERR_MASK | \
> > + XCSI_ISR_DATAIDERR_MASK)
> > +
> > +#define XCSI_INTR_DPHY_MASK (XCSI_ISR_SOTERR_MASK | \
> > + XCSI_ISR_SOTSYNCERR_MASK)
> > +
> > +#define XCSI_INTR_SPKT_MASK (XCSI_ISR_SPFIFOF_MASK | \
> > + XCSI_ISR_SPFIFONE_MASK)
> > +
> > +#define XCSI_INTR_FRAMERCVD_MASK (XCSI_ISR_FR_MASK)
> > +
> > +#define XCSI_INTR_ERR_MASK (XCSI_ISR_ILC_MASK | \
> > + XCSI_ISR_SLBF_MASK | \
> > + XCSI_ISR_STOP_MASK)
> > +
> > +#define XCSI_IER_OFFSET 0x00000028
> > +#define XCSI_IER_FR_SHIFT 31
> > +#define XCSI_IER_ILC_SHIFT 21
> > +#define XCSI_IER_SPFIFOF_SHIFT 20
> > +#define XCSI_IER_SPFIFONE_SHIFT 19
> > +#define XCSI_IER_SLBF_SHIFT 18
> > +#define XCSI_IER_STOP_SHIFT 17
> > +#define XCSI_IER_SOTERR_SHIFT 13
> > +#define XCSI_IER_SOTSYNCERR_SHIFT 12
> > +#define XCSI_IER_ECC2BERR_SHIFT 11
> > +#define XCSI_IER_ECC1BERR_SHIFT 10
> > +#define XCSI_IER_CRCERR_SHIFT 9
> > +#define XCSI_IER_DATAIDERR_SHIFT 8
> > +#define XCSI_IER_VC3FSYNCERR_SHIFT 7
> > +#define XCSI_IER_VC3FLVLERR_SHIFT 6
> > +#define XCSI_IER_VC2FSYNCERR_SHIFT 5
> > +#define XCSI_IER_VC2FLVLERR_SHIFT 4
> > +#define XCSI_IER_VC1FSYNCERR_SHIFT 3
> > +#define XCSI_IER_VC1FLVLERR_SHIFT 2
> > +#define XCSI_IER_VC0FSYNCERR_SHIFT 1
> > +#define XCSI_IER_VC0FLVLERR_SHIFT 0
> > +#define XCSI_IER_FR_MASK BIT(XCSI_IER_FR_SHIFT)
> > +#define XCSI_IER_ILC_MASK BIT(XCSI_IER_ILC_SHIFT)
> > +#define XCSI_IER_SPFIFOF_MASK BIT(XCSI_IER_SPFIFOF_SHIFT)
> > +#define XCSI_IER_SPFIFONE_MASK BIT(XCSI_IER_SPFIFONE_SHIFT)
> > +#define XCSI_IER_SLBF_MASK BIT(XCSI_IER_SLBF_SHIFT)
> > +#define XCSI_IER_STOP_MASK BIT(XCSI_IER_STOP_SHIFT)
> > +#define XCSI_IER_SOTERR_MASK BIT(XCSI_IER_SOTERR_SHIFT)
> > +#define XCSI_IER_SOTSYNCERR_MASK BIT(XCSI_IER_SOTSYNCERR_SHIFT)
> > +#define XCSI_IER_ECC2BERR_MASK BIT(XCSI_IER_ECC2BERR_SHIFT)
> > +#define XCSI_IER_ECC1BERR_MASK BIT(XCSI_IER_ECC1BERR_SHIFT)
> > +#define XCSI_IER_CRCERR_MASK BIT(XCSI_IER_CRCERR_SHIFT)
> > +#define XCSI_IER_DATAIDERR_MASK
> BIT(XCSI_IER_DATAIDERR_SHIFT)
> > +#define XCSI_IER_VC3FSYNCERR_MASK
> BIT(XCSI_IER_VC3FSYNCERR_SHIFT)
> > +#define XCSI_IER_VC3FLVLERR_MASK BIT(XCSI_IER_VC3FLVLERR_SHIFT)
> > +#define XCSI_IER_VC2FSYNCERR_MASK
> BIT(XCSI_IER_VC2FSYNCERR_SHIFT)
> > +#define XCSI_IER_VC2FLVLERR_MASK BIT(XCSI_IER_VC2FLVLERR_SHIFT)
> > +#define XCSI_IER_VC1FSYNCERR_MASK
> BIT(XCSI_IER_VC1FSYNCERR_SHIFT)
> > +#define XCSI_IER_VC1FLVLERR_MASK BIT(XCSI_IER_VC1FLVLERR_SHIFT)
> > +#define XCSI_IER_VC0FSYNCERR_MASK
> BIT(XCSI_IER_VC0FSYNCERR_SHIFT)
> > +#define XCSI_IER_VC0FLVLERR_MASK BIT(XCSI_IER_VC0FLVLERR_SHIFT)
> > +#define XCSI_IER_ALLINTR_MASK 0x803FFFFF
> > +
> > +#define XCSI_SPKTR_OFFSET 0x00000030
> > +#define XCSI_SPKTR_DATA_SHIFT 8
> > +#define XCSI_SPKTR_VC_SHIFT 6
> > +#define XCSI_SPKTR_DT_SHIFT 0
> > +#define XCSI_SPKTR_DATA_MASK 0x00FFFF00
> > +#define XCSI_SPKTR_VC_MASK 0x000000C0
> > +#define XCSI_SPKTR_DT_MASK 0x0000003F
> > +
> > +#define XCSI_CLKINFR_OFFSET 0x0000003C
> > +#define XCSI_CLKINFR_STOP_SHIFT 1
> > +#define XCSI_CLKINFR_STOP_MASK BIT(XCSI_CLKINFR_STOP_SHIFT)
> > +
> > +#define XCSI_L0INFR_OFFSET 0x00000040
> > +#define XCSI_L1INFR_OFFSET 0x00000044
> > +#define XCSI_L2INFR_OFFSET 0x00000048
> > +#define XCSI_L3INFR_OFFSET 0x0000004C
> > +#define XCSI_LXINFR_STOP_SHIFT 5
> > +#define XCSI_LXINFR_SOTERR_SHIFT 1
> > +#define XCSI_LXINFR_SOTSYNCERR_SHIFT 0
> > +#define XCSI_LXINFR_STOP_MASK BIT(XCSI_LXINFR_STOP_SHIFT)
> > +#define XCSI_LXINFR_SOTERR_MASK
> BIT(XCSI_LXINFR_SOTERR_SHIFT)
> > +#define XCSI_LXINFR_SOTSYNCERR_MASK
> BIT(XCSI_LXINFR_SOTSYNCERR_SHIFT)
> > +
> > +#define XCSI_VC0INF1R_OFFSET 0x00000060
> > +#define XCSI_VC1INF1R_OFFSET 0x00000068
> > +#define XCSI_VC2INF1R_OFFSET 0x00000070
> > +#define XCSI_VC3INF1R_OFFSET 0x00000078
> > +#define XCSI_VCXINF1R_LINECOUNT_SHIFT 16
> > +#define XCSI_VCXINF1R_BYTECOUNT_SHIFT 0
> > +#define XCSI_VCXINF1R_LINECOUNT_MASK 0xFFFF0000
> > +#define XCSI_VCXINF1R_BYTECOUNT_MASK 0x0000FFFF
> > +
> > +#define XCSI_VC0INF2R_OFFSET 0x00000064
> > +#define XCSI_VC1INF2R_OFFSET 0x0000006C
> > +#define XCSI_VC2INF2R_OFFSET 0x00000074
> > +#define XCSI_VC3INF2R_OFFSET 0x0000007C
> > +#define XCSI_VCXINF2R_DATATYPE_SHIFT 0
> > +#define XCSI_VCXINF2R_DATATYPE_MASK 0x0000003F
> > +
> > +#define XDPHY_CTRLREG_OFFSET 0x0
> > +#define XDPHY_CTRLREG_DPHYEN_SHIFT 1
> > +#define XDPHY_CTRLREG_DPHYEN_MASK
> BIT(XDPHY_CTRLREG_DPHYEN_SHIFT)
> > +
> > +#define XDPHY_CLKSTATREG_OFFSET 0x18
> > +#define XDPHY_CLKSTATREG_MODE_SHIFT 0
> > +#define XDPHY_CLKSTATREG_MODE_MASK 0x3
> > +#define XDPHY_LOW_POWER_MODE 0x0
> > +#define XDPHY_HI_SPEED_MODE 0x1
> > +#define XDPHY_ESC_MODE 0x2
> > +
> > +/*
> > + * Interrupt mask
> > + */
> > +#define XCSI_INTR_MASK (XCSI_ISR_ALLINTR_MASK &
> ~XCSI_ISR_STOP_MASK)
> > +/*
> > + * Timeout for reset
> > + */
> > +#define XCSI_TIMEOUT_VAL (1000) /* us */
>
> No need for parentheses here or below.

Ok. Will remove these in next version.

>
> > +
> > +/*
> > + * Max string length for CSI Data type string
> > + */
> > +#define MAX_XIL_CSIDT_STR_LENGTH 64
> > +
> > +/*
> > + * Maximum number of short packet events per file handle.
> > + */
> > +#define XCSI_MAX_SPKT (512)
> > +
> > +/* Number of media pads */
> > +#define XILINX_CSI_MEDIA_PADS (2)
> > +
> > +#define XCSI_DEFAULT_WIDTH (1920)
> > +#define XCSI_DEFAULT_HEIGHT (1080)
> > +
> > +/*
> > + * Macro to return "true" or "false" string if bit is set
> > + */
> > +#define XCSI_GET_BITSET_STR(val, mask) (val) & (mask) ? "true" : "false"
> > +
> > +enum csi_datatypes {
> > + MIPI_CSI_DT_FRAME_START_CODE = 0x00,
> > + MIPI_CSI_DT_FRAME_END_CODE,
> > + MIPI_CSI_DT_LINE_START_CODE,
> > + MIPI_CSI_DT_LINE_END_CODE,
> > + MIPI_CSI_DT_SYNC_RSVD_04,
> > + MIPI_CSI_DT_SYNC_RSVD_05,
> > + MIPI_CSI_DT_SYNC_RSVD_06,
> > + MIPI_CSI_DT_SYNC_RSVD_07,
> > + MIPI_CSI_DT_GSPKT_08,
> > + MIPI_CSI_DT_GSPKT_09,
> > + MIPI_CSI_DT_GSPKT_0A,
> > + MIPI_CSI_DT_GSPKT_0B,
> > + MIPI_CSI_DT_GSPKT_0C,
> > + MIPI_CSI_DT_GSPKT_0D,
> > + MIPI_CSI_DT_GSPKT_0E,
> > + MIPI_CSI_DT_GSPKT_0F,
> > + MIPI_CSI_DT_GLPKT_10,
> > + MIPI_CSI_DT_GLPKT_11,
> > + MIPI_CSI_DT_GLPKT_12,
> > + MIPI_CSI_DT_GLPKT_13,
> > + MIPI_CSI_DT_GLPKT_14,
> > + MIPI_CSI_DT_GLPKT_15,
> > + MIPI_CSI_DT_GLPKT_16,
> > + MIPI_CSI_DT_GLPKT_17,
> > + MIPI_CSI_DT_YUV_420_8B,
> > + MIPI_CSI_DT_YUV_420_10B,
> > + MIPI_CSI_DT_YUV_420_8B_LEGACY,
> > + MIPI_CSI_DT_YUV_RSVD,
> > + MIPI_CSI_DT_YUV_420_8B_CSPS,
> > + MIPI_CSI_DT_YUV_420_10B_CSPS,
> > + MIPI_CSI_DT_YUV_422_8B,
> > + MIPI_CSI_DT_YUV_422_10B,
> > + MIPI_CSI_DT_RGB_444,
> > + MIPI_CSI_DT_RGB_555,
> > + MIPI_CSI_DT_RGB_565,
> > + MIPI_CSI_DT_RGB_666,
> > + MIPI_CSI_DT_RGB_888,
> > + MIPI_CSI_DT_RGB_RSVD_25,
> > + MIPI_CSI_DT_RGB_RSVD_26,
> > + MIPI_CSI_DT_RGB_RSVD_27,
> > + MIPI_CSI_DT_RAW_6,
> > + MIPI_CSI_DT_RAW_7,
> > + MIPI_CSI_DT_RAW_8,
> > + MIPI_CSI_DT_RAW_10,
> > + MIPI_CSI_DT_RAW_12,
> > + MIPI_CSI_DT_RAW_14,
> > + MIPI_CSI_DT_RAW_RSVD_2E,
> > + MIPI_CSI_DT_RAW_RSVD_2F,
> > + MIPI_CSI_DT_USER_30,
> > + MIPI_CSI_DT_USER_31,
> > + MIPI_CSI_DT_USER_32,
> > + MIPI_CSI_DT_USER_33,
> > + MIPI_CSI_DT_USER_34,
> > + MIPI_CSI_DT_USER_35,
> > + MIPI_CSI_DT_USER_36,
> > + MIPI_CSI_DT_USER_37,
> > + MIPI_CSI_DT_RSVD_38,
> > + MIPI_CSI_DT_RSVD_39,
> > + MIPI_CSI_DT_RSVD_3A,
> > + MIPI_CSI_DT_RSVD_3B,
> > + MIPI_CSI_DT_RSVD_3C,
> > + MIPI_CSI_DT_RSVD_3D,
> > + MIPI_CSI_DT_RSVD_3E,
> > + MIPI_CSI_DT_RSVD_3F
> > +};
> > +
> > +/**
> > + * struct pixel_format - Data Type to string name structure
> > + * @pixelformat: MIPI CSI2 Data type
> > + * @pixelformatstr: String name of Data Type
> > + */
> > +struct pixel_format {
> > + enum csi_datatypes pixelformat;
> > + char pixelformatstr[MAX_XIL_CSIDT_STR_LENGTH];
> > +};
> > +
> > +/**
> > + * struct xcsi2rxss_event - Event log structure
> > + * @mask: Event mask
> > + * @name: Name of the event
> > + * @counter: Count number of events
> > + */
> > +struct xcsi2rxss_event {
> > + u32 mask;
> > + const char * const name;
> > + unsigned int counter;
> > +};
> > +
> > +/*
> > + * struct xcsi2rxss_core - Core configuration CSI2 Rx Subsystem device
> structure
> > + * @dev: Platform structure
> > + * @iomem: Base address of subsystem
> > + * @irq: requested irq number
> > + * @dphy_present: Flag for DPHY register interface presence
> > + * @dphy_offset: DPHY registers offset
>
> Is the D-PHY a part of the same device, or is it effectively a separate
> device? Maxime has posted a patchset for PHY control so this could be a
> candidate user for that:
>
> <URL:https://lkml.org/lkml/2019/1/9/224>
>

The DPHY is part of the same device. But to save resource count on FPGA, its values may be fixed and
Register interface disabled.

> > + * @enable_active_lanes: If number of active lanes can be modified
> > + * @max_num_lanes: Maximum number of lanes present
> > + * @vfb: Video Format Bridge enabled or not
> > + * @ppc: pixels per clock
> > + * @vc: Virtual Channel
> > + * @axis_tdata_width: AXI Stream data width
> > + * @datatype: Data type filter
> > + * @pxlformat: String with CSI pixel format from IP
> > + * @num_lanes: Number of lanes requested from application
> > + * @events: Structure to maintain event logs
> > + */
> > +struct xcsi2rxss_core {
> > + struct device *dev;
> > + void __iomem *iomem;
> > + int irq;
> > + u32 dphy_offset;
> > + bool dphy_present;
> > + bool enable_active_lanes;
> > + u32 max_num_lanes;
> > + bool vfb;
> > + u32 ppc;
> > + u32 vc;
> > + u32 axis_tdata_width;
> > + u32 datatype;
> > + const char *pxlformat;
> > + u32 num_lanes;
> > + struct xcsi2rxss_event *events;
> > +};
> > +
> > +/**
> > + * struct xcsi2rxss_state - CSI2 Rx Subsystem device structure
> > + * @core: Core structure for MIPI CSI2 Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @formats: Active V4L2 formats on each pad
> > + * @default_format: default V4L2 media bus format
> > + * @vip_format: format information corresponding to the active format
> > + * @event: Holds the short packet event
> > + * @lock: mutex for serializing operations
> > + * @pads: media pads
> > + * @npads: number of pads
> > + * @streaming: Flag for storing streaming state
> > + * @suspended: Flag for storing suspended state
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xcsi2rxss_state {
> > + struct xcsi2rxss_core core;
> > + struct v4l2_subdev subdev;
> > + struct v4l2_ctrl_handler ctrl_handler;
> > + struct v4l2_mbus_framefmt formats[2];
> > + struct v4l2_mbus_framefmt default_format;
> > + const struct xvip_video_format *vip_format;
> > + struct v4l2_event event;
> > + /*
> > + * Used to serialize access.
> > + */
>
> This comment seems redundant.

Ok I will update this in next version to say that it serializes access to different v4l subdev functions.

>
> > + struct mutex lock;
> > + struct media_pad pads[XILINX_CSI_MEDIA_PADS];
> > + unsigned int npads;
> > + bool streaming;
> > + bool suspended;
> > +};
> > +
> > +static inline struct xcsi2rxss_state *
> > +to_xcsi2rxssstate(struct v4l2_subdev *subdev)
> > +{
> > + return container_of(subdev, struct xcsi2rxss_state, subdev);
> > +}
> > +
> > +/*
> > + * Register related operations
> > + */
> > +static inline u32 xcsi2rxss_read(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr)
> > +{
> > + return ioread32(xcsi2rxss->iomem + addr);
> > +}
> > +
> > +static inline void xcsi2rxss_write(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr, u32 value)
> > +{
> > + iowrite32(value, xcsi2rxss->iomem + addr);
> > +}
> > +
> > +static inline void xcsi2rxss_clr(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr, u32 clr)
> > +{
> > + xcsi2rxss_write(xcsi2rxss,
> > + addr,
>
> Fits on previous line.

Ok. I will move this and other instances to previous line in next version.
I added like this as it makes it clear to count the number of arguments passed to the function.

>
> > + xcsi2rxss_read(xcsi2rxss, addr) & ~clr);
> > +}
> > +
> > +static inline void xcsi2rxss_set(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr, u32 set)
> > +{
> > + xcsi2rxss_write(xcsi2rxss,
> > + addr,
>
> Ditto.
>
> > + xcsi2rxss_read(xcsi2rxss, addr) | set);
> > +}
> > +
> > +static const struct pixel_format pixel_formats[] = {
> > + { MIPI_CSI_DT_YUV_420_8B, "YUV420_8bit" },
> > + { MIPI_CSI_DT_YUV_420_10B, "YUV420_10bit" },
> > + { MIPI_CSI_DT_YUV_420_8B_LEGACY, "Legacy_YUV420_8bit" },
> > + { MIPI_CSI_DT_YUV_420_8B_CSPS, "YUV420_8bit_CSPS" },
> > + { MIPI_CSI_DT_YUV_420_10B_CSPS, "YUV420_10bit_CSPS" },
> > + { MIPI_CSI_DT_YUV_422_8B, "YUV422_8bit" },
> > + { MIPI_CSI_DT_YUV_422_10B, "YUV422_10bit" },
> > + { MIPI_CSI_DT_RGB_444, "RGB444" },
> > + { MIPI_CSI_DT_RGB_555, "RGB555" },
> > + { MIPI_CSI_DT_RGB_565, "RGB565" },
> > + { MIPI_CSI_DT_RGB_666, "RGB666" },
> > + { MIPI_CSI_DT_RGB_888, "RGB888" },
> > + { MIPI_CSI_DT_RAW_6, "RAW6" },
> > + { MIPI_CSI_DT_RAW_7, "RAW7" },
> > + { MIPI_CSI_DT_RAW_8, "RAW8" },
> > + { MIPI_CSI_DT_RAW_10, "RAW10" },
> > + { MIPI_CSI_DT_RAW_12, "RAW12" },
> > + { MIPI_CSI_DT_RAW_14, "RAW14 "}
> > +};
> > +
> > +static struct xcsi2rxss_event xcsi2rxss_events[] = {
> > + { XCSI_ISR_FR_MASK, "Frame Received", 0 },
> > + { XCSI_ISR_ILC_MASK, "Invalid Lane Count Error", 0 },
> > + { XCSI_ISR_SPFIFOF_MASK, "Short Packet FIFO OverFlow Error", 0 },
> > + { XCSI_ISR_SPFIFONE_MASK, "Short Packet FIFO Not Empty", 0 },
> > + { XCSI_ISR_SLBF_MASK, "Streamline Buffer Full Error", 0 },
> > + { XCSI_ISR_STOP_MASK, "Lane Stop State", 0 },
> > + { XCSI_ISR_SOTERR_MASK, "SOT Error", 0 },
> > + { XCSI_ISR_SOTSYNCERR_MASK, "SOT Sync Error", 0 },
> > + { XCSI_ISR_ECC2BERR_MASK, "2 Bit ECC Unrecoverable Error", 0 },
> > + { XCSI_ISR_ECC1BERR_MASK, "1 Bit ECC Recoverable Error", 0 },
> > + { XCSI_ISR_CRCERR_MASK, "CRC Error", 0 },
> > + { XCSI_ISR_DATAIDERR_MASK, "Data Id Error", 0 },
> > + { XCSI_ISR_VC3FSYNCERR_MASK, "Virtual Channel 3 Frame Sync Error",
> 0 },
> > + { XCSI_ISR_VC3FLVLERR_MASK, "Virtual Channel 3 Frame Level Error", 0
> },
> > + { XCSI_ISR_VC2FSYNCERR_MASK, "Virtual Channel 2 Frame Sync Error",
> 0 },
> > + { XCSI_ISR_VC2FLVLERR_MASK, "Virtual Channel 2 Frame Level Error", 0
> },
> > + { XCSI_ISR_VC1FSYNCERR_MASK, "Virtual Channel 1 Frame Sync Error",
> 0 },
> > + { XCSI_ISR_VC1FLVLERR_MASK, "Virtual Channel 1 Frame Level Error", 0
> },
> > + { XCSI_ISR_VC0FSYNCERR_MASK, "Virtual Channel 0 Frame Sync Error",
> 0 },
> > + { XCSI_ISR_VC0FLVLERR_MASK, "Virtual Channel 0 Frame Level Error", 0
> }
> > +};
> > +
> > +#define XMIPICSISS_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events)
> > +
> > +/**
> > + * xcsi2rxss_clr_and_set - Clear and set the register with a bitmask
> > + * @xcsi2rxss: Xilinx MIPI CSI2 Rx Subsystem subdev core struct
> > + * @addr: address of register
> > + * @clr: bitmask to be cleared
> > + * @set: bitmask to be set
> > + *
> > + * Clear a bit(s) of mask @clr in the register at address @addr, then set
> > + * a bit(s) of mask @set in the register after.
> > + */
> > +static void xcsi2rxss_clr_and_set(struct xcsi2rxss_core *xcsi2rxss,
> > + u32 addr, u32 clr, u32 set)
> > +{
> > + u32 reg;
> > +
> > + reg = xcsi2rxss_read(xcsi2rxss, addr);
> > + reg &= ~clr;
> > + reg |= set;
> > + xcsi2rxss_write(xcsi2rxss, addr, reg);
> > +}
> > +
> > +/**
> > + * xcsi2rxss_pxlfmtstrtodt - Convert pixel format string got from dts
> > + * to data type.
> > + * @pxlfmtstr: String obtained while parsing device node
> > + *
> > + * This function takes a CSI pixel format string obtained while parsing
> > + * device tree node and converts it to data type.
> > + *
> > + * Eg. "RAW8" string is converted to 0x2A.
> > + * Refer to MIPI CSI2 specification for details.
> > + *
> > + * Return: Equivalent pixel format value from table
> > + */
> > +static u32 xcsi2rxss_pxlfmtstrtodt(const char *pxlfmtstr)
> > +{
> > + u32 i;
> > + u32 maxentries = ARRAY_SIZE(pixel_formats);
> > +
> > + for (i = 0; i < maxentries; i++) {
> > + if (!strncmp(pixel_formats[i].pixelformatstr,
> > + pxlfmtstr, MAX_XIL_CSIDT_STR_LENGTH))
> > + return pixel_formats[i].pixelformat;
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_pxlfmtdttostr - Convert pixel format data type to string.
> > + * @datatype: MIPI CSI-2 Data Type
> > + *
> > + * This function takes a CSI pixel format data type and returns a
> > + * pointer to the string name.
> > + *
> > + * Eg. 0x2A returns "RAW8" string.
> > + * Refer to MIPI CSI2 specification for details.
> > + *
> > + * Return: Equivalent pixel format string from table
> > + */
> > +static const char *xcsi2rxss_pxlfmtdttostr(u32 datatype)
> > +{
> > + u32 i;
> > + u32 maxentries = ARRAY_SIZE(pixel_formats);
> > +
> > + for (i = 0; i < maxentries; i++) {
> > + if (pixel_formats[i].pixelformat == datatype)
> > + return pixel_formats[i].pixelformatstr;
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_enable - Enable or disable the CSI Core
> > + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> > + * @flag: true for enabling, false for disabling
> > + *
> > + * This function enables/disables the MIPI CSI2 Rx Subsystem core.
> > + * After enabling the CSI2 Rx core, the DPHY is enabled in case the
> > + * register interface for it is present.
> > + */
> > +static void xcsi2rxss_enable(struct xcsi2rxss_core *core, bool flag)
> > +{
> > + u32 dphyctrlregoffset = core->dphy_offset + XDPHY_CTRLREG_OFFSET;
> > +
> > + if (flag) {
> > + xcsi2rxss_write(core, XCSI_CCR_OFFSET,
> XCSI_CCR_COREENB_MASK);
> > + if (core->dphy_present)
> > + xcsi2rxss_write(core, dphyctrlregoffset,
> > + XDPHY_CTRLREG_DPHYEN_MASK);
> > + } else {
> > + xcsi2rxss_write(core, XCSI_CCR_OFFSET, 0);
> > + if (core->dphy_present)
> > + xcsi2rxss_write(core, dphyctrlregoffset, 0);
> > + }
> > +}
> > +
> > +/**
> > + * xcsi2rxss_interrupts_enable - Enable or disable CSI interrupts
> > + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> > + * @flag: true for enabling, false for disabling
> > + *
> > + * This function enables/disables the interrupts for the MIPI CSI2
> > + * Rx Subsystem.
> > + */
> > +static void xcsi2rxss_interrupts_enable(struct xcsi2rxss_core *core, bool
> flag)
> > +{
> > + if (flag) {
> > + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> > + xcsi2rxss_write(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
> > + xcsi2rxss_set(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> > + } else {
> > + xcsi2rxss_clr(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
> > + xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
> > + }
> > +}
> > +
> > +/**
> > + * xcsi2rxss_reset - Does a soft reset of the MIPI CSI2 Rx Subsystem
> > + * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
> > + *
> > + * Return: 0 - on success OR -ETIME if reset times out
> > + */
> > +static int xcsi2rxss_reset(struct xcsi2rxss_core *core)
> > +{
> > + u32 timeout = XCSI_TIMEOUT_VAL;
> > +
> > + xcsi2rxss_set(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
> > +
> > + while (xcsi2rxss_read(core, XCSI_CSR_OFFSET) &
> XCSI_CSR_RIPCD_MASK) {
> > + if (timeout == 0) {
> > + dev_err(core->dev, "Xilinx CSI2 Rx Subsystem Soft Reset
> Timeout!\n");
> > + return -ETIME;
> > + }
> > +
> > + timeout--;
> > + udelay(1);
> > + }
> > +
> > + xcsi2rxss_clr(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
> > + return 0;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_irq_handler - Interrupt handler for CSI-2
> > + * @irq: IRQ number
> > + * @dev_id: Pointer to device state
> > + *
> > + * In the interrupt handler, a list of event counters are updated for
> > + * corresponding interrupts. This is useful to get status / debug.
> > + * If the short packet FIFO not empty or overflow interrupt is received
> > + * capture the short packet and notify of event occurrence
> > + *
> > + * Return: IRQ_HANDLED after handling interrupts
> > + */
> > +static irqreturn_t xcsi2rxss_irq_handler(int irq, void *dev_id)
> > +{
> > + struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)dev_id;
> > + struct xcsi2rxss_core *core = &state->core;
> > + u32 status;
> > +
> > + status = xcsi2rxss_read(core, XCSI_ISR_OFFSET) & XCSI_INTR_MASK;
> > + dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> > +
> > + if (!status)
> > + return IRQ_NONE;
> > +
> > + if (status & XCSI_ISR_SPFIFONE_MASK) {
> > + memset(&state->event, 0, sizeof(state->event));
> > +
> > + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT;
> > +
> > + *((u32 *)(&state->event.u.data)) =
> > + xcsi2rxss_read(core, XCSI_SPKTR_OFFSET);
> > +
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XCSI_ISR_SPFIFOF_MASK) {
> > + dev_alert(core->dev, "Short packet FIFO overflowed\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > +
> > + state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT_OVF;
> > +
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XCSI_ISR_SLBF_MASK) {
> > + dev_alert(core->dev, "Stream Line Buffer Full!\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > +
> > + state->event.type = V4L2_EVENT_XLNXCSIRX_SLBF;
> > +
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
>
> This is really low level stuff. What would be the purpose of informing the
> user space on individual short or long packets? Other CSI-2 receiver
> drivers don't do it either.
>

Refer to section 9.9 line 867 - 871 in specification for CSI-2 version 2.0 07-Dec-2016 for details.
This is to send Generic Short packets (Data Type 0x08 to 0x0F) containing information like
opening/closing of shutters, triggering flashes, etc to application layer.
Other short packets like Frame Start/end, Line start/end, etc aren't sent to application layer.

> > +
> > + if (status & XCSI_ISR_ALLINTR_MASK) {
> > + unsigned int i;
> > +
> > + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
> > + if (!(status & core->events[i].mask))
> > + continue;
> > + core->events[i].counter++;
> > + dev_dbg(core->dev, "%s: %d\n", core->events[i].name,
> > + core->events[i].counter);
> > + }
> > + }
> > +
> > + xcsi2rxss_write(core, XCSI_ISR_OFFSET, status);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state)
> > +{
> > + u32 i;
> > +
> > + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++)
> > + state->core.events[i].counter = 0;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_log_counters - Print out the event counters.
> > + * @state: Pointer to device state
> > + *
> > + */
> > +static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state)
> > +{
> > + u32 i;
> > +
> > + for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
> > + if (state->core.events[i].counter > 0)
> > + v4l2_info(&state->subdev, "%s events: %d\n",
> > + state->core.events[i].name,
> > + state->core.events[i].counter);
> > + }
> > +}
> > +
> > +/**
> > + * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver
> > + * @sd: Pointer to V4L2 subdevice structure
> > + *
> > + * This function prints the current status of Xilinx MIPI CSI-2
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xcsi2rxss_log_status(struct v4l2_subdev *sd)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> > + u32 reg, data, i;
>
> unsigned int is fine for counters unless you specifically need a type such
> as u32.
>

Ok. Will update in next version.

> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + xcsi2rxss_log_counters(xcsi2rxss);
> > +
> > + v4l2_info(sd, "***** Core Status *****\n");
>
> Please use either v4l2_*() or dev_*() macros to print stuff but not both.

Ok. I will update it in next version.

>
> > + data = xcsi2rxss_read(core, XCSI_CSR_OFFSET);
> > + v4l2_info(sd, "Short Packet FIFO Full = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFOFULL_MASK));
> > + v4l2_info(sd, "Short Packet FIFO Not Empty = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFONE_MASK));
> > + v4l2_info(sd, "Stream line buffer full = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CSR_SLBF_MASK));
> > + v4l2_info(sd, "Soft reset/Core disable in progress = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CSR_RIPCD_MASK));
> > +
> > + /* Clk & Lane Info */
> > + v4l2_info(sd, "******** Clock Lane Info *********\n");
> > + data = xcsi2rxss_read(core, XCSI_CLKINFR_OFFSET);
> > + v4l2_info(sd, "Clock Lane in Stop State = %s\n",
> > + XCSI_GET_BITSET_STR(data, XCSI_CLKINFR_STOP_MASK));
> > +
> > + v4l2_info(sd, "******** Data Lane Info *********\n");
> > + v4l2_info(sd, "Lane\tSoT Error\tSoT Sync Error\tStop State\n");
> > + reg = XCSI_L0INFR_OFFSET;
> > + for (i = 0; i < 4; i++) {
>
> It'd be nice to have a humna-readable name for 4?

Yes. It was the number of Lanes. I will add a macro for it.

>
> > + data = xcsi2rxss_read(core, reg);
> > +
> > + v4l2_info(sd, "%d\t%s\t\t%s\t\t%s\n",
> > + i,
> > + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_SOTERR_MASK),
> > + data & XCSI_LXINFR_SOTSYNCERR_MASK ? "true" : "false",
> > + XCSI_GET_BITSET_STR(data, XCSI_LXINFR_STOP_MASK));
> > +
> > + reg += 4;
> > + }
> > +
> > + /* Virtual Channel Image Information */
> > + v4l2_info(sd, "********** Virtual Channel Info ************\n");
> > + v4l2_info(sd, "VC\tLine Count\tByte Count\tData Type\n");
> > + reg = XCSI_VC0INF1R_OFFSET;
> > + for (i = 0; i < 4; i++) {
> > + u32 line_count, byte_count, data_type;
> > + char *datatypestr;
> > +
> > + /* Get line and byte count from VCXINFR1 Register */
> > + data = xcsi2rxss_read(core, reg);
> > + byte_count = (data & XCSI_VCXINF1R_BYTECOUNT_MASK) >>
> > + XCSI_VCXINF1R_BYTECOUNT_SHIFT;
> > + line_count = (data & XCSI_VCXINF1R_LINECOUNT_MASK) >>
> > + XCSI_VCXINF1R_LINECOUNT_SHIFT;
> > +
> > + /* Get data type from VCXINFR2 Register */
> > + reg += 4;
> > + data = xcsi2rxss_read(core, reg);
> > + data_type = (data & XCSI_VCXINF2R_DATATYPE_MASK) >>
> > + XCSI_VCXINF2R_DATATYPE_SHIFT;
> > + datatypestr = (char *)xcsi2rxss_pxlfmtdttostr(data_type);
> > +
> > + v4l2_info(sd, "%d\t%d\t\t%d\t\t%s\n",
> > + i, line_count, byte_count, datatypestr);
> > +
> > + /* Move to next pair of VC Info registers */
> > + reg += 4;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * xcsi2rxss_subscribe_event - Subscribe to the custom short packet
> > + * receive event.
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 File Handle
> > + * @sub: Subcribe event structure
> > + *
> > + * There are two types of events to be subscribed.
> > + *
> > + * First is to register for receiving a short packet.
> > + * The short packets received are queued up in a FIFO.
> > + * On reception of a short packet, an event will be generated
> > + * with the short packet contents copied to its data area.
> > + * Application subscribed to this event will poll for POLLPRI.
> > + * On getting the event, the app dequeues the event to get the short packet
> > + * data.
> > + *
> > + * Second is to register for Short packet FIFO overflow
> > + * In case the rate of receiving short packets is high and
> > + * the short packet FIFO overflows, this event will be triggered.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xcsi2rxss_subscribe_event(struct v4l2_subdev *sd,
> > + struct v4l2_fh *fh,
> > + struct v4l2_event_subscription *sub)
> > +{
> > + int ret;
>
> Please declare return values, temporary variables etc. as last.

Ok. I will update this and others in next version.

>
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + switch (sub->type) {
> > + case V4L2_EVENT_XLNXCSIRX_SPKT:
> > + case V4L2_EVENT_XLNXCSIRX_SPKT_OVF:
> > + case V4L2_EVENT_XLNXCSIRX_SLBF:
> > + ret = v4l2_event_subscribe(fh, sub, XCSI_MAX_SPKT, NULL);
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_unsubscribe_event - Unsubscribe from all events registered
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 file handle
> > + * @sub: pointer to Event unsubscription structure
> > + *
> > + * Return: zero on success, else a negative error code.
> > + */
> > +static int xcsi2rxss_unsubscribe_event(struct v4l2_subdev *sd,
> > + struct v4l2_fh *fh,
> > + struct v4l2_event_subscription *sub)
> > +{
> > + int ret = 0;
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > + ret = v4l2_event_unsubscribe(fh, sub);
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_s_ctrl - This is used to set the Xilinx MIPI CSI-2 V4L2 controls
> > + * @ctrl: V4L2 control to be set
> > + *
> > + * This function is used to set the V4L2 controls for the Xilinx MIPI
> > + * CSI-2 Rx Subsystem. It is used to set the active lanes in the system.
> > + * The event counters can be reset.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xcsi2rxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + int ret = 0;
> > + u32 timeout = XCSI_TIMEOUT_VAL;
> > + u32 active_lanes = 1;
> > +
> > + struct xcsi2rxss_state *xcsi2rxss =
> > + container_of(ctrl->handler,
> > + struct xcsi2rxss_state, ctrl_handler);
> > + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_MIPICSISS_ACT_LANES:
> > + /*
> > + * This will be called only when "Enable Active Lanes" parameter
> > + * is set in design
> > + */
> > + xcsi2rxss_clr_and_set(core, XCSI_PCR_OFFSET,
> > + XCSI_PCR_ACTLANES_MASK, ctrl->val - 1);
>
> Controls is not the mechanism to choose the number of active lanes --- at
> least not on the receiver side. Please stick to the number of connected
> lanes (data-lanes property); an alternative is to use frame descriptors.
> There's a patchset around; I can find a pointer if you're interested.
>

Ok. Please share the patchset.
This is a run time configurable property.
We can have a design time configuration as 4 lanes but at run time config as 2 lanes depending on what the connected CSI2 Tx device supports.
Please let me know if there is a way to get this info from the upstream driver like camera sensor driver..

> > +
> > + /*
> > + * If the core is enabled, wait for active lanes to be
> > + * set.
> > + *
> > + * If core is disabled or there is no clock from DPHY Tx
> > + * then the read back won't reflect the updated value
> > + * as the PPI clock will not be present.
> > + */
> > +
> > + if (core->dphy_present) {
> > + u32 dphyclkstatregoffset = core->dphy_offset +
> > + XDPHY_CLKSTATREG_OFFSET;
> > +
> > + u32 dphyclkstat =
> > + xcsi2rxss_read(core, dphyclkstatregoffset) &
> > + XDPHY_CLKSTATREG_MODE_MASK;
> > +
> > + u32 coreenable =
> > + xcsi2rxss_read(core, XCSI_CCR_OFFSET) &
> > + XCSI_CCR_COREENB_MASK;
> > +
> > + char lpmstr[] = "Low Power";
> > + char hsmstr[] = "High Speed";
> > + char esmstr[] = "Escape";
> > + char *modestr;
> > +
> > + switch (dphyclkstat) {
> > + case 0:
> > + modestr = lpmstr;
> > + break;
> > + case 1:
> > + modestr = hsmstr;
> > + break;
> > + case 2:
> > + modestr = esmstr;
> > + break;
> > + default:
> > + modestr = NULL;
> > + break;
> > + }
> > +
> > + dev_dbg(core->dev, "DPHY Clock Lane in %s mode\n",
> > + modestr);
> > +
> > + if (dphyclkstat == XDPHY_HI_SPEED_MODE &&
> > + coreenable) {
> > + /* Wait for core to apply new active lanes */
> > + while (timeout--)
> > + udelay(1);
> > +
> > + active_lanes =
> > + xcsi2rxss_read(core, XCSI_PCR_OFFSET);
> > + active_lanes &= XCSI_PCR_ACTLANES_MASK;
> > + active_lanes++;
> > +
> > + if (active_lanes != (u32)ctrl->val) {
> > + dev_err(core->dev, "Failed to set active lanes!\n");
> > + ret = -EAGAIN;
> > + }
> > + }
> > + } else {
> > + dev_dbg(core->dev, "No read back as no DPHY present.\n");
> > + }
> > +
> > + dev_dbg(core->dev, "Set active lanes: requested = %d, active =
> %d\n",
> > + ctrl->val, active_lanes);
> > + break;
> > + case V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS:
> > + xcsi2rxss_reset_event_counters(xcsi2rxss);
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_g_volatile_ctrl - get the Xilinx MIPI CSI-2 Rx controls
> > + * @ctrl: Pointer to V4L2 control
> > + *
> > + * This is used to get the number of frames received by the Xilinx
> > + * MIPI CSI-2 Rx.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xcsi2rxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + int ret = 0;
> > + struct xcsi2rxss_state *xcsi2rxss =
> > + container_of(ctrl->handler,
> > + struct xcsi2rxss_state, ctrl_handler);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER:
> > + ctrl->val = xcsi2rxss->core.events[0].counter;
> > + break;
> > + default:
> > + ret = -EINVAL;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +static int xcsi2rxss_start_stream(struct xcsi2rxss_state *xcsi2rxss)
> > +{
> > + int ret;
> > +
> > + xcsi2rxss_enable(&xcsi2rxss->core, true);
> > +
> > + ret = xcsi2rxss_reset(&xcsi2rxss->core);
> > + if (ret < 0)
> > + return ret;
> > +
> > + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, true);
> > +
> > + return 0;
> > +}
> > +
> > +static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *xcsi2rxss)
> > +{
> > + xcsi2rxss_interrupts_enable(&xcsi2rxss->core, false);
> > + xcsi2rxss_enable(&xcsi2rxss->core, false);
> > +}
> > +
> > +/**
> > + * xcsi2rxss_s_stream - It is used to start/stop the streaming.
> > + * @sd: V4L2 Sub device
> > + * @enable: Flag (True / False)
> > + *
> > + * This function controls the start or stop of streaming for the
> > + * Xilinx MIPI CSI-2 Rx Subsystem provided the device isn't in
> > + * suspended state.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable)
> > +{
> > + int ret = 0;
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + if (xcsi2rxss->suspended) {
> > + ret = -EBUSY;
> > + goto unlock;
> > + }
> > +
> > + if (enable) {
> > + if (!xcsi2rxss->streaming) {
> > + /* reset the event counters */
> > + xcsi2rxss_reset_event_counters(xcsi2rxss);
> > +
> > + ret = xcsi2rxss_start_stream(xcsi2rxss);
> > + if (ret == 0)
> > + xcsi2rxss->streaming = true;
> > + }
> > + } else {
> > + if (xcsi2rxss->streaming) {
> > + xcsi2rxss_stop_stream(xcsi2rxss);
> > + xcsi2rxss->streaming = false;
> > + }
> > + }
> > +unlock:
> > + mutex_unlock(&xcsi2rxss->lock);
> > + return ret;
> > +}
> > +
> > +static struct v4l2_mbus_framefmt *
> > +__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss,
> > + struct v4l2_subdev_pad_config *cfg,
> > + unsigned int pad, u32 which)
> > +{
> > + switch (which) {
> > + case V4L2_SUBDEV_FORMAT_TRY:
> > + return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad);
> > + case V4L2_SUBDEV_FORMAT_ACTIVE:
> > + return &xcsi2rxss->formats[pad];
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +/**
> > + * xcsi2rxss_get_format - Get the pad format
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + * @fmt: Pointer to pad level media bus format
> > + *
> > + * This function is used to get the pad format information.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xcsi2rxss_get_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > + fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
> > + fmt->pad, fmt->which);
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_set_format - This is used to set the pad format
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + * @fmt: Pointer to pad level media bus format
> > + *
> > + * This function is used to set the pad format.
> > + * Since the pad format is fixed in hardware, it can't be
> > + * modified on run time. So when a format set is requested by
> > + * application, all parameters except the format type is
> > + * saved for the pad and the original pad format is sent
> > + * back to the application.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct v4l2_mbus_framefmt *__format;
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> > + u32 code;
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + /*
> > + * Only the format->code parameter matters for CSI as the
> > + * CSI format cannot be changed at runtime.
> > + * Ensure that format to set is copied to over to CSI pad format
> > + */
> > + __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
> > + fmt->pad, fmt->which);
> > +
> > + /* Save the pad format code */
> > + code = __format->code;
> > +
> > + /* If the bayer pattern to be set is SXXXX8 then only 1x8 type
> > + * is supported and core's data type doesn't matter.
> > + * In case the bayer pattern being set is SXXX10 then only
> > + * 1x10 type are supported and core should be configured for RAW10.
> > + * In case the bayer pattern being set is SXXX12 then only
> > + * 1x12 type are supported and core should be configured for RAW12.
> > + *
> > + * Otherwise don't allow change.
> > + */
> > + if ((fmt->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGBRG8_1X8 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGRBG8_1X8 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SRGGB8_1X8) ||
> > + (core->datatype == MIPI_CSI_DT_RAW_10 &&
> > + (fmt->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGBRG10_1X10 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGRBG10_1X10 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SRGGB10_1X10)) ||
> > + (core->datatype == MIPI_CSI_DT_RAW_12 &&
> > + (fmt->format.code == MEDIA_BUS_FMT_SBGGR12_1X12 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGBRG12_1X12 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SGRBG12_1X12 ||
> > + fmt->format.code == MEDIA_BUS_FMT_SRGGB12_1X12))) {
> > + /* Copy over the format to be set */
> > + *__format = fmt->format;
> > + } else {
> > + /* Restore the original pad format code */
> > + fmt->format.code = code;
> > + __format->code = code;
> > + }
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xcsi2rxss_open - Called on v4l2_open()
> > + * @sd: Pointer to V4L2 sub device structure
> > + * @fh: Pointer to V4L2 File handle
> > + *
> > + * This function is called on v4l2_open(). It sets the default format
> > + * for both pads.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xcsi2rxss_open(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_fh *fh)
> > +{
> > + struct v4l2_mbus_framefmt *format;
> > + struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
> > +
> > + format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> > + *format = xcsi2rxss->default_format;
> > +
> > + format = v4l2_subdev_get_try_format(sd, fh->pad, 1);
> > + *format = xcsi2rxss->default_format;
> > +
> > + return 0;
> > +}
> > +
> > +static int xcsi2rxss_close(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_fh *fh)
> > +{
> > + return 0;
> > +}
>
> You can drop the close callback if there's nothing to do there.

Ok. I will remove in next version.

>
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Media Operations
> > + */
> > +
> > +static const struct media_entity_operations xcsi2rxss_media_ops = {
> > + .link_validate = v4l2_subdev_link_validate
> > +};
> > +
> > +static const struct v4l2_ctrl_ops xcsi2rxss_ctrl_ops = {
> > + .g_volatile_ctrl = xcsi2rxss_g_volatile_ctrl,
> > + .s_ctrl = xcsi2rxss_s_ctrl
> > +};
> > +
> > +static struct v4l2_ctrl_config xcsi2rxss_ctrls[] = {
> > + {
> > + .ops = &xcsi2rxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_MIPICSISS_ACT_LANES,
> > + .name = "MIPI CSI2 Rx Subsystem: Active Lanes",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 1,
> > + .max = 4,
> > + .step = 1,
> > + .def = 1,
> > + }, {
> > + .ops = &xcsi2rxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER,
> > + .name = "MIPI CSI2 Rx Subsystem: Frames Received Counter",
> > + .type = V4L2_CTRL_TYPE_INTEGER,
> > + .min = 0,
> > + .max = 0xFFFFFFFF,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > + }, {
> > + .ops = &xcsi2rxss_ctrl_ops,
> > + .id = V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS,
> > + .name = "MIPI CSI2 Rx Subsystem: Reset Counters",
> > + .type = V4L2_CTRL_TYPE_BUTTON,
> > + .min = 0,
> > + .max = 1,
> > + .step = 1,
> > + .def = 0,
> > + .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
> > + }
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = {
> > + .log_status = xcsi2rxss_log_status,
> > + .subscribe_event = xcsi2rxss_subscribe_event,
> > + .unsubscribe_event = xcsi2rxss_unsubscribe_event
> > +};
> > +
> > +static struct v4l2_subdev_video_ops xcsi2rxss_video_ops = {
> > + .s_stream = xcsi2rxss_s_stream
> > +};
> > +
> > +static struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = {
> > + .get_fmt = xcsi2rxss_get_format,
> > + .set_fmt = xcsi2rxss_set_format,
> > +};
> > +
> > +static struct v4l2_subdev_ops xcsi2rxss_ops = {
> > + .core = &xcsi2rxss_core_ops,
> > + .video = &xcsi2rxss_video_ops,
> > + .pad = &xcsi2rxss_pad_ops
> > +};
> > +
> > +static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = {
> > + .open = xcsi2rxss_open,
> > + .close = xcsi2rxss_close
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Power Management
> > + */
> > +
> > +/*
> > + * xcsi2rxss_pm_suspend - Function called on Power Suspend
> > + * @dev: Pointer to device structure
> > + *
> > + * On power suspend the CSI-2 Core is disabled if the device isn't
> > + * in suspended state and is streaming.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int __maybe_unused xcsi2rxss_pm_suspend(struct device *dev)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + if (!xcsi2rxss->suspended && xcsi2rxss->streaming)
> > + xcsi2rxss_clr(&xcsi2rxss->core,
> > + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
> > +
> > + xcsi2rxss->suspended = true;
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * xcsi2rxss_pm_resume - Function called on Power Resume
> > + * @dev: Pointer to device structure
> > + *
> > + * On power resume the CSI-2 Core is enabled when it is in suspended state
> > + * and prior to entering suspended state it was streaming.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int __maybe_unused xcsi2rxss_pm_resume(struct device *dev)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
> > +
> > + mutex_lock(&xcsi2rxss->lock);
> > +
> > + if (xcsi2rxss->suspended && xcsi2rxss->streaming)
> > + xcsi2rxss_set(&xcsi2rxss->core,
> > + XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
> > +
> > + xcsi2rxss->suspended = false;
> > +
> > + mutex_unlock(&xcsi2rxss->lock);
> > + return 0;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss)
> > +{
> > + struct device_node *node = xcsi2rxss->core.dev->of_node;
> > + struct device_node *ports = NULL;
> > + struct device_node *port = NULL;
> > + unsigned int nports = 0;
> > + struct xcsi2rxss_core *core = &xcsi2rxss->core;
> > + int ret;
> > + bool iic_present;
> > +
> > + core->dphy_present = of_property_read_bool(node, "xlnx,dphy-
> present");
> > + dev_dbg(core->dev, "DPHY present property = %s\n",
> > + core->dphy_present ? "Present" : "Absent");
> > +
> > + iic_present = of_property_read_bool(node, "xlnx,iic-present");
> > + dev_dbg(core->dev, "IIC present property = %s\n",
> > + iic_present ? "Present" : "Absent");
> > +
> > + if (core->dphy_present) {
> > + if (iic_present)
> > + core->dphy_offset = 0x20000;
> > + else
> > + core->dphy_offset = 0x10000;
> > + }
> > +
> > + ret = of_property_read_u32(node, "xlnx,max-lanes",
> > + &core->max_num_lanes);
> > + if (ret < 0) {
> > + dev_err(core->dev, "missing xlnx,max-lanes property\n");
> > + return ret;
> > + }
> > +
> > + if (core->max_num_lanes > 4 || core->max_num_lanes < 1) {
> > + dev_err(core->dev, "%d max lanes : invalid xlnx,max-lanes
> property\n",
> > + core->max_num_lanes);
> > + return -EINVAL;
> > + }
> > +
> > + ret = of_property_read_u32(node, "xlnx,vc", &core->vc);
> > + if (ret < 0) {
> > + dev_err(core->dev, "missing xlnx,vc property\n");
> > + return ret;
> > + }
> > + if (core->vc > 4) {
> > + dev_err(core->dev, "invalid virtual channel property value.\n");
> > + return -EINVAL;
> > + }
> > +
> > + core->enable_active_lanes =
> > + of_property_read_bool(node, "xlnx,en-active-lanes");
> > + dev_dbg(core->dev, "Enable active lanes property = %s\n",
> > + core->enable_active_lanes ? "Present" : "Absent");
> > +
> > + ret = of_property_read_string(node, "xlnx,csi-pxl-format",
> > + &core->pxlformat);
> > + if (ret < 0) {
> > + dev_err(core->dev, "missing xlnx,csi-pxl-format property\n");
> > + return ret;
> > + }
> > +
> > + core->datatype = xcsi2rxss_pxlfmtstrtodt(core->pxlformat);
> > + if (core->datatype < MIPI_CSI_DT_YUV_420_8B ||
> > + core->datatype > MIPI_CSI_DT_RAW_14) {
> > + dev_err(core->dev, "Invalid xlnx,csi-pxl-format string\n");
> > + return -EINVAL;
> > + }
> > +
> > + core->vfb = of_property_read_bool(node, "xlnx,vfb");
> > + dev_dbg(core->dev, "Video Format Bridge property = %s\n",
> > + core->vfb ? "Present" : "Absent");
> > +
> > + if (core->vfb) {
> > + ret = of_property_read_u32(node, "xlnx,ppc", &core->ppc);
> > + if (ret < 0 || !(core->ppc == 1 || core->ppc == 2 ||
> > + core->ppc == 4)) {
> > + dev_err(core->dev, "Invalid xlnx,ppc property ret = %d ppc =
> %d\n",
> > + ret, core->ppc);
> > + return -EINVAL;
> > + }
> > + }
> > +
> > + ports = of_get_child_by_name(node, "ports");
> > + if (!ports)
> > + ports = node;
> > +
> > + for_each_child_of_node(ports, port) {
> > + int ret;
> > + const struct xvip_video_format *format;
> > + struct device_node *endpoint;
> > + struct v4l2_fwnode_endpoint v4lendpoint;
> > +
> > + if (!port->name || of_node_cmp(port->name, "port"))
> > + continue;
> > +
> > + /*
> > + * Currently only a subset of VFB enabled formats present in
> > + * xvip are supported in the driver.
> > + *
> > + * If the VFB is disabled, the pixels per clock don't matter.
> > + * The data width is either 32 or 64 bit as selected in design.
> > + *
> > + * For e.g. If Data Type is RGB888, VFB is disabled and
> > + * data width is 32 bits.
> > + *
> > + * Clk Cycle | Byte 0 | Byte 1 | Byte 2 | Byte 3
> > + * -----------+----------+----------+----------+----------
> > + * 1 | B0 | G0 | R0 | B1
> > + * 2 | G1 | R1 | B2 | G2
> > + * 3 | R2 | B3 | G3 | R3
> > + */
> > + format = xvip_of_get_format(port);
> > + if (IS_ERR(format)) {
> > + dev_err(core->dev, "invalid format in DT");
> > + return PTR_ERR(format);
> > + }
> > +
> > + if (core->vfb &&
> > + format->vf_code != XVIP_VF_YUV_422 &&
> > + format->vf_code != XVIP_VF_RBG &&
> > + format->vf_code != XVIP_VF_MONO_SENSOR) {
> > + dev_err(core->dev, "Invalid UG934 video format set.\n");
> > + return -EINVAL;
> > + }
> > +
> > + /* Get and check the format description */
> > + if (!xcsi2rxss->vip_format) {
> > + xcsi2rxss->vip_format = format;
> > + } else if (xcsi2rxss->vip_format != format) {
> > + dev_err(core->dev, "in/out format mismatch in DT");
> > + return -EINVAL;
> > + }
> > +
> > + endpoint = of_get_next_child(port, NULL);
> > + if (!endpoint) {
> > + dev_err(core->dev, "No port at\n");
> > + return -EINVAL;
> > + }
> > +
> > + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
> > + &v4lendpoint);
> > + if (ret) {
> > + of_node_put(endpoint);
> > + return ret;
> > + }
> > +
> > + of_node_put(endpoint);
> > + dev_dbg(core->dev, "%s : port %d bus type = %d\n",
> > + __func__, nports, v4lendpoint.bus_type);
> > +
> > + if (v4lendpoint.bus_type == V4L2_MBUS_CSI2) {
> > + dev_dbg(core->dev, "%s : base.port = %d base.id = %d\n",
> > + __func__, v4lendpoint.base.port,
> > + v4lendpoint.base.id);
> > +
> > + dev_dbg(core->dev, "%s : mipi number lanes = %d\n",
> > + __func__,
> > + v4lendpoint.bus.mipi_csi2.num_data_lanes);
> > + } else {
> > + dev_dbg(core->dev, "%s : Not a CSI2 bus\n", __func__);
> > + }
> > +
> > + /* Count the number of ports. */
> > + nports++;
> > + }
> > +
> > + if (nports != 2) {
> > + dev_err(core->dev, "invalid number of ports %u\n", nports);
> > + return -EINVAL;
> > + }
> > + xcsi2rxss->npads = nports;
> > +
> > + /*Register interrupt handler */
> > + core->irq = irq_of_parse_and_map(node, 0);
> > +
> > + ret = devm_request_irq(core->dev, core->irq, xcsi2rxss_irq_handler,
> > + IRQF_SHARED, "xilinx-csi2rxss", xcsi2rxss);
> > + if (ret) {
> > + dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int xcsi2rxss_probe(struct platform_device *pdev)
> > +{
> > + struct v4l2_subdev *subdev;
> > + struct xcsi2rxss_state *xcsi2rxss;
> > + struct resource *res;
> > +
> > + u32 i;
> > + int ret;
> > + int num_ctrls;
> > +
> > + xcsi2rxss = devm_kzalloc(&pdev->dev, sizeof(*xcsi2rxss), GFP_KERNEL);
> > + if (!xcsi2rxss)
> > + return -ENOMEM;
> > +
> > + mutex_init(&xcsi2rxss->lock);
> > +
> > + xcsi2rxss->core.dev = &pdev->dev;
> > +
> > + ret = xcsi2rxss_parse_of(xcsi2rxss);
> > + if (ret < 0)
> > + return ret;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + xcsi2rxss->core.iomem = devm_ioremap_resource(xcsi2rxss->core.dev,
> res);
> > + if (IS_ERR(xcsi2rxss->core.iomem))
> > + return PTR_ERR(xcsi2rxss->core.iomem);
> > +
> > + /*
> > + * Reset and initialize the core.
> > + */
> > + xcsi2rxss_reset(&xcsi2rxss->core);
> > +
> > + xcsi2rxss->core.events = (struct xcsi2rxss_event *)&xcsi2rxss_events;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + xcsi2rxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> > + xcsi2rxss->pads[1].flags = MEDIA_PAD_FL_SINK;
> > +
> > + /* Initialize the default format */
> > + memset(&xcsi2rxss->default_format, 0,
> > + sizeof(xcsi2rxss->default_format));
> > + xcsi2rxss->default_format.code = xcsi2rxss->vip_format->code;
> > + xcsi2rxss->default_format.field = V4L2_FIELD_NONE;
> > + xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB;
> > + xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH;
> > + xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT;
> > +
> > + xcsi2rxss->formats[0] = xcsi2rxss->default_format;
> > + xcsi2rxss->formats[1] = xcsi2rxss->default_format;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + subdev = &xcsi2rxss->subdev;
> > + v4l2_subdev_init(subdev, &xcsi2rxss_ops);
> > +
> > + subdev->dev = &pdev->dev;
> > + subdev->internal_ops = &xcsi2rxss_internal_ops;
> > + strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
> > +
> > + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS |
> V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +
> > + subdev->entity.ops = &xcsi2rxss_media_ops;
> > +
> > + v4l2_set_subdevdata(subdev, xcsi2rxss);
> > +
> > + ret = media_entity_pads_init(&subdev->entity, 2, xcsi2rxss->pads);
> > + if (ret < 0)
> > + goto error;
> > +
> > + /*
> > + * In case the Enable Active Lanes config parameter is not set,
> > + * dynamic lane reconfiguration is not allowed.
> > + * So V4L2_CID_XILINX_MIPICSISS_ACT_LANES ctrl will not be registered.
> > + * Accordingly allocate the number of controls
> > + */
> > + num_ctrls = ARRAY_SIZE(xcsi2rxss_ctrls);
> > +
> > + if (!xcsi2rxss->core.enable_active_lanes)
> > + num_ctrls--;
> > +
> > + dev_dbg(xcsi2rxss->core.dev, "# of ctrls = %d\n", num_ctrls);
> > +
> > + v4l2_ctrl_handler_init(&xcsi2rxss->ctrl_handler, num_ctrls);
> > +
> > + for (i = 0; i < ARRAY_SIZE(xcsi2rxss_ctrls); i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + if (xcsi2rxss_ctrls[i].id ==
> > + V4L2_CID_XILINX_MIPICSISS_ACT_LANES) {
> > + if (xcsi2rxss->core.enable_active_lanes) {
> > + xcsi2rxss_ctrls[i].max =
> > + xcsi2rxss->core.max_num_lanes;
> > + } else {
> > + /* Don't register control */
> > + dev_dbg(xcsi2rxss->core.dev,
> > + "Skip active lane control\n");
> > + continue;
> > + }
> > + }
> > +
> > + dev_dbg(xcsi2rxss->core.dev, "%d ctrl = 0x%x\n",
> > + i, xcsi2rxss_ctrls[i].id);
> > + ctrl = v4l2_ctrl_new_custom(&xcsi2rxss->ctrl_handler,
> > + &xcsi2rxss_ctrls[i], NULL);
> > + if (!ctrl) {
> > + dev_err(xcsi2rxss->core.dev, "Failed for %s ctrl\n",
> > + xcsi2rxss_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > +
> > + dev_dbg(xcsi2rxss->core.dev, "# v4l2 ctrls registered = %d\n", i - 1);
> > +
> > + if (xcsi2rxss->ctrl_handler.error) {
> > + dev_err(&pdev->dev, "failed to add controls\n");
> > + ret = xcsi2rxss->ctrl_handler.error;
> > + goto error;
> > + }
> > +
> > + subdev->ctrl_handler = &xcsi2rxss->ctrl_handler;
> > +
> > + ret = v4l2_ctrl_handler_setup(&xcsi2rxss->ctrl_handler);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "failed to set controls\n");
> > + goto error;
> > + }
> > +
> > + platform_set_drvdata(pdev, xcsi2rxss);
> > +
> > + dev_info(xcsi2rxss->core.dev, "Xilinx CSI2 Rx Subsystem device
> found!\n");
> > +
> > + ret = v4l2_async_register_subdev(subdev);
> > + if (ret < 0) {
> > + dev_err(&pdev->dev, "failed to register subdev\n");
> > + goto error;
> > + }
> > +
> > + /* default states for streaming and suspend */
> > + xcsi2rxss->streaming = false;
> > + xcsi2rxss->suspended = false;
> > + return 0;
> > +
> > +error:
> > + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + mutex_destroy(&xcsi2rxss->lock);
> > +
> > + return ret;
> > +}
> > +
> > +static int xcsi2rxss_remove(struct platform_device *pdev)
> > +{
> > + struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev);
> > + struct v4l2_subdev *subdev = &xcsi2rxss->subdev;
> > +
> > + v4l2_async_unregister_subdev(subdev);
> > + v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + mutex_destroy(&xcsi2rxss->lock);
> > +
> > + return 0;
> > +}
> > +
> > +static SIMPLE_DEV_PM_OPS(xcsi2rxss_pm_ops,
> > + xcsi2rxss_pm_suspend, xcsi2rxss_pm_resume);
> > +
> > +static const struct of_device_id xcsi2rxss_of_id_table[] = {
> > + { .compatible = "xlnx,mipi-csi2-rx-subsystem-2.0" },
> > + { .compatible = "xlnx,mipi-csi2-rx-subsystem-3.0" },
> > + { }
> > +};
> > +MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table);
> > +
> > +static struct platform_driver xcsi2rxss_driver = {
> > + .driver = {
> > + .name = "xilinx-csi2rxss",
> > + .pm = &xcsi2rxss_pm_ops,
> > + .of_match_table = xcsi2rxss_of_id_table,
> > + },
> > + .probe = xcsi2rxss_probe,
> > + .remove = xcsi2rxss_remove,
> > +};
> > +module_platform_driver(xcsi2rxss_driver);
>
> A newline here would be nice.

Will add a new line here in next version.

Regards
Vishal Sagar

>
> > +MODULE_AUTHOR("Vishal Sagar <[email protected]>");
> > +MODULE_DESCRIPTION("Xilinx MIPI CSI2 Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-csi2rxss.h b/include/uapi/linux/xilinx-
> csi2rxss.h
> > new file mode 100644
> > index 0000000..973d5b46
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-csi2rxss.h
> > @@ -0,0 +1,25 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > +/*
> > + * Events from Xilinx MIPI CSI2 Rx Subsystem exposed to V4L2 application.
> > + *
> > + * Copyright (C) 2016 - 2018 Xilinx, Inc.
> > + * Author: Vishal Sagar <[email protected]>
> > + */
> > +#ifndef __UAPI_XILINX_CSI2RXSS_H__
> > +#define __UAPI_XILINX_CSI2RXSS_H__
> > +
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * Events
> > + *
> > + * V4L2_EVENT_XLNXCSIRX_SPKT: Short packet received
> > + * V4L2_EVENT_XLNXCSIRX_SPKT_OVF: Short packet FIFO overflow
> > + * V4L2_EVENT_XLNXCSIRX_SLBF: Stream line buffer full
> > + */
> > +#define V4L2_EVENT_XLNXCSIRX_CLASS (V4L2_EVENT_PRIVATE_START |
> 0x100)
> > +#define V4L2_EVENT_XLNXCSIRX_SPKT (V4L2_EVENT_XLNXCSIRX_CLASS
> | 0x1)
> > +#define V4L2_EVENT_XLNXCSIRX_SPKT_OVF
> (V4L2_EVENT_XLNXCSIRX_CLASS | 0x2)
> > +#define V4L2_EVENT_XLNXCSIRX_SLBF (V4L2_EVENT_XLNXCSIRX_CLASS |
> 0x3)
> > +
> > +#endif /* __UAPI_XILINX_CSI2RXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index b6441fe..4ca3b44 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -71,4 +71,18 @@
> > /* Noise level */
> > #define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG +
> 17)
> >
> > +/*
> > + * Xilinx MIPI CSI2 Rx Subsystem
> > + */
> > +
> > +/* Base ID */
> > +#define V4L2_CID_XILINX_MIPICSISS (V4L2_CID_USER_BASE +
> 0xc080)
> > +
> > +/* Active Lanes */
> > +#define V4L2_CID_XILINX_MIPICSISS_ACT_LANES
> (V4L2_CID_XILINX_MIPICSISS + 1)
> > +/* Frames received since streaming is set */
> > +#define V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER
> (V4L2_CID_XILINX_MIPICSISS + 2)
> > +/* Reset all event counters */
> > +#define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS
> (V4L2_CID_XILINX_MIPICSISS + 3)
> > +
> > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > --
> > 2.7.4
> >
> > This email and any attachments are intended for the sole use of the named
> recipient(s) and contain(s) confidential information that may be proprietary,
> privileged or copyrighted under applicable law. If you are not the intended
> recipient, do not read, copy, or forward this email message or any attachments.
> Delete this email message and any attachments immediately.
>
> --
> Regards,
>
> Sakari Ailus



2019-01-28 12:00:49

by Sakari Ailus

[permalink] [raw]
Subject: Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem

Hi Vishal,

On Mon, Jan 14, 2019 at 09:47:41AM +0000, Vishal Sagar wrote:
> Hi Sakari,
>
> Thanks for reviewing this.
>
> > -----Original Message-----
> > From: Sakari Ailus [mailto:[email protected]]
> > Sent: Tuesday, January 08, 2019 6:35 PM
> > To: Vishal Sagar <[email protected]>
> > Cc: Hyun Kwon <[email protected]>; [email protected];
> > Michal Simek <[email protected]>; [email protected];
> > [email protected]; [email protected]; [email protected];
> > [email protected]; [email protected]; Dinesh Kumar
> > <[email protected]>; [email protected]; linux-
> > [email protected]
> > Subject: Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2
> > Rx Subsystem
> >
> > EXTERNAL EMAIL
> >
> > Hi Vishal,
> >
> > The patchset hard escaped me somehow earlier and your reply to Rob made me
> > notice it again. Thanks. :-)
> >
> > On Wed, May 30, 2018 at 12:24:43AM +0530, Vishal Sagar wrote:
> > > Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem.
> > >
> > > The Xilinx MIPI CSI-2 Rx Subsystem consists of a DPHY, CSI-2 Rx, an
> > > optional I2C controller and an optional Video Format Bridge (VFB). The
> > > active lanes can be configured at run time if enabled in the IP. The
> > > DPHY register interface may also be enabled.
> > >
> > > Signed-off-by: Vishal Sagar <[email protected]>
> > > ---
> > > .../bindings/media/xilinx/xlnx,csi2rxss.txt | 117
> > +++++++++++++++++++++
> > > 1 file changed, 117 insertions(+)
> > > create mode 100644
> > Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > >
> > > diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > > new file mode 100644
> > > index 0000000..31ed721
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > > @@ -0,0 +1,117 @@
> > > +
> >
> > Extra newline.
> >
>
> Will remove it in next version.
>
> > > +Xilinx MIPI CSI2 Receiver Subsystem Device Tree Bindings
> > > +--------------------------------------------------------
> > > +
> > > +The Xilinx MIPI CSI2 Receiver Subsystem is used to capture MIPI CSI2 traffic
> > > +from compliant camera sensors and send the output as AXI4 Stream video
> > data
> > > +for image processing.
> > > +
> > > +The subsystem consists of a MIPI DPHY in slave mode which captures the
> > > +data packets. This is passed along the MIPI CSI2 Rx IP which extracts the
> > > +packet data. This data is taken in by the Video Format Bridge (VFB),
> > > +if selected, and converted into AXI4 Stream video data at selected
> > > +pixels per clock as per AXI4-Stream Video IP and System Design UG934.
> > > +
> > > +For more details, please refer to PG232 MIPI CSI-2 Receiver Subsystem.
> > >
> > +https://www.xilinx.com/support/documentation/ip_documentation/mipi_csi
> > 2_rx_subsystem/v3_0/pg232-mipi-csi2-rx.pdf
> > > +
> > > +Required properties:
> > > +
> > > +- compatible: Must contain "xlnx,mipi-csi2-rx-subsystem-2.0" or
> > > + "xlnx,mipi-csi2-rx-subsystem-3.0"
> > > +
> > > +- reg: Physical base address and length of the registers set for the device.
> > > +
> > > +- interrupt-parent: specifies the phandle to the parent interrupt controller
> > > +
> > > +- interrupts: Property with a value describing the interrupt number.
> > > +
> > > +- xlnx,max-lanes: Maximum active lanes in the design.
> > > +
> > > +- xlnx,vc: Virtual Channel, specifies virtual channel number to be filtered.
> > > + If this is 4 then all virtual channels are allowed.
> >
> > This seems like something a driver should configure, based on the
> > configuration of the connected device.
> >
>
> The filtering of the Virtual channels is property of the hardware IP and is fixed in design.
> This is not software controlled.

So... you have different IP blocks between which (one of) the difference(s)
is the virtual channel?

>
> > > +
> > > +- xlnx,csi-pxl-format: This denotes the CSI Data type selected in hw design.
> > > + Packets other than this data type (except for RAW8 and User defined data
> > > + types) will be filtered out. Possible values are RAW6, RAW7, RAW8, RAW10,
> > > + RAW12, RAW14, RGB444, RGB555, RGB565, RGB666, RGB888 and
> > YUV4228bit.
> >
> > This should be configured at runtime instead through V4L2 sub-device
> > interface; it's not a property of the hardware.
> >
>
> This too is a property of the hardware IP and is fixed to one data type
> during design to reduce gate count. So for e.g. if RGB888 is selected
> during design, then the hardware will only pass across RGB888 packet data
> to output. (RAW8 packets are also allowed to pass through for all data
> types selected) This is used in the driver to determine the media bus
> format of the connected pads.

If I understand this correctly, RAW8 and user defined data types will
always pass through, plus the other data types listed here. Is that right?

>
> > > +
> > > +- xlnx,axis-tdata-width: AXI Stream width, This denotes the AXI Stream width.
> > > + It depends on Data type chosen, Video Format Bridge enabled/disabled and
> > > + pixels per clock. If VFB is disabled then its value is either 0x20 (32 bit)
> > > + or 0x40(64 bit) width.
> > > +
> > > +- xlnx,video-format, xlnx,video-width: Video format and width, as defined in
> > > + video.txt.
> >
> > Ditto.
> >
> Again these are fixed values and can't be changed at run time.
> These are used to determine the media bus format.

What kind of values can the xlnx,video-format property have? How about
xlnx.video-width? Where can video.txt be found?

>
> > > +
> > > +- port: Video port, using the DT bindings defined in ../video-interfaces.txt.
> > > + The CSI 2 Rx Subsystem has a two ports, one input port for connecting to
> > > + camera sensor and other is output port.
> > > +
> > > +- data-lanes: The number of data lanes through which CSI2 Rx Subsystem is
> > > + connected to the camera sensor as per video-interfaces.txt
> >
> > This is somewhat different from the documentation in video-interfaces.txt.
> > Could you align the two? I don't think there's a need to document standard
> > properties in device binding files elaborately; rather just the hardware
> > specific bits.
> >
>
> Agree. In this current IP there is no way to re-order the lanes which are set at design time.
> So physical and logical lanes are at same index. This could only be used to determine how many lanes are allowed to be programmed.
> For e.g. if design has set the number of lanes as 4 and xlnx,en-active-lanes is present, then the number of lanes
> can be set from 1 to 4.

Ack.

--
Regards,

Sakari Ailus
[email protected]

2019-01-30 05:50:40

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem

Hi Sakari,

> -----Original Message-----
> From: Sakari Ailus [mailto:[email protected]]
> Sent: Monday, January 28, 2019 5:30 PM
> To: Vishal Sagar <[email protected]>
> Cc: Vishal Sagar <[email protected]>; Hyun Kwon <[email protected]>;
> [email protected]; Michal Simek <[email protected]>;
> [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; Dinesh Kumar <[email protected]>; linux-arm-
> [email protected]; [email protected]
> Subject: Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2
> Rx Subsystem
>
> Hi Vishal,
>
> On Mon, Jan 14, 2019 at 09:47:41AM +0000, Vishal Sagar wrote:
> > Hi Sakari,
> >
> > Thanks for reviewing this.
> >
> > > -----Original Message-----
> > > From: Sakari Ailus [mailto:[email protected]]
> > > Sent: Tuesday, January 08, 2019 6:35 PM
> > > To: Vishal Sagar <[email protected]>
> > > Cc: Hyun Kwon <[email protected]>; [email protected];
> > > Michal Simek <[email protected]>; [email protected];
> > > [email protected]; [email protected]; [email protected];
> > > [email protected]; [email protected]; Dinesh Kumar
> > > <[email protected]>; [email protected]; linux-
> > > [email protected]
> > > Subject: Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI
> CSI-2
> > > Rx Subsystem
> > >
> > > EXTERNAL EMAIL
> > >
> > > Hi Vishal,
> > >
> > > The patchset hard escaped me somehow earlier and your reply to Rob made
> me
> > > notice it again. Thanks. :-)
> > >
> > > On Wed, May 30, 2018 at 12:24:43AM +0530, Vishal Sagar wrote:
> > > > Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem.
> > > >
> > > > The Xilinx MIPI CSI-2 Rx Subsystem consists of a DPHY, CSI-2 Rx, an
> > > > optional I2C controller and an optional Video Format Bridge (VFB). The
> > > > active lanes can be configured at run time if enabled in the IP. The
> > > > DPHY register interface may also be enabled.
> > > >
> > > > Signed-off-by: Vishal Sagar <[email protected]>
> > > > ---
> > > > .../bindings/media/xilinx/xlnx,csi2rxss.txt | 117
> > > +++++++++++++++++++++
> > > > 1 file changed, 117 insertions(+)
> > > > create mode 100644
> > > Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > > >
> > > > diff --git
> a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > > b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > > > new file mode 100644
> > > > index 0000000..31ed721
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
> > > > @@ -0,0 +1,117 @@
> > > > +
> > >
> > > Extra newline.
> > >
> >
> > Will remove it in next version.
> >
> > > > +Xilinx MIPI CSI2 Receiver Subsystem Device Tree Bindings
> > > > +--------------------------------------------------------
> > > > +
> > > > +The Xilinx MIPI CSI2 Receiver Subsystem is used to capture MIPI CSI2
> traffic
> > > > +from compliant camera sensors and send the output as AXI4 Stream
> video
> > > data
> > > > +for image processing.
> > > > +
> > > > +The subsystem consists of a MIPI DPHY in slave mode which captures the
> > > > +data packets. This is passed along the MIPI CSI2 Rx IP which extracts the
> > > > +packet data. This data is taken in by the Video Format Bridge (VFB),
> > > > +if selected, and converted into AXI4 Stream video data at selected
> > > > +pixels per clock as per AXI4-Stream Video IP and System Design UG934.
> > > > +
> > > > +For more details, please refer to PG232 MIPI CSI-2 Receiver Subsystem.
> > > >
> > >
> +https://www.xilinx.com/support/documentation/ip_documentation/mipi_csi
> > > 2_rx_subsystem/v3_0/pg232-mipi-csi2-rx.pdf
> > > > +
> > > > +Required properties:
> > > > +
> > > > +- compatible: Must contain "xlnx,mipi-csi2-rx-subsystem-2.0" or
> > > > + "xlnx,mipi-csi2-rx-subsystem-3.0"
> > > > +
> > > > +- reg: Physical base address and length of the registers set for the device.
> > > > +
> > > > +- interrupt-parent: specifies the phandle to the parent interrupt
> controller
> > > > +
> > > > +- interrupts: Property with a value describing the interrupt number.
> > > > +
> > > > +- xlnx,max-lanes: Maximum active lanes in the design.
> > > > +
> > > > +- xlnx,vc: Virtual Channel, specifies virtual channel number to be filtered.
> > > > + If this is 4 then all virtual channels are allowed.
> > >
> > > This seems like something a driver should configure, based on the
> > > configuration of the connected device.
> > >
> >
> > The filtering of the Virtual channels is property of the hardware IP and is fixed
> in design.
> > This is not software controlled.
>
> So... you have different IP blocks between which (one of) the difference(s)
> is the virtual channel?
>

Your understanding is correct.

The Xilinx CSI2 Rx subsystem has the 3 blocks -
1 - Xilinx CSI2 Rx controller
2 - Xilinx DPHY in Rx mode (whose register interface may be disabled/fixed configuration to reduce logic gate count).
3 - Xilinx I2C controller (used as CCI - camera control interface)

The virtual channel filtering is a property of the CSI2 Rx controller.
This was present in v1 as I wanted the IP configuration to be available for debug.
This has been removed in v2 as it is not really used in the driver.

> >
> > > > +
> > > > +- xlnx,csi-pxl-format: This denotes the CSI Data type selected in hw
> design.
> > > > + Packets other than this data type (except for RAW8 and User defined
> data
> > > > + types) will be filtered out. Possible values are RAW6, RAW7, RAW8,
> RAW10,
> > > > + RAW12, RAW14, RGB444, RGB555, RGB565, RGB666, RGB888 and
> > > YUV4228bit.
> > >
> > > This should be configured at runtime instead through V4L2 sub-device
> > > interface; it's not a property of the hardware.
> > >
> >
> > This too is a property of the hardware IP and is fixed to one data type
> > during design to reduce gate count. So for e.g. if RGB888 is selected
> > during design, then the hardware will only pass across RGB888 packet data
> > to output. (RAW8 packets are also allowed to pass through for all data
> > types selected) This is used in the driver to determine the media bus
> > format of the connected pads.
>
> If I understand this correctly, RAW8 and user defined data types will
> always pass through, plus the other data types listed here. Is that right?

Correct.

>
> >
> > > > +
> > > > +- xlnx,axis-tdata-width: AXI Stream width, This denotes the AXI Stream
> width.
> > > > + It depends on Data type chosen, Video Format Bridge enabled/disabled
> and
> > > > + pixels per clock. If VFB is disabled then its value is either 0x20 (32 bit)
> > > > + or 0x40(64 bit) width.
> > > > +
> > > > +- xlnx,video-format, xlnx,video-width: Video format and width, as defined
> in
> > > > + video.txt.
> > >
> > > Ditto.
> > >
> > Again these are fixed values and can't be changed at run time.
> > These are used to determine the media bus format.
>
> What kind of values can the xlnx,video-format property have? How about
> xlnx.video-width? Where can video.txt be found?
>

The video.txt is present at Documentation/devicetree/bindings/media/xilinx/video.txt
But I have removed this from v2 as the media bus format can be derived from the CSI pixel format
as described above.

> >
> > > > +
> > > > +- port: Video port, using the DT bindings defined in ../video-interfaces.txt.
> > > > + The CSI 2 Rx Subsystem has a two ports, one input port for connecting
> to
> > > > + camera sensor and other is output port.
> > > > +
> > > > +- data-lanes: The number of data lanes through which CSI2 Rx Subsystem
> is
> > > > + connected to the camera sensor as per video-interfaces.txt
> > >
> > > This is somewhat different from the documentation in video-interfaces.txt.
> > > Could you align the two? I don't think there's a need to document standard
> > > properties in device binding files elaborately; rather just the hardware
> > > specific bits.
> > >
> >
> > Agree. In this current IP there is no way to re-order the lanes which are set at
> design time.
> > So physical and logical lanes are at same index. This could only be used to
> determine how many lanes are allowed to be programmed.
> > For e.g. if design has set the number of lanes as 4 and xlnx,en-active-lanes is
> present, then the number of lanes
> > can be set from 1 to 4.
>
> Ack.
>
> --
> Regards,
>
> Sakari Ailus
> [email protected]

Regards
Vishal Sagar