All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Add support for Xilinx UHD-SDI Receiver subsystem
@ 2019-06-04 13:55 ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-04 13:55 UTC (permalink / raw)
  To: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar

Xilinx SMPTE UHD-SDI Receiver Subsystem
----------------------------------------

The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create
systems based on SMPTE SDI protocols. It receives unaligned native SDI
streams from the SDI GT PHY and outputs an AXI4-Stream video stream,
native video, or native SDI using Xilinx transceivers as the physical
layer.

                SMPTE UHD-SDI Rx Subsystem AXI4-Stream Architecture

          +============================================================+
	  |           Native SDI           Native Video                |
SDI	  |   +=========+   |   +============+  |   +=============+    |AXI4
Stream	  |   |  SMPTE  |   V   |   SDI Rx   |  V   | Video In    |    |Stream
--------->|-->| UHD-SDI |------>|    to      |----->|   to        |--->|------->
	  |   |   RX    |       |  Native    |      | AXI4-Stream |    |
	  |   +=========+       |Video Bridge|      +=============+    |
	  |      |  ^           +============+                         |
   <------|------+  |                                                  |
sdi_rx_irq|         |                                                  |
	  +=====+===+==================================================+
                    |                            ^              ^
                    |                            |              |
                 s_axi_aclk                   sdi_rx_clk   video_out_clk


The subsystem consists of the following subcores:
- SMPTE UHD-SDI (RX)
- SDI RX to Video Bridge
- Video In to AXI4-Stream

At design time, this subsystem can be configured in 3Gbps, 6Gbps or
12Gbps mode. It can also be configured to output
- SDI Native stream
- Native Video
- AXI4-Stream

This driver only supports the AXI4-Stream configuration as there is a
corresponding media bus format for YUV 422 10 bits per component in
MEDIA_BUS_FMT_UYVY10_1X20.

Though the core also supports YUV 420 10 bits per component, this is
not supported in driver due to lack of corresponding media bus format
currently.

The SDI core has detection modes where in it can be configured to detect
one or more modes from SD (Standard Definition), HD (High Definition),
3GA, 3GB, 6G and 12G modes. When the core has detected the format, it
generates a video lock. In case the source is removed or there is data
corruption, the video may unlock. This is intimated to the application
via a V4L2 event. Other events which application can subscribe are for
overflow and underflow of the video bridges.

The driver gives out the stream properties like width, height, colorformat,
frame interval and progressive/interlaced based on the ST352 packet in SDI
stream. If the ST352 packet is absent, then the values detected by the
SMPTE UHD-SDI Rx core are used.

The SDI core detection modes and detected mode, errors, etc are all
accessible via v4l controls. This driver has been tested with Omnitek
Ultra4K HD, Phabrix Qx and Blackmagic SDI-HDMI convertors.

This patch set is being sent on top of v8 of Xilinx MIPI CSI2-Rx Subsystem
driver patches.

Vishal Sagar (2):
  media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver
    Subsystem
  media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

 .../bindings/media/xilinx/xlnx,sdirxss.txt         |   80 +
 drivers/media/platform/xilinx/Kconfig              |   11 +
 drivers/media/platform/xilinx/Makefile             |    1 +
 drivers/media/platform/xilinx/xilinx-sdirxss.c     | 1846 ++++++++++++++++++++
 include/uapi/linux/xilinx-sdirxss.h                |   63 +
 include/uapi/linux/xilinx-v4l2-controls.h          |   30 +
 include/uapi/linux/xilinx-v4l2-events.h            |    9 +
 7 files changed, 2040 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
 create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
 create mode 100644 include/uapi/linux/xilinx-sdirxss.h

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 0/2] Add support for Xilinx UHD-SDI Receiver subsystem
@ 2019-06-04 13:55 ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-04 13:55 UTC (permalink / raw)
  To: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland
  Cc: Sandip Kothari, devicetree, linux-kernel, Vishal Sagar,
	Dinesh Kumar, linux-arm-kernel, linux-media

Xilinx SMPTE UHD-SDI Receiver Subsystem
----------------------------------------

The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create
systems based on SMPTE SDI protocols. It receives unaligned native SDI
streams from the SDI GT PHY and outputs an AXI4-Stream video stream,
native video, or native SDI using Xilinx transceivers as the physical
layer.

                SMPTE UHD-SDI Rx Subsystem AXI4-Stream Architecture

          +============================================================+
	  |           Native SDI           Native Video                |
SDI	  |   +=========+   |   +============+  |   +=============+    |AXI4
Stream	  |   |  SMPTE  |   V   |   SDI Rx   |  V   | Video In    |    |Stream
--------->|-->| UHD-SDI |------>|    to      |----->|   to        |--->|------->
	  |   |   RX    |       |  Native    |      | AXI4-Stream |    |
	  |   +=========+       |Video Bridge|      +=============+    |
	  |      |  ^           +============+                         |
   <------|------+  |                                                  |
sdi_rx_irq|         |                                                  |
	  +=====+===+==================================================+
                    |                            ^              ^
                    |                            |              |
                 s_axi_aclk                   sdi_rx_clk   video_out_clk


The subsystem consists of the following subcores:
- SMPTE UHD-SDI (RX)
- SDI RX to Video Bridge
- Video In to AXI4-Stream

At design time, this subsystem can be configured in 3Gbps, 6Gbps or
12Gbps mode. It can also be configured to output
- SDI Native stream
- Native Video
- AXI4-Stream

This driver only supports the AXI4-Stream configuration as there is a
corresponding media bus format for YUV 422 10 bits per component in
MEDIA_BUS_FMT_UYVY10_1X20.

Though the core also supports YUV 420 10 bits per component, this is
not supported in driver due to lack of corresponding media bus format
currently.

The SDI core has detection modes where in it can be configured to detect
one or more modes from SD (Standard Definition), HD (High Definition),
3GA, 3GB, 6G and 12G modes. When the core has detected the format, it
generates a video lock. In case the source is removed or there is data
corruption, the video may unlock. This is intimated to the application
via a V4L2 event. Other events which application can subscribe are for
overflow and underflow of the video bridges.

The driver gives out the stream properties like width, height, colorformat,
frame interval and progressive/interlaced based on the ST352 packet in SDI
stream. If the ST352 packet is absent, then the values detected by the
SMPTE UHD-SDI Rx core are used.

The SDI core detection modes and detected mode, errors, etc are all
accessible via v4l controls. This driver has been tested with Omnitek
Ultra4K HD, Phabrix Qx and Blackmagic SDI-HDMI convertors.

This patch set is being sent on top of v8 of Xilinx MIPI CSI2-Rx Subsystem
driver patches.

Vishal Sagar (2):
  media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver
    Subsystem
  media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

 .../bindings/media/xilinx/xlnx,sdirxss.txt         |   80 +
 drivers/media/platform/xilinx/Kconfig              |   11 +
 drivers/media/platform/xilinx/Makefile             |    1 +
 drivers/media/platform/xilinx/xilinx-sdirxss.c     | 1846 ++++++++++++++++++++
 include/uapi/linux/xilinx-sdirxss.h                |   63 +
 include/uapi/linux/xilinx-v4l2-controls.h          |   30 +
 include/uapi/linux/xilinx-v4l2-events.h            |    9 +
 7 files changed, 2040 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
 create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
 create mode 100644 include/uapi/linux/xilinx-sdirxss.h

-- 
1.8.3.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem
  2019-06-04 13:55 ` Vishal Sagar
@ 2019-06-04 13:55   ` Vishal Sagar
  -1 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-04 13:55 UTC (permalink / raw)
  To: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar

Add bindings documentation for Xilinx UHD-SDI Receiver Subsystem.

The Xilinx UHD-SDI Receiver Subsystem consists of SMPTE UHD-SDI (RX) IP
core, an SDI RX to Video Bridge IP core to convert SDI video to native
video and a Video In to AXI4-Stream IP core to convert native video to
AXI4-Stream.

Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
---
 .../bindings/media/xilinx/xlnx,sdirxss.txt         | 80 ++++++++++++++++++++++
 1 file changed, 80 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt

diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
new file mode 100644
index 0000000..8445bee
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
@@ -0,0 +1,80 @@
+
+Xilinx SMPTE UHD-SDI Receiver Subsystem Device Tree Bindings
+------------------------------------------------------------
+
+The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create systems
+based on SMPTE SDI protocols. It receives unaligned native SDI streams from
+the SDI GT PHY and outputs an AXI4-Stream video stream, native video, or
+native SDI using Xilinx transceivers as the physical layer.
+
+The subsystem consists of
+1 - SMPTE UHD-SDI Rx
+2 - SDI Rx to Native Video Bridge
+3 - Video In to AXI4-Stream Bridge
+
+The subsystem can capture SDI streams in utpo 12G mode and output a dual pixel
+per clock YUV 422 or 420 10 bits per component AXI4-Stream.
+
+Required properties:
+--------------------
+- compatible: Must contain "xlnx,v-smpte-uhdsdi-rx-ss"
+- reg: Physical base address and length of the registers set for the device.
+- interrupts: Contains the interrupt line number.
+- clocks: List of phandles to AXI4-Lite clock, core clock to SMPTE UHD-SDI Rx
+  and Video clocks.
+- clock-names: Must contain "s_axi_aclk", "sdi_rx_clk" and "video_out_clk" in
+  the same order as clocks listed in clocks property.
+- xlnx,line-rate: The maximum mode supported by the design. Possible values are
+  are as below -
+  12G_SDI_8DS	- 12G mode
+  6G_SDI	-  6G mode
+  3G_SDI	-  3G mode
+
+Optional properties:
+--------------------
+- xlnx,include-edh: This is present when the Error Detection and Handling
+  processor is enabled in design.
+
+Ports
+-----
+The device node shall contain one 'port' child node as defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Generally the SDI port is connected to a device like SDI Broadcast camera which
+is independently controlled. Hence port@0 is a source port which can be
+connected to downstream IP which can work with AXI4 Stream data.
+
+Required port properties:
+-------------------------
+- reg: 0 - for source port.
+
+- xlnx,video-format: This can be XVIP_VF_YUV_422 or XVIP_VF_YUV_420.
+- xlnx,video-width: This is should be 10.
+
+Example:
+		v_smpte_uhdsdi_rx_ss: v_smpte_uhdsdi_rx_ss@80000000 {
+			compatible = "xlnx,v-smpte-uhdsdi-rx-ss";
+			interrupt-parent = <&gic>;
+			interrupts = <0 89 4>;
+			reg = <0x0 0x80000000 0x0 0x10000>;
+			xlnx,include-edh;
+			xlnx,line-rate = "12G_SDI_8DS";
+			clocks = <&clk_1>, <&si570_1>, <&clk_2>;
+			clock-names = "s_axi_aclk", "sdi_rx_clk", "video_out_clk";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					reg = <0>;
+
+					xlnx,video-format = <XVIP_VF_YUV_422>;
+					xlnx,video-width = <10>;
+
+					sdirx_out: endpoint {
+						remote-endpoint = <&vcap_sdirx_in>;
+					};
+				};
+			};
+		};
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem
@ 2019-06-04 13:55   ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-04 13:55 UTC (permalink / raw)
  To: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland
  Cc: Sandip Kothari, devicetree, linux-kernel, Vishal Sagar,
	Dinesh Kumar, linux-arm-kernel, linux-media

Add bindings documentation for Xilinx UHD-SDI Receiver Subsystem.

The Xilinx UHD-SDI Receiver Subsystem consists of SMPTE UHD-SDI (RX) IP
core, an SDI RX to Video Bridge IP core to convert SDI video to native
video and a Video In to AXI4-Stream IP core to convert native video to
AXI4-Stream.

Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
---
 .../bindings/media/xilinx/xlnx,sdirxss.txt         | 80 ++++++++++++++++++++++
 1 file changed, 80 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt

diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
new file mode 100644
index 0000000..8445bee
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
@@ -0,0 +1,80 @@
+
+Xilinx SMPTE UHD-SDI Receiver Subsystem Device Tree Bindings
+------------------------------------------------------------
+
+The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create systems
+based on SMPTE SDI protocols. It receives unaligned native SDI streams from
+the SDI GT PHY and outputs an AXI4-Stream video stream, native video, or
+native SDI using Xilinx transceivers as the physical layer.
+
+The subsystem consists of
+1 - SMPTE UHD-SDI Rx
+2 - SDI Rx to Native Video Bridge
+3 - Video In to AXI4-Stream Bridge
+
+The subsystem can capture SDI streams in utpo 12G mode and output a dual pixel
+per clock YUV 422 or 420 10 bits per component AXI4-Stream.
+
+Required properties:
+--------------------
+- compatible: Must contain "xlnx,v-smpte-uhdsdi-rx-ss"
+- reg: Physical base address and length of the registers set for the device.
+- interrupts: Contains the interrupt line number.
+- clocks: List of phandles to AXI4-Lite clock, core clock to SMPTE UHD-SDI Rx
+  and Video clocks.
+- clock-names: Must contain "s_axi_aclk", "sdi_rx_clk" and "video_out_clk" in
+  the same order as clocks listed in clocks property.
+- xlnx,line-rate: The maximum mode supported by the design. Possible values are
+  are as below -
+  12G_SDI_8DS	- 12G mode
+  6G_SDI	-  6G mode
+  3G_SDI	-  3G mode
+
+Optional properties:
+--------------------
+- xlnx,include-edh: This is present when the Error Detection and Handling
+  processor is enabled in design.
+
+Ports
+-----
+The device node shall contain one 'port' child node as defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Generally the SDI port is connected to a device like SDI Broadcast camera which
+is independently controlled. Hence port@0 is a source port which can be
+connected to downstream IP which can work with AXI4 Stream data.
+
+Required port properties:
+-------------------------
+- reg: 0 - for source port.
+
+- xlnx,video-format: This can be XVIP_VF_YUV_422 or XVIP_VF_YUV_420.
+- xlnx,video-width: This is should be 10.
+
+Example:
+		v_smpte_uhdsdi_rx_ss: v_smpte_uhdsdi_rx_ss@80000000 {
+			compatible = "xlnx,v-smpte-uhdsdi-rx-ss";
+			interrupt-parent = <&gic>;
+			interrupts = <0 89 4>;
+			reg = <0x0 0x80000000 0x0 0x10000>;
+			xlnx,include-edh;
+			xlnx,line-rate = "12G_SDI_8DS";
+			clocks = <&clk_1>, <&si570_1>, <&clk_2>;
+			clock-names = "s_axi_aclk", "sdi_rx_clk", "video_out_clk";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					reg = <0>;
+
+					xlnx,video-format = <XVIP_VF_YUV_422>;
+					xlnx,video-width = <10>;
+
+					sdirx_out: endpoint {
+						remote-endpoint = <&vcap_sdirx_in>;
+					};
+				};
+			};
+		};
-- 
1.8.3.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-04 13:55 ` Vishal Sagar
@ 2019-06-04 13:55   ` Vishal Sagar
  -1 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-04 13:55 UTC (permalink / raw)
  To: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar

The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
streams from SDI sources like SDI broadcast equipment like cameras and
mixers. This block outputs either native SDI, native video or
AXI4-Stream compliant data stream for further processing. Please refer
to PG290 for details.

The driver is used to configure the IP to add framer, search for
specific modes, get the detected mode, stream parameters, errors, etc.
It also generates events for video lock/unlock, bridge over/under flow.

The driver supports only 10 bpc YUV 422 media bus format. It also
decodes the stream parameters based on the ST352 packet embedded in the
stream. In case the ST352 packet isn't present in the stream, the core's
detected properties are used to set stream properties.

The driver currently supports only the AXI4-Stream configuration.

Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
---
 drivers/media/platform/xilinx/Kconfig          |   11 +
 drivers/media/platform/xilinx/Makefile         |    1 +
 drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
 include/uapi/linux/xilinx-sdirxss.h            |   63 +
 include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
 include/uapi/linux/xilinx-v4l2-events.h        |    9 +
 6 files changed, 1960 insertions(+)
 create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
 create mode 100644 include/uapi/linux/xilinx-sdirxss.h

diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
index cd1a0fd..0c68caa 100644
--- a/drivers/media/platform/xilinx/Kconfig
+++ b/drivers/media/platform/xilinx/Kconfig
@@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
 	  Bridge. The driver is used to set the number of active lanes and
 	  get short packet data.
 
+config VIDEO_XILINX_SDIRXSS
+	tristate "Xilinx UHD SDI Rx Subsystem"
+	help
+	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
+	  based driver that takes input from a SDI source like SDI camera and
+	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
+	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
+	  AXI4-Stream bridge. The driver is used to set different stream
+	  detection modes and identify stream properties to properly configure
+	  downstream.
+
 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 6119a34..223f2ea 100644
--- a/drivers/media/platform/xilinx/Makefile
+++ b/drivers/media/platform/xilinx/Makefile
@@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
new file mode 100644
index 0000000..ba2d9d0
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
@@ -0,0 +1,1846 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Xilinx SDI Rx Subsystem
+ *
+ * Copyright (C) 2017 - 2019 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
+ *
+ */
+
+#include <dt-bindings/media/xilinx-vip.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/clk.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/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-sdirxss.h>
+#include <linux/xilinx-v4l2-events.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"
+
+/*
+ * SDI Rx register map, bitmask and offsets
+ */
+#define XSDIRX_RST_CTRL_REG		0x00
+#define XSDIRX_MDL_CTRL_REG		0x04
+#define XSDIRX_GLBL_IER_REG		0x0C
+#define XSDIRX_ISR_REG			0x10
+#define XSDIRX_IER_REG			0x14
+#define XSDIRX_ST352_VALID_REG		0x18
+#define XSDIRX_ST352_DS1_REG		0x1C
+#define XSDIRX_ST352_DS3_REG		0x20
+#define XSDIRX_ST352_DS5_REG		0x24
+#define XSDIRX_ST352_DS7_REG		0x28
+#define XSDIRX_ST352_DS9_REG		0x2C
+#define XSDIRX_ST352_DS11_REG		0x30
+#define XSDIRX_ST352_DS13_REG		0x34
+#define XSDIRX_ST352_DS15_REG		0x38
+#define XSDIRX_VERSION_REG		0x3C
+#define XSDIRX_SS_CONFIG_REG		0x40
+#define XSDIRX_MODE_DET_STAT_REG	0x44
+#define XSDIRX_TS_DET_STAT_REG		0x48
+#define XSDIRX_EDH_STAT_REG		0x4C
+#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
+#define XSDIRX_EDH_ERRCNT_REG		0x54
+#define XSDIRX_CRC_ERRCNT_REG		0x58
+#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
+#define XSDIRX_SB_RX_STS_REG		0x60
+
+#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
+#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
+#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
+#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
+#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
+#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
+
+#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
+#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
+#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
+#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
+#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
+#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
+#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
+#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
+#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
+
+#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
+#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
+
+#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
+
+#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
+#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
+#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
+#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
+
+#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
+				XSDIRX_INTR_VIDUNLOCK_MASK |\
+				XSDIRX_INTR_OVERFLOW_MASK |\
+				XSDIRX_INTR_UNDERFLOW_MASK)
+
+#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
+#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
+#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
+#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
+#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
+#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
+#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
+#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
+
+#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
+#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
+#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
+#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
+#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
+
+#define XSDIRX_ACTIVE_STREAMS_1		0x0
+#define XSDIRX_ACTIVE_STREAMS_2		0x1
+#define XSDIRX_ACTIVE_STREAMS_4		0x2
+#define XSDIRX_ACTIVE_STREAMS_8		0x3
+#define XSDIRX_ACTIVE_STREAMS_16	0x4
+
+#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
+#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
+#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
+#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
+#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
+#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
+#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
+
+#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
+#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
+#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
+#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
+#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
+#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
+#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
+#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
+#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
+#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
+#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
+
+#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
+#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
+#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
+#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
+#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
+#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
+#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
+
+#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
+
+#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
+#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
+
+#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
+#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
+#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
+#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
+#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
+
+#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
+
+#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
+#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
+#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
+#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
+
+/* Number of media pads */
+#define XSDIRX_MEDIA_PADS	(1)
+
+#define XSDIRX_DEFAULT_WIDTH	(1920)
+#define XSDIRX_DEFAULT_HEIGHT	(1080)
+
+#define XSDIRX_MAX_STR_LENGTH	16
+
+#define XSDIRXSS_SDI_STD_3G		0
+#define XSDIRXSS_SDI_STD_6G		1
+#define XSDIRXSS_SDI_STD_12G_8DS	2
+
+#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
+
+#define XSDIRX_MODE_HD_MASK	0x0
+#define XSDIRX_MODE_SD_MASK	0x1
+#define XSDIRX_MODE_3G_MASK	0x2
+#define XSDIRX_MODE_6G_MASK	0x4
+#define XSDIRX_MODE_12GI_MASK	0x5
+#define XSDIRX_MODE_12GF_MASK	0x6
+
+/*
+ * Maximum number of events per file handle.
+ */
+#define XSDIRX_MAX_EVENTS	(128)
+
+/* ST352 related macros */
+#define XST352_PAYLOAD_BYTE_MASK	0xFF
+#define XST352_PAYLOAD_BYTE1_SHIFT	0
+#define XST352_PAYLOAD_BYTE2_SHIFT	8
+#define XST352_PAYLOAD_BYTE3_SHIFT	16
+#define XST352_PAYLOAD_BYTE4_SHIFT	24
+
+#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
+#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
+#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
+#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
+#define XST352_BYTE1_ST372_DL_3GB		0x8A
+#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
+#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
+#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
+#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
+#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
+
+#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
+#define XST352_BYTE2_TS_TYPE_OFFSET		15
+#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
+#define XST352_BYTE2_PIC_TYPE_OFFSET		14
+#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
+#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
+
+#define XST352_BYTE2_FPS_MASK			0xF
+#define XST352_BYTE2_FPS_SHIFT			8
+#define XST352_BYTE2_FPS_24F			0x2
+#define XST352_BYTE2_FPS_24			0x3
+#define XST352_BYTE2_FPS_48F			0x4
+#define XST352_BYTE2_FPS_25			0x5
+#define XST352_BYTE2_FPS_30F			0x6
+#define XST352_BYTE2_FPS_30			0x7
+#define XST352_BYTE2_FPS_48			0x8
+#define XST352_BYTE2_FPS_50			0x9
+#define XST352_BYTE2_FPS_60F			0xA
+#define XST352_BYTE2_FPS_60			0xB
+/* Table 4 ST 2081-10:2015 */
+#define XST352_BYTE2_FPS_96			0xC
+#define XST352_BYTE2_FPS_100			0xD
+#define XST352_BYTE2_FPS_120			0xE
+#define XST352_BYTE2_FPS_120F			0xF
+
+#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
+#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
+
+#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
+#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
+#define XST352_BYTE3_COLOR_FORMAT_422		0x0
+#define XST352_BYTE3_COLOR_FORMAT_420		0x3
+
+/**
+ * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
+ * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
+ * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
+ * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
+ * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
+ * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
+ * @XSDIRX_PAL: PAL encoding detected with AP 720x576
+ * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
+ */
+enum sdi_family_enc {
+	XSDIRX_SMPTE_ST_274	= 0,
+	XSDIRX_SMPTE_ST_296	= 1,
+	XSDIRX_SMPTE_ST_2048_2	= 2,
+	XSDIRX_SMPTE_ST_295	= 3,
+	XSDIRX_NTSC		= 8,
+	XSDIRX_PAL		= 9,
+	XSDIRX_TS_UNKNOWN	= 15
+};
+
+/**
+ * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
+ * @dev: Platform structure
+ * @iomem: Base address of subsystem
+ * @irq: requested irq number
+ * @include_edh: EDH processor presence
+ * @mode: 3G/6G/12G mode
+ * @axi_clk: Axi lite interface clock
+ * @sdirx_clk: SDI Rx GT clock
+ * @vidout_clk: Video clock
+ */
+struct xsdirxss_core {
+	struct device *dev;
+	void __iomem *iomem;
+	int irq;
+	bool include_edh;
+	int mode;
+	struct clk *axi_clk;
+	struct clk *sdirx_clk;
+	struct clk *vidout_clk;
+};
+
+/**
+ * struct xsdirxss_state - SDI Rx Subsystem device structure
+ * @core: Core structure for MIPI SDI Rx Subsystem
+ * @subdev: The v4l2 subdev structure
+ * @ctrl_handler: control handler
+ * @event: Holds the video unlock event
+ * @formats: Active V4L2 formats on each pad
+ * @default_format: default V4L2 media bus format
+ * @frame_interval: Captures the frame rate
+ * @vip_format: format information corresponding to the active format
+ * @pads: media pads
+ * @streaming: Flag for storing streaming state
+ * @vidlocked: Flag indicating SDI Rx has locked onto video stream
+ * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
+ *
+ * This structure contains the device driver related parameters
+ */
+struct xsdirxss_state {
+	struct xsdirxss_core core;
+	struct v4l2_subdev subdev;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_event event;
+	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
+	struct v4l2_mbus_framefmt default_format;
+	struct v4l2_fract frame_interval;
+	const struct xvip_video_format *vip_format;
+	struct media_pad pads[XSDIRX_MEDIA_PADS];
+	bool streaming;
+	bool vidlocked;
+	bool ts_is_interlaced;
+};
+
+static inline struct xsdirxss_state *
+to_xsdirxssstate(struct v4l2_subdev *subdev)
+{
+	return container_of(subdev, struct xsdirxss_state, subdev);
+}
+
+/*
+ * Register related operations
+ */
+static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
+{
+	return ioread32(xsdirxss->iomem + addr);
+}
+
+static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
+				  u32 value)
+{
+	iowrite32(value, xsdirxss->iomem + addr);
+}
+
+static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
+				u32 clr)
+{
+	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
+}
+
+static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
+				u32 set)
+{
+	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
+}
+
+static void xsdirx_core_disable(struct xsdirxss_core *core)
+{
+	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
+}
+
+static void xsdirx_core_enable(struct xsdirxss_core *core)
+{
+	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
+}
+
+static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
+{
+	u32 i, val;
+
+	mask &= XSDIRX_DETECT_ALL_MODES;
+	if (!mask) {
+		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
+		return -EINVAL;
+	}
+
+	dev_dbg(core->dev, "mask = 0x%x\n", mask);
+
+	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
+	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
+	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
+	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
+
+	if (hweight16(mask) > 1) {
+		/* Multi mode detection as more than 1 bit set in mask */
+		dev_dbg(core->dev, "Detect multiple modes\n");
+		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
+			switch (mask & (1 << i)) {
+			case BIT(XSDIRX_MODE_SD_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_HD_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_3G_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_6G_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_12GI_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_12GF_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
+				break;
+			}
+		}
+		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
+	} else {
+		/* Fixed Mode */
+		u32 forced_mode_mask = 0;
+
+		dev_dbg(core->dev, "Detect fixed mode\n");
+
+		/* Find offset of first bit set */
+		switch (__ffs(mask)) {
+		case XSDIRX_MODE_SD_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_SD_MASK;
+			break;
+		case XSDIRX_MODE_HD_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_HD_MASK;
+			break;
+		case XSDIRX_MODE_3G_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_3G_MASK;
+			break;
+		case XSDIRX_MODE_6G_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_6G_MASK;
+			break;
+		case XSDIRX_MODE_12GI_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
+			break;
+		case XSDIRX_MODE_12GF_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
+			break;
+		}
+		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
+			forced_mode_mask);
+		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
+	}
+
+	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
+		val);
+	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
+
+	return 0;
+}
+
+static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
+{
+	if (flag)
+		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
+			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
+	else
+		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
+			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
+}
+
+static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
+{
+	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
+
+	val = enable & XSDIRX_EDH_ALLERR_MASK;
+
+	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
+}
+
+static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
+{
+	/*
+	 * The video lock window is the amount of time for which the
+	 * the mode and transport stream should be locked to get the
+	 * video lock interrupt.
+	 */
+	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
+}
+
+static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
+{
+	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
+}
+
+static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
+{
+	xsdirxss_set(core, XSDIRX_IER_REG, mask);
+}
+
+static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
+{
+	if (flag)
+		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
+			     XSDIRX_GLBL_INTR_EN_MASK);
+	else
+		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
+			     XSDIRX_GLBL_INTR_EN_MASK);
+}
+
+static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
+{
+	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
+}
+
+static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
+{
+	if (enable)
+		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
+	else
+		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
+}
+
+static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
+{
+	if (enable)
+		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
+	else
+		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
+}
+
+static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
+{
+	/* The sdi to native bridge is followed by native to axis4 bridge */
+	if (enable) {
+		xsdirx_axis4_bridge_control(core, enable);
+		xsdirx_vid_bridge_control(core, enable);
+	} else {
+		xsdirx_vid_bridge_control(core, enable);
+		xsdirx_axis4_bridge_control(core, enable);
+	}
+}
+
+static void xsdirx_streamdowncb(struct xsdirxss_core *core)
+{
+	xsdirx_streamflow_control(core, false);
+}
+
+static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
+				   u32 framerate)
+{
+	switch (framerate) {
+	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
+		frame_interval->numerator = 1001;
+		frame_interval->denominator = 24000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_24HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 24000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_25HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 25000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
+		frame_interval->numerator = 1001;
+		frame_interval->denominator = 30000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_30HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 30000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
+		frame_interval->numerator = 1001;
+		frame_interval->denominator = 48000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_48HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 48000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_50HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 50000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
+		frame_interval->numerator = 1001;
+		frame_interval->denominator = 60000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_60HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 60000;
+		break;
+	default:
+		frame_interval->numerator = 1;
+		frame_interval->denominator = 1;
+	}
+}
+
+/**
+ * xsdirx_get_stream_properties - Get SDI Rx stream properties
+ * @state: pointer to driver state
+ *
+ * This function decodes the stream's ST352 payload (if available) to get
+ * stream properties like width, height, picture type (interlaced/progressive),
+ * etc.
+ *
+ * Return: 0 for success else errors
+ */
+static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
+{
+	struct xsdirxss_core *core = &state->core;
+	u32 mode, payload = 0, val, family, valid, tscan;
+	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
+	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
+	struct v4l2_mbus_framefmt *format = &state->formats[0];
+
+	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+
+	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
+
+	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
+		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
+		return -EINVAL;
+	}
+
+	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
+	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
+		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
+		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
+				XST352_PAYLOAD_BYTE_MASK;
+		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
+				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
+		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
+				XST352_BYTE2_PIC_TYPE_OFFSET;
+		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
+				XST352_BYTE2_FPS_MASK;
+		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
+				XST352_BYTE2_TS_TYPE_OFFSET;
+		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
+			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
+	} else {
+		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
+			mode);
+		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
+				XSDIRX_TS_DET_STAT_RATE_OFFSET;
+		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
+				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
+	}
+
+	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
+		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
+	state->ts_is_interlaced = tscan ? false : true;
+
+	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
+		state->ts_is_interlaced, family);
+
+	switch (mode) {
+	case XSDIRX_MODE_HD_MASK:
+		if (!valid) {
+			/* No payload obtained */
+			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
+				framerate, tscan);
+			/*
+			 * NOTE : A progressive segmented frame pSF will be
+			 * reported incorrectly as Interlaced as we rely on IP's
+			 * transport scan locked bit.
+			 */
+			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
+
+			switch (framerate) {
+			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
+			case XSDIRX_TS_DET_STAT_RATE_24HZ:
+			case XSDIRX_TS_DET_STAT_RATE_25HZ:
+			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
+			case XSDIRX_TS_DET_STAT_RATE_30HZ:
+				if (family == XSDIRX_SMPTE_ST_296) {
+					format->width = 1280;
+					format->height = 720;
+					format->field = V4L2_FIELD_NONE;
+				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
+					format->width = 2048;
+					format->height = 1080;
+					if (tscan)
+						format->field = V4L2_FIELD_NONE;
+					else
+						format->field =
+							V4L2_FIELD_ALTERNATE;
+				} else {
+					format->width = 1920;
+					format->height = 1080;
+					if (tscan)
+						format->field = V4L2_FIELD_NONE;
+					else
+						format->field =
+							V4L2_FIELD_ALTERNATE;
+				}
+				break;
+			case XSDIRX_TS_DET_STAT_RATE_50HZ:
+			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
+			case XSDIRX_TS_DET_STAT_RATE_60HZ:
+				if (family == XSDIRX_SMPTE_ST_274) {
+					format->width = 1920;
+					format->height = 1080;
+				} else {
+					format->width = 1280;
+					format->height = 720;
+				}
+				format->field = V4L2_FIELD_NONE;
+				break;
+			default:
+				format->width = 1920;
+				format->height = 1080;
+				format->field = V4L2_FIELD_NONE;
+			}
+		} else {
+			dev_dbg(core->dev, "Got the payload\n");
+			switch (byte1) {
+			case XST352_BYTE1_ST292_1x720L_1_5G:
+				/* SMPTE ST 292-1 for 720 line payloads */
+				format->width = 1280;
+				format->height = 720;
+				break;
+			case XST352_BYTE1_ST292_1x1080L_1_5G:
+				/* SMPTE ST 292-1 for 1080 line payloads */
+				format->height = 1080;
+				if (active_luma)
+					format->width = 2048;
+				else
+					format->width = 1920;
+				break;
+			default:
+				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
+				return -EINVAL;
+			}
+		}
+		break;
+	case XSDIRX_MODE_SD_MASK:
+		format->field = V4L2_FIELD_ALTERNATE;
+
+		switch (family) {
+		case XSDIRX_NTSC:
+			format->width = 720;
+			format->height = 486;
+			break;
+		case XSDIRX_PAL:
+			format->width = 720;
+			format->height = 576;
+			break;
+		default:
+			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
+			return -EINVAL;
+		}
+		break;
+	case XSDIRX_MODE_3G_MASK:
+		switch (byte1) {
+		case XST352_BYTE1_ST425_2008_750L_3GB:
+			/* Sec 4.1.6.1 SMPTE 425-2008 */
+		case XST352_BYTE1_ST372_2x720L_3GB:
+			/* Table 13 SMPTE 425-2008 */
+			format->width = 1280;
+			format->height = 720;
+			break;
+		case XST352_BYTE1_ST425_2008_1125L_3GA:
+			/* ST352 Table SMPTE 425-1 */
+		case XST352_BYTE1_ST372_DL_3GB:
+			/* Table 13 SMPTE 425-2008 */
+		case XST352_BYTE1_ST372_2x1080L_3GB:
+			/* Table 13 SMPTE 425-2008 */
+			format->height = 1080;
+			if (active_luma)
+				format->width = 2048;
+			else
+				format->width = 1920;
+			break;
+		default:
+			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
+			return -EINVAL;
+		}
+		break;
+	case XSDIRX_MODE_6G_MASK:
+		switch (byte1) {
+		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
+			/* Dual link 6G */
+		case XST352_BYTE1_ST2081_10_2160L_6G:
+			/* Table 3 SMPTE ST 2081-10 */
+			format->height = 2160;
+			if (active_luma)
+				format->width = 4096;
+			else
+				format->width = 3840;
+			break;
+		default:
+			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
+			return -EINVAL;
+		}
+		break;
+	case XSDIRX_MODE_12GI_MASK:
+	case XSDIRX_MODE_12GF_MASK:
+		switch (byte1) {
+		case XST352_BYTE1_ST2082_10_2160L_12G:
+			/* Section 4.3.1 SMPTE ST 2082-10 */
+			format->height = 2160;
+			if (active_luma)
+				format->width = 4096;
+			else
+				format->width = 3840;
+			break;
+		default:
+			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
+			return -EINVAL;
+		}
+		break;
+	default:
+		dev_err(core->dev, "Invalid Mode\n");
+		return -EINVAL;
+	}
+
+	if (valid) {
+		if (pic_type)
+			format->field = V4L2_FIELD_NONE;
+		else
+			format->field = V4L2_FIELD_ALTERNATE;
+	}
+
+	if (format->field == V4L2_FIELD_ALTERNATE)
+		format->height = format->height / 2;
+
+	switch (sampling) {
+	case XST352_BYTE3_COLOR_FORMAT_422:
+		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
+		break;
+	default:
+		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
+		return -EINVAL;
+	}
+
+	xsdirxss_get_framerate(&state->frame_interval, framerate);
+
+	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
+		format->width, format->height, format->field, payload, val);
+	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
+		state->frame_interval.numerator,
+		state->frame_interval.denominator);
+	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
+	return 0;
+}
+
+/**
+ * xsdirxss_irq_handler - Interrupt handler for SDI Rx
+ * @irq: IRQ number
+ * @dev_id: Pointer to device state
+ *
+ * The SDI Rx interrupts are cleared by first setting and then clearing the bits
+ * in the interrupt clear register. The interrupt status register is read only.
+ *
+ * Return: IRQ_HANDLED after handling interrupts
+ */
+static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
+{
+	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
+	struct xsdirxss_core *core = &state->core;
+	u32 status;
+
+	status = xsdirxss_read(core, XSDIRX_ISR_REG);
+	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
+
+	if (!status)
+		return IRQ_NONE;
+
+	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
+		u32 val1, val2;
+
+		dev_dbg(core->dev, "video lock interrupt\n");
+		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
+
+		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
+
+		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
+		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
+			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
+				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
+
+			dev_dbg(core->dev, "mode & ts lock occurred\n");
+
+			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
+			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
+
+			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
+			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
+
+			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
+			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
+
+			if (!xsdirx_get_stream_properties(state)) {
+				memset(&state->event, 0, sizeof(state->event));
+				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
+				state->event.u.src_change.changes =
+					V4L2_EVENT_SRC_CH_RESOLUTION;
+				v4l2_subdev_notify_event(&state->subdev,
+							 &state->event);
+
+				state->vidlocked = true;
+			} else {
+				dev_err(core->dev, "Unable to get stream properties!\n");
+				state->vidlocked = false;
+			}
+		} else {
+			dev_dbg(core->dev, "video unlock before video lock!\n");
+			state->vidlocked = false;
+		}
+	}
+
+	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
+		dev_dbg(core->dev, "video unlock interrupt\n");
+		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
+		xsdirx_streamdowncb(core);
+
+		memset(&state->event, 0, sizeof(state->event));
+		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
+		v4l2_subdev_notify_event(&state->subdev, &state->event);
+
+		state->vidlocked = false;
+	}
+
+	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
+		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
+		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
+
+		memset(&state->event, 0, sizeof(state->event));
+		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
+		v4l2_subdev_notify_event(&state->subdev, &state->event);
+	}
+
+	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
+		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
+		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
+
+		memset(&state->event, 0, sizeof(state->event));
+		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
+		v4l2_subdev_notify_event(&state->subdev, &state->event);
+	}
+	return IRQ_HANDLED;
+}
+
+/**
+ * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
+ * @sd: V4L2 Sub device
+ * @fh: V4L2 File Handle
+ * @sub: Subcribe event structure
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
+				    struct v4l2_fh *fh,
+				    struct v4l2_event_subscription *sub)
+{
+	int ret;
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	switch (sub->type) {
+	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
+	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
+	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
+		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
+		break;
+	case V4L2_EVENT_SOURCE_CHANGE:
+		ret = v4l2_src_change_event_subscribe(fh, sub);
+		break;
+	default:
+		return -EINVAL;
+	}
+	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
+	return ret;
+}
+
+/**
+ * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
+				      struct v4l2_fh *fh,
+				      struct v4l2_event_subscription *sub)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
+	return v4l2_event_unsubscribe(fh, sub);
+}
+
+/**
+ * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
+ * @ctrl: V4L2 control to be set
+ *
+ * This function is used to set the V4L2 controls for the Xilinx SDI Rx
+ * Subsystem.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	int ret = 0;
+	struct xsdirxss_state *xsdirxss =
+		container_of(ctrl->handler,
+			     struct xsdirxss_state, ctrl_handler);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
+		ctrl->id, ctrl->val);
+
+	if (xsdirxss->streaming) {
+		dev_err(core->dev, "Cannot set controls while streaming\n");
+		return -EINVAL;
+	}
+
+	xsdirx_core_disable(core);
+	switch (ctrl->id) {
+	case V4L2_CID_XILINX_SDIRX_FRAMER:
+		xsdirx_framer(core, ctrl->val);
+		break;
+	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
+		xsdirx_setvidlockwindow(core, ctrl->val);
+		break;
+	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
+		xsdirx_setedherrcnttrigger(core, ctrl->val);
+		break;
+	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
+		if (ctrl->val) {
+			if (core->mode == XSDIRXSS_SDI_STD_3G) {
+				dev_dbg(core->dev, "Upto 3G supported\n");
+				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
+					       BIT(XSDIRX_MODE_12GI_OFFSET) |
+					       BIT(XSDIRX_MODE_12GF_OFFSET));
+			}
+
+			if (core->mode == XSDIRXSS_SDI_STD_6G) {
+				dev_dbg(core->dev, "Upto 6G supported\n");
+				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
+					       BIT(XSDIRX_MODE_12GF_OFFSET));
+			}
+
+			ret = xsdirx_set_modedetect(core, ctrl->val);
+		} else {
+			dev_err(core->dev, "Select at least one mode!\n");
+			return -EINVAL;
+		}
+		break;
+	default:
+		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_SS_EN_MASK);
+		return -EINVAL;
+	}
+	xsdirx_core_enable(core);
+	return ret;
+}
+
+/**
+ * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
+ * @ctrl: Pointer to V4L2 control
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	u32 val;
+	struct xsdirxss_state *xsdirxss =
+		container_of(ctrl->handler,
+			     struct xsdirxss_state, ctrl_handler);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	switch (ctrl->id) {
+	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
+		if (!xsdirxss->vidlocked) {
+			dev_err(core->dev, "Can't get values when video not locked!\n");
+			return -EINVAL;
+		}
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+
+		switch (val) {
+		case XSDIRX_MODE_SD_MASK:
+			ctrl->val = XSDIRX_MODE_SD_OFFSET;
+			break;
+		case XSDIRX_MODE_HD_MASK:
+			ctrl->val = XSDIRX_MODE_HD_OFFSET;
+			break;
+		case XSDIRX_MODE_3G_MASK:
+			ctrl->val = XSDIRX_MODE_3G_OFFSET;
+			break;
+		case XSDIRX_MODE_6G_MASK:
+			ctrl->val = XSDIRX_MODE_6G_OFFSET;
+			break;
+		case XSDIRX_MODE_12GI_MASK:
+			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
+			break;
+		case XSDIRX_MODE_12GF_MASK:
+			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
+			break;
+		}
+		break;
+	case V4L2_CID_XILINX_SDIRX_CRC:
+		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
+		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
+		break;
+	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+		if (val == XSDIRX_MODE_SD_MASK) {
+			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
+		} else {
+			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+		if (val == XSDIRX_MODE_SD_MASK) {
+			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
+		} else {
+			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
+		if (!xsdirxss->vidlocked) {
+			dev_err(core->dev, "Can't get values when video not locked!\n");
+			return -EINVAL;
+		}
+		ctrl->val = xsdirxss->ts_is_interlaced;
+		break;
+	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
+		if (!xsdirxss->vidlocked) {
+			dev_err(core->dev, "Can't get values when video not locked!\n");
+			return -EINVAL;
+		}
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
+		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
+		ctrl->val = 1 << val;
+		break;
+	case V4L2_CID_XILINX_SDIRX_IS_3GB:
+		if (!xsdirxss->vidlocked) {
+			dev_err(core->dev, "Can't get values when video not locked!\n");
+			return -EINVAL;
+		}
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
+		ctrl->val = val ? true : false;
+		break;
+	default:
+		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
+		return -EINVAL;
+	}
+	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
+		ctrl->id, ctrl->val);
+	return 0;
+}
+
+/**
+ * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
+ * @sd: Pointer to V4L2 subdevice structure
+ *
+ * This function prints the current status of Xilinx SDI Rx Subsystem
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_log_status(struct v4l2_subdev *sd)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+	u32 data, i;
+
+	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
+	for (i = 0; i < 0x28; i++) {
+		data = xsdirxss_read(core, i * 4);
+		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
+			  i * 4, data);
+	}
+	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
+	return 0;
+}
+
+static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
+{
+	xsdirx_streamflow_control(&xsdirxss->core, true);
+}
+
+static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
+{
+	xsdirx_streamflow_control(&xsdirxss->core, false);
+}
+
+/**
+ * xsdirxss_g_frame_interval - Get the frame interval
+ * @sd: V4L2 Sub device
+ * @fi: Pointer to V4l2 Sub device frame interval structure
+ *
+ * This function is used to get the frame interval.
+ * The frame rate can be integral or fractional.
+ * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
+ * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
+				     struct v4l2_subdev_frame_interval *fi)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	if (!xsdirxss->vidlocked) {
+		dev_err(core->dev, "Video not locked!\n");
+		return -EINVAL;
+	}
+
+	fi->interval = xsdirxss->frame_interval;
+
+	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
+		xsdirxss->frame_interval.numerator,
+		xsdirxss->frame_interval.denominator);
+	return 0;
+}
+
+/**
+ * xsdirxss_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 SDI Rx Subsystem.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	if (enable) {
+		if (!xsdirxss->vidlocked) {
+			dev_dbg(core->dev, "Video is not locked\n");
+			return -EINVAL;
+		}
+		if (xsdirxss->streaming) {
+			dev_dbg(core->dev, "Already streaming\n");
+			return -EINVAL;
+		}
+
+		xsdirxss_start_stream(xsdirxss);
+		xsdirxss->streaming = true;
+		dev_dbg(core->dev, "Streaming started\n");
+	} else {
+		if (!xsdirxss->streaming) {
+			dev_dbg(core->dev, "Stopped streaming already\n");
+			return -EINVAL;
+		}
+
+		xsdirxss_stop_stream(xsdirxss);
+		xsdirxss->streaming = false;
+		dev_dbg(core->dev, "Streaming stopped\n");
+	}
+
+	return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
+			  struct v4l2_subdev_pad_config *cfg,
+				unsigned int pad, u32 which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &xsdirxss->formats[pad];
+	default:
+		return NULL;
+	}
+}
+
+/**
+ * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+					struct v4l2_subdev_format *fmt)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	if (!xsdirxss->vidlocked) {
+		dev_err(core->dev, "Video not locked!\n");
+		return -EINVAL;
+	}
+
+	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
+						 fmt->pad, fmt->which);
+
+	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
+		fmt->format.width, fmt->format.height, fmt->format.field);
+
+	return 0;
+}
+
+/**
+ * xsdirxss_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.
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_set_format(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *__format;
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+
+	dev_dbg(xsdirxss->core.dev,
+		"set width %d height %d code %d field %d colorspace %d\n",
+		fmt->format.width, fmt->format.height,
+		fmt->format.code, fmt->format.field,
+		fmt->format.colorspace);
+
+	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
+					     fmt->pad, fmt->which);
+
+	/* Currently reset the code to one fixed in hardware */
+	/* TODO : Add checks for width height */
+	fmt->format.code = __format->code;
+
+	return 0;
+}
+
+/**
+ * xsdirxss_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 pad.
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_open(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format;
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+
+	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	*format = xsdirxss->default_format;
+
+	return 0;
+}
+
+/**
+ * xsdirxss_close - Called on v4l2_close()
+ * @sd: Pointer to V4L2 sub device structure
+ * @fh: Pointer to V4L2 File handle
+ *
+ * This function is called on v4l2_close().
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_close(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_fh *fh)
+{
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static const struct media_entity_operations xsdirxss_media_ops = {
+	.link_validate = v4l2_subdev_link_validate
+};
+
+static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
+	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
+	.s_ctrl	= xsdirxss_s_ctrl
+};
+
+static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
+	{
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
+		.name	= "SDI Rx : EDH Error Count Enable",
+		.type	= V4L2_CTRL_TYPE_BITMASK,
+		.min	= 0,
+		.max	= XSDIRX_EDH_ALLERR_MASK,
+		.def	= 0,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
+		.name	= "SDI Rx : EDH Error Count",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= 0xFFFF,
+		.step	= 1,
+		.def	= 0,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
+		.name	= "SDI Rx : EDH Status",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= 0xFFFFFFFF,
+		.step	= 1,
+		.def	= 0,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}
+};
+
+static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
+	{
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
+		.name	= "SDI Rx : Enable Framer",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.step	= 1,
+		.def	= true,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
+		.name	= "SDI Rx : Video Lock Window",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= 0xFFFFFFFF,
+		.step	= 1,
+		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
+		.name	= "SDI Rx : Modes search Mask",
+		.type	= V4L2_CTRL_TYPE_BITMASK,
+		.min	= 0,
+		.max	= XSDIRX_DETECT_ALL_MODES,
+		.def	= XSDIRX_DETECT_ALL_MODES,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
+		.name	= "SDI Rx : Mode Detect Status",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= XSDIRX_MODE_SD_OFFSET,
+		.max	= XSDIRX_MODE_12GF_OFFSET,
+		.step	= 1,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_CRC,
+		.name	= "SDI Rx : CRC Error status",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= 0xFFFFFFFF,
+		.step	= 1,
+		.def	= 0,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
+		.name	= "SDI Rx : TS is Interlaced",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.def	= false,
+		.step	= 1,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
+		.name	= "SDI Rx : Active Streams",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 1,
+		.max	= 16,
+		.def	= 1,
+		.step	= 1,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
+		.name	= "SDI Rx : Is 3GB",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.def	= false,
+		.step	= 1,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}
+};
+
+static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
+	.log_status = xsdirxss_log_status,
+	.subscribe_event = xsdirxss_subscribe_event,
+	.unsubscribe_event = xsdirxss_unsubscribe_event
+};
+
+static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
+	.g_frame_interval = xsdirxss_g_frame_interval,
+	.s_stream = xsdirxss_s_stream
+};
+
+static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
+	.get_fmt = xsdirxss_get_format,
+	.set_fmt = xsdirxss_set_format,
+};
+
+static struct v4l2_subdev_ops xsdirxss_ops = {
+	.core = &xsdirxss_core_ops,
+	.video = &xsdirxss_video_ops,
+	.pad = &xsdirxss_pad_ops
+};
+
+static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
+	.open = xsdirxss_open,
+	.close = xsdirxss_close
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
+{
+	struct device_node *node = xsdirxss->core.dev->of_node;
+	struct device_node *ports = NULL;
+	struct device_node *port = NULL;
+	unsigned int nports = 0;
+	struct xsdirxss_core *core = &xsdirxss->core;
+	int ret;
+	const char *sdi_std;
+
+	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
+	dev_dbg(core->dev, "EDH property = %s\n",
+		core->include_edh ? "Present" : "Absent");
+
+	ret = of_property_read_string(node, "xlnx,line-rate",
+				      &sdi_std);
+	if (ret < 0) {
+		dev_err(core->dev, "xlnx,line-rate property not found\n");
+		return ret;
+	}
+
+	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
+		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
+	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
+		core->mode = XSDIRXSS_SDI_STD_6G;
+	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
+		core->mode = XSDIRXSS_SDI_STD_3G;
+	} else {
+		dev_err(core->dev, "Invalid Line Rate\n");
+		return -EINVAL;
+	}
+	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
+		core->mode);
+
+	ports = of_get_child_by_name(node, "ports");
+	if (!ports)
+		ports = node;
+
+	for_each_child_of_node(ports, port) {
+		const struct xvip_video_format *format;
+		struct device_node *endpoint;
+
+		if (!port->name || of_node_cmp(port->name, "port"))
+			continue;
+
+		format = xvip_of_get_format(port);
+		if (IS_ERR(format)) {
+			dev_err(core->dev, "invalid format in DT");
+			return PTR_ERR(format);
+		}
+
+		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
+			format->vf_code, format->width, format->bpp);
+
+		if (format->vf_code != XVIP_VF_YUV_422 &&
+		    format->vf_code != XVIP_VF_YUV_420) {
+			dev_err(core->dev, "Incorrect UG934 video format set.\n");
+			return -EINVAL;
+		}
+		xsdirxss->vip_format = format;
+
+		endpoint = of_get_next_child(port, NULL);
+		if (!endpoint) {
+			dev_err(core->dev, "No port at\n");
+			return -EINVAL;
+		}
+
+		/* Count the number of ports. */
+		nports++;
+	}
+
+	if (nports != 1) {
+		dev_err(core->dev, "invalid number of ports %u\n", nports);
+		return -EINVAL;
+	}
+
+	/* Register interrupt handler */
+	core->irq = irq_of_parse_and_map(node, 0);
+
+	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
+			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
+	if (ret) {
+		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int xsdirxss_probe(struct platform_device *pdev)
+{
+	struct v4l2_subdev *subdev;
+	struct xsdirxss_state *xsdirxss;
+	struct xsdirxss_core *core;
+	struct resource *res;
+	int ret;
+	unsigned int num_ctrls, num_edh_ctrls = 0, i;
+
+	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
+	if (!xsdirxss)
+		return -ENOMEM;
+
+	xsdirxss->core.dev = &pdev->dev;
+	core = &xsdirxss->core;
+
+	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
+	if (IS_ERR(core->axi_clk)) {
+		ret = PTR_ERR(core->axi_clk);
+		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
+		return ret;
+	}
+
+	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
+	if (IS_ERR(core->sdirx_clk)) {
+		ret = PTR_ERR(core->sdirx_clk);
+		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
+		return ret;
+	}
+
+	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
+	if (IS_ERR(core->vidout_clk)) {
+		ret = PTR_ERR(core->vidout_clk);
+		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(core->axi_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(core->sdirx_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
+		goto rx_clk_err;
+	}
+
+	ret = clk_prepare_enable(core->vidout_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
+		goto vidout_clk_err;
+	}
+
+	ret = xsdirxss_parse_of(xsdirxss);
+	if (ret < 0)
+		goto clk_err;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
+	if (IS_ERR(xsdirxss->core.iomem)) {
+		ret = PTR_ERR(xsdirxss->core.iomem);
+		goto clk_err;
+	}
+
+	/* Reset the core */
+	xsdirx_streamflow_control(core, false);
+	xsdirx_core_disable(core);
+	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
+	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
+	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
+	xsdirx_globalintr(core, true);
+	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
+
+	/* Initialize V4L2 subdevice and media entity */
+	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
+
+	/* Initialize the default format */
+	xsdirxss->default_format.code = xsdirxss->vip_format->code;
+	xsdirxss->default_format.field = V4L2_FIELD_NONE;
+	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
+	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
+	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
+
+	xsdirxss->formats[0] = xsdirxss->default_format;
+
+	/* Initialize V4L2 subdevice and media entity */
+	subdev = &xsdirxss->subdev;
+	v4l2_subdev_init(subdev, &xsdirxss_ops);
+
+	subdev->dev = &pdev->dev;
+	subdev->internal_ops = &xsdirxss_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 = &xsdirxss_media_ops;
+
+	v4l2_set_subdevdata(subdev, xsdirxss);
+
+	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
+	if (ret < 0)
+		goto error;
+
+	/* Initialise and register the controls */
+	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
+
+	if (xsdirxss->core.include_edh)
+		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
+
+	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
+			       (num_ctrls + num_edh_ctrls));
+
+	for (i = 0; i < num_ctrls; i++) {
+		struct v4l2_ctrl *ctrl;
+
+		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
+			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
+
+		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
+					    &xsdirxss_ctrls[i], NULL);
+		if (!ctrl) {
+			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
+				xsdirxss_ctrls[i].name);
+			goto error;
+		}
+	}
+
+	if (xsdirxss->core.include_edh) {
+		for (i = 0; i < num_edh_ctrls; i++) {
+			struct v4l2_ctrl *ctrl;
+
+			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
+				i, xsdirxss_edh_ctrls[i].name,
+				xsdirxss_edh_ctrls[i].id);
+
+			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
+						    &xsdirxss_edh_ctrls[i],
+						    NULL);
+			if (!ctrl) {
+				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
+					xsdirxss_edh_ctrls[i].name);
+				goto error;
+			}
+		}
+	} else {
+		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");
+	}
+
+	if (xsdirxss->ctrl_handler.error) {
+		dev_err(&pdev->dev, "failed to add controls\n");
+		ret = xsdirxss->ctrl_handler.error;
+		goto error;
+	}
+
+	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
+
+	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to set controls\n");
+		goto error;
+	}
+
+	platform_set_drvdata(pdev, xsdirxss);
+
+	ret = v4l2_async_register_subdev(subdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register subdev\n");
+		goto error;
+	}
+
+	xsdirxss->streaming = false;
+
+	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
+
+	xsdirx_core_enable(core);
+
+	return 0;
+error:
+	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
+	media_entity_cleanup(&subdev->entity);
+
+clk_err:
+	clk_disable_unprepare(core->vidout_clk);
+vidout_clk_err:
+	clk_disable_unprepare(core->sdirx_clk);
+rx_clk_err:
+	clk_disable_unprepare(core->axi_clk);
+	return ret;
+}
+
+static int xsdirxss_remove(struct platform_device *pdev)
+{
+	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
+	struct v4l2_subdev *subdev = &xsdirxss->subdev;
+
+	v4l2_async_unregister_subdev(subdev);
+	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
+	media_entity_cleanup(&subdev->entity);
+	clk_disable_unprepare(xsdirxss->core.vidout_clk);
+	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
+	clk_disable_unprepare(xsdirxss->core.axi_clk);
+	return 0;
+}
+
+static const struct of_device_id xsdirxss_of_id_table[] = {
+	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
+
+static struct platform_driver xsdirxss_driver = {
+	.driver = {
+		.name		= "xilinx-sdirxss",
+		.of_match_table	= xsdirxss_of_id_table,
+	},
+	.probe			= xsdirxss_probe,
+	.remove			= xsdirxss_remove,
+};
+
+module_platform_driver(xsdirxss_driver);
+
+MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
+MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
new file mode 100644
index 0000000..983a43b
--- /dev/null
+++ b/include/uapi/linux/xilinx-sdirxss.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Xilinx SDI Rx Subsystem mode and flag definitions.
+ *
+ * Copyright (C) 2019 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
+ *
+ */
+
+#ifndef __UAPI_XILINX_SDIRXSS_H__
+#define __UAPI_XILINX_SDIRXSS_H__
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+/*
+ * This enum is used to prepare the bitmask of modes to be detected
+ */
+enum {
+	XSDIRX_MODE_SD_OFFSET = 0,
+	XSDIRX_MODE_HD_OFFSET,
+	XSDIRX_MODE_3G_OFFSET,
+	XSDIRX_MODE_6G_OFFSET,
+	XSDIRX_MODE_12GI_OFFSET,
+	XSDIRX_MODE_12GF_OFFSET,
+	XSDIRX_MODE_NUM_SUPPORTED,
+};
+
+#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
+					BIT(XSDIRX_MODE_HD_OFFSET) | \
+					BIT(XSDIRX_MODE_3G_OFFSET) | \
+					BIT(XSDIRX_MODE_6G_OFFSET) | \
+					BIT(XSDIRX_MODE_12GI_OFFSET) | \
+					BIT(XSDIRX_MODE_12GF_OFFSET))
+
+/*
+ * EDH Error Types
+ * ANC - Ancillary Data Packet Errors
+ * FF - Full Field Errors
+ * AP - Active Portion Errors
+ */
+
+#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
+#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
+#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
+#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
+#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
+#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
+#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
+#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
+#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
+#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
+#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
+#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
+#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
+#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
+#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
+#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
+
+#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
+
+#endif /* __UAPI_XILINX_SDIRXSS_H__ */
diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
index f023623..4c68a10 100644
--- a/include/uapi/linux/xilinx-v4l2-controls.h
+++ b/include/uapi/linux/xilinx-v4l2-controls.h
@@ -83,4 +83,34 @@
 /* Reset all event counters */
 #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
 
+/*
+ * Xilinx SDI Rx Subsystem
+ */
+
+/* Base ID */
+#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
+
+/* Framer Control */
+#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
+/* Video Lock Window Control */
+#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
+/* EDH Error Mask Control */
+#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
+/* Mode search Control */
+#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
+/* Get Detected Mode control */
+#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
+/* Get CRC error status */
+#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
+/* Get EDH error count control */
+#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
+/* Get EDH status control */
+#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
+/* Get Transport Interlaced status */
+#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
+/* Get Active Streams count */
+#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
+/* Is Mode 3GB */
+#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
+
 #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
index 2aa357c..7b47595 100644
--- a/include/uapi/linux/xilinx-v4l2-events.h
+++ b/include/uapi/linux/xilinx-v4l2-events.h
@@ -18,4 +18,13 @@
 /* Stream Line Buffer full */
 #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
 
+/* Xilinx SMPTE UHD-SDI RX Subsystem */
+#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
+/* Video Unlock event */
+#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
+/* Video in to AXI4 Stream core underflowed */
+#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
+/* Video in to AXI4 Stream core overflowed */
+#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
+
 #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-04 13:55   ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-04 13:55 UTC (permalink / raw)
  To: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland
  Cc: Sandip Kothari, devicetree, linux-kernel, Vishal Sagar,
	Dinesh Kumar, linux-arm-kernel, linux-media

The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
streams from SDI sources like SDI broadcast equipment like cameras and
mixers. This block outputs either native SDI, native video or
AXI4-Stream compliant data stream for further processing. Please refer
to PG290 for details.

The driver is used to configure the IP to add framer, search for
specific modes, get the detected mode, stream parameters, errors, etc.
It also generates events for video lock/unlock, bridge over/under flow.

The driver supports only 10 bpc YUV 422 media bus format. It also
decodes the stream parameters based on the ST352 packet embedded in the
stream. In case the ST352 packet isn't present in the stream, the core's
detected properties are used to set stream properties.

The driver currently supports only the AXI4-Stream configuration.

Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
---
 drivers/media/platform/xilinx/Kconfig          |   11 +
 drivers/media/platform/xilinx/Makefile         |    1 +
 drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
 include/uapi/linux/xilinx-sdirxss.h            |   63 +
 include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
 include/uapi/linux/xilinx-v4l2-events.h        |    9 +
 6 files changed, 1960 insertions(+)
 create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
 create mode 100644 include/uapi/linux/xilinx-sdirxss.h

diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
index cd1a0fd..0c68caa 100644
--- a/drivers/media/platform/xilinx/Kconfig
+++ b/drivers/media/platform/xilinx/Kconfig
@@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
 	  Bridge. The driver is used to set the number of active lanes and
 	  get short packet data.
 
+config VIDEO_XILINX_SDIRXSS
+	tristate "Xilinx UHD SDI Rx Subsystem"
+	help
+	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
+	  based driver that takes input from a SDI source like SDI camera and
+	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
+	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
+	  AXI4-Stream bridge. The driver is used to set different stream
+	  detection modes and identify stream properties to properly configure
+	  downstream.
+
 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 6119a34..223f2ea 100644
--- a/drivers/media/platform/xilinx/Makefile
+++ b/drivers/media/platform/xilinx/Makefile
@@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
new file mode 100644
index 0000000..ba2d9d0
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
@@ -0,0 +1,1846 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Xilinx SDI Rx Subsystem
+ *
+ * Copyright (C) 2017 - 2019 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
+ *
+ */
+
+#include <dt-bindings/media/xilinx-vip.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/clk.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/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-sdirxss.h>
+#include <linux/xilinx-v4l2-events.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"
+
+/*
+ * SDI Rx register map, bitmask and offsets
+ */
+#define XSDIRX_RST_CTRL_REG		0x00
+#define XSDIRX_MDL_CTRL_REG		0x04
+#define XSDIRX_GLBL_IER_REG		0x0C
+#define XSDIRX_ISR_REG			0x10
+#define XSDIRX_IER_REG			0x14
+#define XSDIRX_ST352_VALID_REG		0x18
+#define XSDIRX_ST352_DS1_REG		0x1C
+#define XSDIRX_ST352_DS3_REG		0x20
+#define XSDIRX_ST352_DS5_REG		0x24
+#define XSDIRX_ST352_DS7_REG		0x28
+#define XSDIRX_ST352_DS9_REG		0x2C
+#define XSDIRX_ST352_DS11_REG		0x30
+#define XSDIRX_ST352_DS13_REG		0x34
+#define XSDIRX_ST352_DS15_REG		0x38
+#define XSDIRX_VERSION_REG		0x3C
+#define XSDIRX_SS_CONFIG_REG		0x40
+#define XSDIRX_MODE_DET_STAT_REG	0x44
+#define XSDIRX_TS_DET_STAT_REG		0x48
+#define XSDIRX_EDH_STAT_REG		0x4C
+#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
+#define XSDIRX_EDH_ERRCNT_REG		0x54
+#define XSDIRX_CRC_ERRCNT_REG		0x58
+#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
+#define XSDIRX_SB_RX_STS_REG		0x60
+
+#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
+#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
+#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
+#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
+#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
+#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
+
+#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
+#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
+#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
+#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
+#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
+#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
+#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
+#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
+#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
+
+#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
+#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
+
+#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
+
+#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
+#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
+#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
+#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
+
+#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
+				XSDIRX_INTR_VIDUNLOCK_MASK |\
+				XSDIRX_INTR_OVERFLOW_MASK |\
+				XSDIRX_INTR_UNDERFLOW_MASK)
+
+#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
+#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
+#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
+#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
+#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
+#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
+#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
+#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
+
+#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
+#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
+#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
+#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
+#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
+
+#define XSDIRX_ACTIVE_STREAMS_1		0x0
+#define XSDIRX_ACTIVE_STREAMS_2		0x1
+#define XSDIRX_ACTIVE_STREAMS_4		0x2
+#define XSDIRX_ACTIVE_STREAMS_8		0x3
+#define XSDIRX_ACTIVE_STREAMS_16	0x4
+
+#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
+#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
+#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
+#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
+#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
+#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
+#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
+
+#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
+#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
+#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
+#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
+#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
+#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
+#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
+#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
+#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
+#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
+#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
+
+#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
+#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
+#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
+#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
+#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
+#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
+#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
+
+#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
+
+#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
+#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
+
+#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
+#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
+#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
+#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
+#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
+
+#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
+
+#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
+#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
+#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
+#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
+
+/* Number of media pads */
+#define XSDIRX_MEDIA_PADS	(1)
+
+#define XSDIRX_DEFAULT_WIDTH	(1920)
+#define XSDIRX_DEFAULT_HEIGHT	(1080)
+
+#define XSDIRX_MAX_STR_LENGTH	16
+
+#define XSDIRXSS_SDI_STD_3G		0
+#define XSDIRXSS_SDI_STD_6G		1
+#define XSDIRXSS_SDI_STD_12G_8DS	2
+
+#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
+
+#define XSDIRX_MODE_HD_MASK	0x0
+#define XSDIRX_MODE_SD_MASK	0x1
+#define XSDIRX_MODE_3G_MASK	0x2
+#define XSDIRX_MODE_6G_MASK	0x4
+#define XSDIRX_MODE_12GI_MASK	0x5
+#define XSDIRX_MODE_12GF_MASK	0x6
+
+/*
+ * Maximum number of events per file handle.
+ */
+#define XSDIRX_MAX_EVENTS	(128)
+
+/* ST352 related macros */
+#define XST352_PAYLOAD_BYTE_MASK	0xFF
+#define XST352_PAYLOAD_BYTE1_SHIFT	0
+#define XST352_PAYLOAD_BYTE2_SHIFT	8
+#define XST352_PAYLOAD_BYTE3_SHIFT	16
+#define XST352_PAYLOAD_BYTE4_SHIFT	24
+
+#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
+#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
+#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
+#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
+#define XST352_BYTE1_ST372_DL_3GB		0x8A
+#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
+#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
+#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
+#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
+#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
+
+#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
+#define XST352_BYTE2_TS_TYPE_OFFSET		15
+#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
+#define XST352_BYTE2_PIC_TYPE_OFFSET		14
+#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
+#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
+
+#define XST352_BYTE2_FPS_MASK			0xF
+#define XST352_BYTE2_FPS_SHIFT			8
+#define XST352_BYTE2_FPS_24F			0x2
+#define XST352_BYTE2_FPS_24			0x3
+#define XST352_BYTE2_FPS_48F			0x4
+#define XST352_BYTE2_FPS_25			0x5
+#define XST352_BYTE2_FPS_30F			0x6
+#define XST352_BYTE2_FPS_30			0x7
+#define XST352_BYTE2_FPS_48			0x8
+#define XST352_BYTE2_FPS_50			0x9
+#define XST352_BYTE2_FPS_60F			0xA
+#define XST352_BYTE2_FPS_60			0xB
+/* Table 4 ST 2081-10:2015 */
+#define XST352_BYTE2_FPS_96			0xC
+#define XST352_BYTE2_FPS_100			0xD
+#define XST352_BYTE2_FPS_120			0xE
+#define XST352_BYTE2_FPS_120F			0xF
+
+#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
+#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
+
+#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
+#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
+#define XST352_BYTE3_COLOR_FORMAT_422		0x0
+#define XST352_BYTE3_COLOR_FORMAT_420		0x3
+
+/**
+ * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
+ * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
+ * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
+ * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
+ * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
+ * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
+ * @XSDIRX_PAL: PAL encoding detected with AP 720x576
+ * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
+ */
+enum sdi_family_enc {
+	XSDIRX_SMPTE_ST_274	= 0,
+	XSDIRX_SMPTE_ST_296	= 1,
+	XSDIRX_SMPTE_ST_2048_2	= 2,
+	XSDIRX_SMPTE_ST_295	= 3,
+	XSDIRX_NTSC		= 8,
+	XSDIRX_PAL		= 9,
+	XSDIRX_TS_UNKNOWN	= 15
+};
+
+/**
+ * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
+ * @dev: Platform structure
+ * @iomem: Base address of subsystem
+ * @irq: requested irq number
+ * @include_edh: EDH processor presence
+ * @mode: 3G/6G/12G mode
+ * @axi_clk: Axi lite interface clock
+ * @sdirx_clk: SDI Rx GT clock
+ * @vidout_clk: Video clock
+ */
+struct xsdirxss_core {
+	struct device *dev;
+	void __iomem *iomem;
+	int irq;
+	bool include_edh;
+	int mode;
+	struct clk *axi_clk;
+	struct clk *sdirx_clk;
+	struct clk *vidout_clk;
+};
+
+/**
+ * struct xsdirxss_state - SDI Rx Subsystem device structure
+ * @core: Core structure for MIPI SDI Rx Subsystem
+ * @subdev: The v4l2 subdev structure
+ * @ctrl_handler: control handler
+ * @event: Holds the video unlock event
+ * @formats: Active V4L2 formats on each pad
+ * @default_format: default V4L2 media bus format
+ * @frame_interval: Captures the frame rate
+ * @vip_format: format information corresponding to the active format
+ * @pads: media pads
+ * @streaming: Flag for storing streaming state
+ * @vidlocked: Flag indicating SDI Rx has locked onto video stream
+ * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
+ *
+ * This structure contains the device driver related parameters
+ */
+struct xsdirxss_state {
+	struct xsdirxss_core core;
+	struct v4l2_subdev subdev;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_event event;
+	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
+	struct v4l2_mbus_framefmt default_format;
+	struct v4l2_fract frame_interval;
+	const struct xvip_video_format *vip_format;
+	struct media_pad pads[XSDIRX_MEDIA_PADS];
+	bool streaming;
+	bool vidlocked;
+	bool ts_is_interlaced;
+};
+
+static inline struct xsdirxss_state *
+to_xsdirxssstate(struct v4l2_subdev *subdev)
+{
+	return container_of(subdev, struct xsdirxss_state, subdev);
+}
+
+/*
+ * Register related operations
+ */
+static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
+{
+	return ioread32(xsdirxss->iomem + addr);
+}
+
+static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
+				  u32 value)
+{
+	iowrite32(value, xsdirxss->iomem + addr);
+}
+
+static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
+				u32 clr)
+{
+	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
+}
+
+static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
+				u32 set)
+{
+	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
+}
+
+static void xsdirx_core_disable(struct xsdirxss_core *core)
+{
+	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
+}
+
+static void xsdirx_core_enable(struct xsdirxss_core *core)
+{
+	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
+}
+
+static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
+{
+	u32 i, val;
+
+	mask &= XSDIRX_DETECT_ALL_MODES;
+	if (!mask) {
+		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
+		return -EINVAL;
+	}
+
+	dev_dbg(core->dev, "mask = 0x%x\n", mask);
+
+	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
+	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
+	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
+	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
+
+	if (hweight16(mask) > 1) {
+		/* Multi mode detection as more than 1 bit set in mask */
+		dev_dbg(core->dev, "Detect multiple modes\n");
+		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
+			switch (mask & (1 << i)) {
+			case BIT(XSDIRX_MODE_SD_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_HD_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_3G_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_6G_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_12GI_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
+				break;
+			case BIT(XSDIRX_MODE_12GF_OFFSET):
+				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
+				break;
+			}
+		}
+		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
+	} else {
+		/* Fixed Mode */
+		u32 forced_mode_mask = 0;
+
+		dev_dbg(core->dev, "Detect fixed mode\n");
+
+		/* Find offset of first bit set */
+		switch (__ffs(mask)) {
+		case XSDIRX_MODE_SD_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_SD_MASK;
+			break;
+		case XSDIRX_MODE_HD_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_HD_MASK;
+			break;
+		case XSDIRX_MODE_3G_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_3G_MASK;
+			break;
+		case XSDIRX_MODE_6G_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_6G_MASK;
+			break;
+		case XSDIRX_MODE_12GI_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
+			break;
+		case XSDIRX_MODE_12GF_OFFSET:
+			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
+			break;
+		}
+		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
+			forced_mode_mask);
+		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
+	}
+
+	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
+		val);
+	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
+
+	return 0;
+}
+
+static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
+{
+	if (flag)
+		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
+			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
+	else
+		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
+			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
+}
+
+static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
+{
+	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
+
+	val = enable & XSDIRX_EDH_ALLERR_MASK;
+
+	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
+}
+
+static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
+{
+	/*
+	 * The video lock window is the amount of time for which the
+	 * the mode and transport stream should be locked to get the
+	 * video lock interrupt.
+	 */
+	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
+}
+
+static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
+{
+	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
+}
+
+static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
+{
+	xsdirxss_set(core, XSDIRX_IER_REG, mask);
+}
+
+static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
+{
+	if (flag)
+		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
+			     XSDIRX_GLBL_INTR_EN_MASK);
+	else
+		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
+			     XSDIRX_GLBL_INTR_EN_MASK);
+}
+
+static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
+{
+	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
+}
+
+static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
+{
+	if (enable)
+		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
+	else
+		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
+}
+
+static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
+{
+	if (enable)
+		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
+	else
+		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
+}
+
+static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
+{
+	/* The sdi to native bridge is followed by native to axis4 bridge */
+	if (enable) {
+		xsdirx_axis4_bridge_control(core, enable);
+		xsdirx_vid_bridge_control(core, enable);
+	} else {
+		xsdirx_vid_bridge_control(core, enable);
+		xsdirx_axis4_bridge_control(core, enable);
+	}
+}
+
+static void xsdirx_streamdowncb(struct xsdirxss_core *core)
+{
+	xsdirx_streamflow_control(core, false);
+}
+
+static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
+				   u32 framerate)
+{
+	switch (framerate) {
+	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
+		frame_interval->numerator = 1001;
+		frame_interval->denominator = 24000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_24HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 24000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_25HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 25000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
+		frame_interval->numerator = 1001;
+		frame_interval->denominator = 30000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_30HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 30000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
+		frame_interval->numerator = 1001;
+		frame_interval->denominator = 48000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_48HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 48000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_50HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 50000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
+		frame_interval->numerator = 1001;
+		frame_interval->denominator = 60000;
+		break;
+	case XSDIRX_TS_DET_STAT_RATE_60HZ:
+		frame_interval->numerator = 1000;
+		frame_interval->denominator = 60000;
+		break;
+	default:
+		frame_interval->numerator = 1;
+		frame_interval->denominator = 1;
+	}
+}
+
+/**
+ * xsdirx_get_stream_properties - Get SDI Rx stream properties
+ * @state: pointer to driver state
+ *
+ * This function decodes the stream's ST352 payload (if available) to get
+ * stream properties like width, height, picture type (interlaced/progressive),
+ * etc.
+ *
+ * Return: 0 for success else errors
+ */
+static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
+{
+	struct xsdirxss_core *core = &state->core;
+	u32 mode, payload = 0, val, family, valid, tscan;
+	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
+	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
+	struct v4l2_mbus_framefmt *format = &state->formats[0];
+
+	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+
+	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
+
+	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
+		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
+		return -EINVAL;
+	}
+
+	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
+	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
+		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
+		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
+				XST352_PAYLOAD_BYTE_MASK;
+		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
+				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
+		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
+				XST352_BYTE2_PIC_TYPE_OFFSET;
+		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
+				XST352_BYTE2_FPS_MASK;
+		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
+				XST352_BYTE2_TS_TYPE_OFFSET;
+		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
+			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
+	} else {
+		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
+			mode);
+		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
+				XSDIRX_TS_DET_STAT_RATE_OFFSET;
+		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
+				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
+	}
+
+	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
+		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
+	state->ts_is_interlaced = tscan ? false : true;
+
+	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
+		state->ts_is_interlaced, family);
+
+	switch (mode) {
+	case XSDIRX_MODE_HD_MASK:
+		if (!valid) {
+			/* No payload obtained */
+			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
+				framerate, tscan);
+			/*
+			 * NOTE : A progressive segmented frame pSF will be
+			 * reported incorrectly as Interlaced as we rely on IP's
+			 * transport scan locked bit.
+			 */
+			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
+
+			switch (framerate) {
+			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
+			case XSDIRX_TS_DET_STAT_RATE_24HZ:
+			case XSDIRX_TS_DET_STAT_RATE_25HZ:
+			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
+			case XSDIRX_TS_DET_STAT_RATE_30HZ:
+				if (family == XSDIRX_SMPTE_ST_296) {
+					format->width = 1280;
+					format->height = 720;
+					format->field = V4L2_FIELD_NONE;
+				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
+					format->width = 2048;
+					format->height = 1080;
+					if (tscan)
+						format->field = V4L2_FIELD_NONE;
+					else
+						format->field =
+							V4L2_FIELD_ALTERNATE;
+				} else {
+					format->width = 1920;
+					format->height = 1080;
+					if (tscan)
+						format->field = V4L2_FIELD_NONE;
+					else
+						format->field =
+							V4L2_FIELD_ALTERNATE;
+				}
+				break;
+			case XSDIRX_TS_DET_STAT_RATE_50HZ:
+			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
+			case XSDIRX_TS_DET_STAT_RATE_60HZ:
+				if (family == XSDIRX_SMPTE_ST_274) {
+					format->width = 1920;
+					format->height = 1080;
+				} else {
+					format->width = 1280;
+					format->height = 720;
+				}
+				format->field = V4L2_FIELD_NONE;
+				break;
+			default:
+				format->width = 1920;
+				format->height = 1080;
+				format->field = V4L2_FIELD_NONE;
+			}
+		} else {
+			dev_dbg(core->dev, "Got the payload\n");
+			switch (byte1) {
+			case XST352_BYTE1_ST292_1x720L_1_5G:
+				/* SMPTE ST 292-1 for 720 line payloads */
+				format->width = 1280;
+				format->height = 720;
+				break;
+			case XST352_BYTE1_ST292_1x1080L_1_5G:
+				/* SMPTE ST 292-1 for 1080 line payloads */
+				format->height = 1080;
+				if (active_luma)
+					format->width = 2048;
+				else
+					format->width = 1920;
+				break;
+			default:
+				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
+				return -EINVAL;
+			}
+		}
+		break;
+	case XSDIRX_MODE_SD_MASK:
+		format->field = V4L2_FIELD_ALTERNATE;
+
+		switch (family) {
+		case XSDIRX_NTSC:
+			format->width = 720;
+			format->height = 486;
+			break;
+		case XSDIRX_PAL:
+			format->width = 720;
+			format->height = 576;
+			break;
+		default:
+			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
+			return -EINVAL;
+		}
+		break;
+	case XSDIRX_MODE_3G_MASK:
+		switch (byte1) {
+		case XST352_BYTE1_ST425_2008_750L_3GB:
+			/* Sec 4.1.6.1 SMPTE 425-2008 */
+		case XST352_BYTE1_ST372_2x720L_3GB:
+			/* Table 13 SMPTE 425-2008 */
+			format->width = 1280;
+			format->height = 720;
+			break;
+		case XST352_BYTE1_ST425_2008_1125L_3GA:
+			/* ST352 Table SMPTE 425-1 */
+		case XST352_BYTE1_ST372_DL_3GB:
+			/* Table 13 SMPTE 425-2008 */
+		case XST352_BYTE1_ST372_2x1080L_3GB:
+			/* Table 13 SMPTE 425-2008 */
+			format->height = 1080;
+			if (active_luma)
+				format->width = 2048;
+			else
+				format->width = 1920;
+			break;
+		default:
+			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
+			return -EINVAL;
+		}
+		break;
+	case XSDIRX_MODE_6G_MASK:
+		switch (byte1) {
+		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
+			/* Dual link 6G */
+		case XST352_BYTE1_ST2081_10_2160L_6G:
+			/* Table 3 SMPTE ST 2081-10 */
+			format->height = 2160;
+			if (active_luma)
+				format->width = 4096;
+			else
+				format->width = 3840;
+			break;
+		default:
+			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
+			return -EINVAL;
+		}
+		break;
+	case XSDIRX_MODE_12GI_MASK:
+	case XSDIRX_MODE_12GF_MASK:
+		switch (byte1) {
+		case XST352_BYTE1_ST2082_10_2160L_12G:
+			/* Section 4.3.1 SMPTE ST 2082-10 */
+			format->height = 2160;
+			if (active_luma)
+				format->width = 4096;
+			else
+				format->width = 3840;
+			break;
+		default:
+			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
+			return -EINVAL;
+		}
+		break;
+	default:
+		dev_err(core->dev, "Invalid Mode\n");
+		return -EINVAL;
+	}
+
+	if (valid) {
+		if (pic_type)
+			format->field = V4L2_FIELD_NONE;
+		else
+			format->field = V4L2_FIELD_ALTERNATE;
+	}
+
+	if (format->field == V4L2_FIELD_ALTERNATE)
+		format->height = format->height / 2;
+
+	switch (sampling) {
+	case XST352_BYTE3_COLOR_FORMAT_422:
+		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
+		break;
+	default:
+		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
+		return -EINVAL;
+	}
+
+	xsdirxss_get_framerate(&state->frame_interval, framerate);
+
+	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
+		format->width, format->height, format->field, payload, val);
+	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
+		state->frame_interval.numerator,
+		state->frame_interval.denominator);
+	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
+	return 0;
+}
+
+/**
+ * xsdirxss_irq_handler - Interrupt handler for SDI Rx
+ * @irq: IRQ number
+ * @dev_id: Pointer to device state
+ *
+ * The SDI Rx interrupts are cleared by first setting and then clearing the bits
+ * in the interrupt clear register. The interrupt status register is read only.
+ *
+ * Return: IRQ_HANDLED after handling interrupts
+ */
+static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
+{
+	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
+	struct xsdirxss_core *core = &state->core;
+	u32 status;
+
+	status = xsdirxss_read(core, XSDIRX_ISR_REG);
+	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
+
+	if (!status)
+		return IRQ_NONE;
+
+	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
+		u32 val1, val2;
+
+		dev_dbg(core->dev, "video lock interrupt\n");
+		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
+
+		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
+
+		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
+		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
+			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
+				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
+
+			dev_dbg(core->dev, "mode & ts lock occurred\n");
+
+			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
+			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
+
+			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
+			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
+
+			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
+			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
+
+			if (!xsdirx_get_stream_properties(state)) {
+				memset(&state->event, 0, sizeof(state->event));
+				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
+				state->event.u.src_change.changes =
+					V4L2_EVENT_SRC_CH_RESOLUTION;
+				v4l2_subdev_notify_event(&state->subdev,
+							 &state->event);
+
+				state->vidlocked = true;
+			} else {
+				dev_err(core->dev, "Unable to get stream properties!\n");
+				state->vidlocked = false;
+			}
+		} else {
+			dev_dbg(core->dev, "video unlock before video lock!\n");
+			state->vidlocked = false;
+		}
+	}
+
+	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
+		dev_dbg(core->dev, "video unlock interrupt\n");
+		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
+		xsdirx_streamdowncb(core);
+
+		memset(&state->event, 0, sizeof(state->event));
+		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
+		v4l2_subdev_notify_event(&state->subdev, &state->event);
+
+		state->vidlocked = false;
+	}
+
+	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
+		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
+		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
+
+		memset(&state->event, 0, sizeof(state->event));
+		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
+		v4l2_subdev_notify_event(&state->subdev, &state->event);
+	}
+
+	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
+		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
+		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
+
+		memset(&state->event, 0, sizeof(state->event));
+		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
+		v4l2_subdev_notify_event(&state->subdev, &state->event);
+	}
+	return IRQ_HANDLED;
+}
+
+/**
+ * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
+ * @sd: V4L2 Sub device
+ * @fh: V4L2 File Handle
+ * @sub: Subcribe event structure
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
+				    struct v4l2_fh *fh,
+				    struct v4l2_event_subscription *sub)
+{
+	int ret;
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	switch (sub->type) {
+	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
+	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
+	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
+		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
+		break;
+	case V4L2_EVENT_SOURCE_CHANGE:
+		ret = v4l2_src_change_event_subscribe(fh, sub);
+		break;
+	default:
+		return -EINVAL;
+	}
+	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
+	return ret;
+}
+
+/**
+ * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
+				      struct v4l2_fh *fh,
+				      struct v4l2_event_subscription *sub)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
+	return v4l2_event_unsubscribe(fh, sub);
+}
+
+/**
+ * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
+ * @ctrl: V4L2 control to be set
+ *
+ * This function is used to set the V4L2 controls for the Xilinx SDI Rx
+ * Subsystem.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	int ret = 0;
+	struct xsdirxss_state *xsdirxss =
+		container_of(ctrl->handler,
+			     struct xsdirxss_state, ctrl_handler);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
+		ctrl->id, ctrl->val);
+
+	if (xsdirxss->streaming) {
+		dev_err(core->dev, "Cannot set controls while streaming\n");
+		return -EINVAL;
+	}
+
+	xsdirx_core_disable(core);
+	switch (ctrl->id) {
+	case V4L2_CID_XILINX_SDIRX_FRAMER:
+		xsdirx_framer(core, ctrl->val);
+		break;
+	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
+		xsdirx_setvidlockwindow(core, ctrl->val);
+		break;
+	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
+		xsdirx_setedherrcnttrigger(core, ctrl->val);
+		break;
+	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
+		if (ctrl->val) {
+			if (core->mode == XSDIRXSS_SDI_STD_3G) {
+				dev_dbg(core->dev, "Upto 3G supported\n");
+				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
+					       BIT(XSDIRX_MODE_12GI_OFFSET) |
+					       BIT(XSDIRX_MODE_12GF_OFFSET));
+			}
+
+			if (core->mode == XSDIRXSS_SDI_STD_6G) {
+				dev_dbg(core->dev, "Upto 6G supported\n");
+				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
+					       BIT(XSDIRX_MODE_12GF_OFFSET));
+			}
+
+			ret = xsdirx_set_modedetect(core, ctrl->val);
+		} else {
+			dev_err(core->dev, "Select at least one mode!\n");
+			return -EINVAL;
+		}
+		break;
+	default:
+		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
+			     XSDIRX_RST_CTRL_SS_EN_MASK);
+		return -EINVAL;
+	}
+	xsdirx_core_enable(core);
+	return ret;
+}
+
+/**
+ * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
+ * @ctrl: Pointer to V4L2 control
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	u32 val;
+	struct xsdirxss_state *xsdirxss =
+		container_of(ctrl->handler,
+			     struct xsdirxss_state, ctrl_handler);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	switch (ctrl->id) {
+	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
+		if (!xsdirxss->vidlocked) {
+			dev_err(core->dev, "Can't get values when video not locked!\n");
+			return -EINVAL;
+		}
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+
+		switch (val) {
+		case XSDIRX_MODE_SD_MASK:
+			ctrl->val = XSDIRX_MODE_SD_OFFSET;
+			break;
+		case XSDIRX_MODE_HD_MASK:
+			ctrl->val = XSDIRX_MODE_HD_OFFSET;
+			break;
+		case XSDIRX_MODE_3G_MASK:
+			ctrl->val = XSDIRX_MODE_3G_OFFSET;
+			break;
+		case XSDIRX_MODE_6G_MASK:
+			ctrl->val = XSDIRX_MODE_6G_OFFSET;
+			break;
+		case XSDIRX_MODE_12GI_MASK:
+			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
+			break;
+		case XSDIRX_MODE_12GF_MASK:
+			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
+			break;
+		}
+		break;
+	case V4L2_CID_XILINX_SDIRX_CRC:
+		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
+		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
+		break;
+	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+		if (val == XSDIRX_MODE_SD_MASK) {
+			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
+		} else {
+			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+		if (val == XSDIRX_MODE_SD_MASK) {
+			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
+		} else {
+			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
+			return -EINVAL;
+		}
+		break;
+	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
+		if (!xsdirxss->vidlocked) {
+			dev_err(core->dev, "Can't get values when video not locked!\n");
+			return -EINVAL;
+		}
+		ctrl->val = xsdirxss->ts_is_interlaced;
+		break;
+	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
+		if (!xsdirxss->vidlocked) {
+			dev_err(core->dev, "Can't get values when video not locked!\n");
+			return -EINVAL;
+		}
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
+		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
+		ctrl->val = 1 << val;
+		break;
+	case V4L2_CID_XILINX_SDIRX_IS_3GB:
+		if (!xsdirxss->vidlocked) {
+			dev_err(core->dev, "Can't get values when video not locked!\n");
+			return -EINVAL;
+		}
+		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
+		ctrl->val = val ? true : false;
+		break;
+	default:
+		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
+		return -EINVAL;
+	}
+	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
+		ctrl->id, ctrl->val);
+	return 0;
+}
+
+/**
+ * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
+ * @sd: Pointer to V4L2 subdevice structure
+ *
+ * This function prints the current status of Xilinx SDI Rx Subsystem
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_log_status(struct v4l2_subdev *sd)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+	u32 data, i;
+
+	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
+	for (i = 0; i < 0x28; i++) {
+		data = xsdirxss_read(core, i * 4);
+		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
+			  i * 4, data);
+	}
+	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
+	return 0;
+}
+
+static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
+{
+	xsdirx_streamflow_control(&xsdirxss->core, true);
+}
+
+static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
+{
+	xsdirx_streamflow_control(&xsdirxss->core, false);
+}
+
+/**
+ * xsdirxss_g_frame_interval - Get the frame interval
+ * @sd: V4L2 Sub device
+ * @fi: Pointer to V4l2 Sub device frame interval structure
+ *
+ * This function is used to get the frame interval.
+ * The frame rate can be integral or fractional.
+ * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
+ * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
+				     struct v4l2_subdev_frame_interval *fi)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	if (!xsdirxss->vidlocked) {
+		dev_err(core->dev, "Video not locked!\n");
+		return -EINVAL;
+	}
+
+	fi->interval = xsdirxss->frame_interval;
+
+	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
+		xsdirxss->frame_interval.numerator,
+		xsdirxss->frame_interval.denominator);
+	return 0;
+}
+
+/**
+ * xsdirxss_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 SDI Rx Subsystem.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	if (enable) {
+		if (!xsdirxss->vidlocked) {
+			dev_dbg(core->dev, "Video is not locked\n");
+			return -EINVAL;
+		}
+		if (xsdirxss->streaming) {
+			dev_dbg(core->dev, "Already streaming\n");
+			return -EINVAL;
+		}
+
+		xsdirxss_start_stream(xsdirxss);
+		xsdirxss->streaming = true;
+		dev_dbg(core->dev, "Streaming started\n");
+	} else {
+		if (!xsdirxss->streaming) {
+			dev_dbg(core->dev, "Stopped streaming already\n");
+			return -EINVAL;
+		}
+
+		xsdirxss_stop_stream(xsdirxss);
+		xsdirxss->streaming = false;
+		dev_dbg(core->dev, "Streaming stopped\n");
+	}
+
+	return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
+			  struct v4l2_subdev_pad_config *cfg,
+				unsigned int pad, u32 which)
+{
+	switch (which) {
+	case V4L2_SUBDEV_FORMAT_TRY:
+		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
+	case V4L2_SUBDEV_FORMAT_ACTIVE:
+		return &xsdirxss->formats[pad];
+	default:
+		return NULL;
+	}
+}
+
+/**
+ * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+					struct v4l2_subdev_format *fmt)
+{
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+	struct xsdirxss_core *core = &xsdirxss->core;
+
+	if (!xsdirxss->vidlocked) {
+		dev_err(core->dev, "Video not locked!\n");
+		return -EINVAL;
+	}
+
+	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
+						 fmt->pad, fmt->which);
+
+	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
+		fmt->format.width, fmt->format.height, fmt->format.field);
+
+	return 0;
+}
+
+/**
+ * xsdirxss_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.
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_set_format(struct v4l2_subdev *sd,
+			       struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *__format;
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+
+	dev_dbg(xsdirxss->core.dev,
+		"set width %d height %d code %d field %d colorspace %d\n",
+		fmt->format.width, fmt->format.height,
+		fmt->format.code, fmt->format.field,
+		fmt->format.colorspace);
+
+	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
+					     fmt->pad, fmt->which);
+
+	/* Currently reset the code to one fixed in hardware */
+	/* TODO : Add checks for width height */
+	fmt->format.code = __format->code;
+
+	return 0;
+}
+
+/**
+ * xsdirxss_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 pad.
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_open(struct v4l2_subdev *sd,
+			 struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format;
+	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+
+	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	*format = xsdirxss->default_format;
+
+	return 0;
+}
+
+/**
+ * xsdirxss_close - Called on v4l2_close()
+ * @sd: Pointer to V4L2 sub device structure
+ * @fh: Pointer to V4L2 File handle
+ *
+ * This function is called on v4l2_close().
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_close(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_fh *fh)
+{
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static const struct media_entity_operations xsdirxss_media_ops = {
+	.link_validate = v4l2_subdev_link_validate
+};
+
+static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
+	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
+	.s_ctrl	= xsdirxss_s_ctrl
+};
+
+static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
+	{
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
+		.name	= "SDI Rx : EDH Error Count Enable",
+		.type	= V4L2_CTRL_TYPE_BITMASK,
+		.min	= 0,
+		.max	= XSDIRX_EDH_ALLERR_MASK,
+		.def	= 0,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
+		.name	= "SDI Rx : EDH Error Count",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= 0xFFFF,
+		.step	= 1,
+		.def	= 0,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
+		.name	= "SDI Rx : EDH Status",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= 0xFFFFFFFF,
+		.step	= 1,
+		.def	= 0,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}
+};
+
+static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
+	{
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
+		.name	= "SDI Rx : Enable Framer",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.step	= 1,
+		.def	= true,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
+		.name	= "SDI Rx : Video Lock Window",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= 0xFFFFFFFF,
+		.step	= 1,
+		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
+		.name	= "SDI Rx : Modes search Mask",
+		.type	= V4L2_CTRL_TYPE_BITMASK,
+		.min	= 0,
+		.max	= XSDIRX_DETECT_ALL_MODES,
+		.def	= XSDIRX_DETECT_ALL_MODES,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
+		.name	= "SDI Rx : Mode Detect Status",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= XSDIRX_MODE_SD_OFFSET,
+		.max	= XSDIRX_MODE_12GF_OFFSET,
+		.step	= 1,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_CRC,
+		.name	= "SDI Rx : CRC Error status",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 0,
+		.max	= 0xFFFFFFFF,
+		.step	= 1,
+		.def	= 0,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
+		.name	= "SDI Rx : TS is Interlaced",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.def	= false,
+		.step	= 1,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
+		.name	= "SDI Rx : Active Streams",
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.min	= 1,
+		.max	= 16,
+		.def	= 1,
+		.step	= 1,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}, {
+		.ops	= &xsdirxss_ctrl_ops,
+		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
+		.name	= "SDI Rx : Is 3GB",
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.min	= false,
+		.max	= true,
+		.def	= false,
+		.step	= 1,
+		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	}
+};
+
+static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
+	.log_status = xsdirxss_log_status,
+	.subscribe_event = xsdirxss_subscribe_event,
+	.unsubscribe_event = xsdirxss_unsubscribe_event
+};
+
+static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
+	.g_frame_interval = xsdirxss_g_frame_interval,
+	.s_stream = xsdirxss_s_stream
+};
+
+static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
+	.get_fmt = xsdirxss_get_format,
+	.set_fmt = xsdirxss_set_format,
+};
+
+static struct v4l2_subdev_ops xsdirxss_ops = {
+	.core = &xsdirxss_core_ops,
+	.video = &xsdirxss_video_ops,
+	.pad = &xsdirxss_pad_ops
+};
+
+static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
+	.open = xsdirxss_open,
+	.close = xsdirxss_close
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
+{
+	struct device_node *node = xsdirxss->core.dev->of_node;
+	struct device_node *ports = NULL;
+	struct device_node *port = NULL;
+	unsigned int nports = 0;
+	struct xsdirxss_core *core = &xsdirxss->core;
+	int ret;
+	const char *sdi_std;
+
+	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
+	dev_dbg(core->dev, "EDH property = %s\n",
+		core->include_edh ? "Present" : "Absent");
+
+	ret = of_property_read_string(node, "xlnx,line-rate",
+				      &sdi_std);
+	if (ret < 0) {
+		dev_err(core->dev, "xlnx,line-rate property not found\n");
+		return ret;
+	}
+
+	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
+		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
+	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
+		core->mode = XSDIRXSS_SDI_STD_6G;
+	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
+		core->mode = XSDIRXSS_SDI_STD_3G;
+	} else {
+		dev_err(core->dev, "Invalid Line Rate\n");
+		return -EINVAL;
+	}
+	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
+		core->mode);
+
+	ports = of_get_child_by_name(node, "ports");
+	if (!ports)
+		ports = node;
+
+	for_each_child_of_node(ports, port) {
+		const struct xvip_video_format *format;
+		struct device_node *endpoint;
+
+		if (!port->name || of_node_cmp(port->name, "port"))
+			continue;
+
+		format = xvip_of_get_format(port);
+		if (IS_ERR(format)) {
+			dev_err(core->dev, "invalid format in DT");
+			return PTR_ERR(format);
+		}
+
+		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
+			format->vf_code, format->width, format->bpp);
+
+		if (format->vf_code != XVIP_VF_YUV_422 &&
+		    format->vf_code != XVIP_VF_YUV_420) {
+			dev_err(core->dev, "Incorrect UG934 video format set.\n");
+			return -EINVAL;
+		}
+		xsdirxss->vip_format = format;
+
+		endpoint = of_get_next_child(port, NULL);
+		if (!endpoint) {
+			dev_err(core->dev, "No port at\n");
+			return -EINVAL;
+		}
+
+		/* Count the number of ports. */
+		nports++;
+	}
+
+	if (nports != 1) {
+		dev_err(core->dev, "invalid number of ports %u\n", nports);
+		return -EINVAL;
+	}
+
+	/* Register interrupt handler */
+	core->irq = irq_of_parse_and_map(node, 0);
+
+	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
+			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
+	if (ret) {
+		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int xsdirxss_probe(struct platform_device *pdev)
+{
+	struct v4l2_subdev *subdev;
+	struct xsdirxss_state *xsdirxss;
+	struct xsdirxss_core *core;
+	struct resource *res;
+	int ret;
+	unsigned int num_ctrls, num_edh_ctrls = 0, i;
+
+	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
+	if (!xsdirxss)
+		return -ENOMEM;
+
+	xsdirxss->core.dev = &pdev->dev;
+	core = &xsdirxss->core;
+
+	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
+	if (IS_ERR(core->axi_clk)) {
+		ret = PTR_ERR(core->axi_clk);
+		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
+		return ret;
+	}
+
+	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
+	if (IS_ERR(core->sdirx_clk)) {
+		ret = PTR_ERR(core->sdirx_clk);
+		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
+		return ret;
+	}
+
+	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
+	if (IS_ERR(core->vidout_clk)) {
+		ret = PTR_ERR(core->vidout_clk);
+		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(core->axi_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(core->sdirx_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
+		goto rx_clk_err;
+	}
+
+	ret = clk_prepare_enable(core->vidout_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
+		goto vidout_clk_err;
+	}
+
+	ret = xsdirxss_parse_of(xsdirxss);
+	if (ret < 0)
+		goto clk_err;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
+	if (IS_ERR(xsdirxss->core.iomem)) {
+		ret = PTR_ERR(xsdirxss->core.iomem);
+		goto clk_err;
+	}
+
+	/* Reset the core */
+	xsdirx_streamflow_control(core, false);
+	xsdirx_core_disable(core);
+	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
+	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
+	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
+	xsdirx_globalintr(core, true);
+	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
+
+	/* Initialize V4L2 subdevice and media entity */
+	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
+
+	/* Initialize the default format */
+	xsdirxss->default_format.code = xsdirxss->vip_format->code;
+	xsdirxss->default_format.field = V4L2_FIELD_NONE;
+	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
+	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
+	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
+
+	xsdirxss->formats[0] = xsdirxss->default_format;
+
+	/* Initialize V4L2 subdevice and media entity */
+	subdev = &xsdirxss->subdev;
+	v4l2_subdev_init(subdev, &xsdirxss_ops);
+
+	subdev->dev = &pdev->dev;
+	subdev->internal_ops = &xsdirxss_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 = &xsdirxss_media_ops;
+
+	v4l2_set_subdevdata(subdev, xsdirxss);
+
+	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
+	if (ret < 0)
+		goto error;
+
+	/* Initialise and register the controls */
+	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
+
+	if (xsdirxss->core.include_edh)
+		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
+
+	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
+			       (num_ctrls + num_edh_ctrls));
+
+	for (i = 0; i < num_ctrls; i++) {
+		struct v4l2_ctrl *ctrl;
+
+		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
+			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
+
+		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
+					    &xsdirxss_ctrls[i], NULL);
+		if (!ctrl) {
+			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
+				xsdirxss_ctrls[i].name);
+			goto error;
+		}
+	}
+
+	if (xsdirxss->core.include_edh) {
+		for (i = 0; i < num_edh_ctrls; i++) {
+			struct v4l2_ctrl *ctrl;
+
+			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
+				i, xsdirxss_edh_ctrls[i].name,
+				xsdirxss_edh_ctrls[i].id);
+
+			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
+						    &xsdirxss_edh_ctrls[i],
+						    NULL);
+			if (!ctrl) {
+				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
+					xsdirxss_edh_ctrls[i].name);
+				goto error;
+			}
+		}
+	} else {
+		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");
+	}
+
+	if (xsdirxss->ctrl_handler.error) {
+		dev_err(&pdev->dev, "failed to add controls\n");
+		ret = xsdirxss->ctrl_handler.error;
+		goto error;
+	}
+
+	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
+
+	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to set controls\n");
+		goto error;
+	}
+
+	platform_set_drvdata(pdev, xsdirxss);
+
+	ret = v4l2_async_register_subdev(subdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register subdev\n");
+		goto error;
+	}
+
+	xsdirxss->streaming = false;
+
+	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
+
+	xsdirx_core_enable(core);
+
+	return 0;
+error:
+	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
+	media_entity_cleanup(&subdev->entity);
+
+clk_err:
+	clk_disable_unprepare(core->vidout_clk);
+vidout_clk_err:
+	clk_disable_unprepare(core->sdirx_clk);
+rx_clk_err:
+	clk_disable_unprepare(core->axi_clk);
+	return ret;
+}
+
+static int xsdirxss_remove(struct platform_device *pdev)
+{
+	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
+	struct v4l2_subdev *subdev = &xsdirxss->subdev;
+
+	v4l2_async_unregister_subdev(subdev);
+	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
+	media_entity_cleanup(&subdev->entity);
+	clk_disable_unprepare(xsdirxss->core.vidout_clk);
+	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
+	clk_disable_unprepare(xsdirxss->core.axi_clk);
+	return 0;
+}
+
+static const struct of_device_id xsdirxss_of_id_table[] = {
+	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
+
+static struct platform_driver xsdirxss_driver = {
+	.driver = {
+		.name		= "xilinx-sdirxss",
+		.of_match_table	= xsdirxss_of_id_table,
+	},
+	.probe			= xsdirxss_probe,
+	.remove			= xsdirxss_remove,
+};
+
+module_platform_driver(xsdirxss_driver);
+
+MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
+MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
new file mode 100644
index 0000000..983a43b
--- /dev/null
+++ b/include/uapi/linux/xilinx-sdirxss.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Xilinx SDI Rx Subsystem mode and flag definitions.
+ *
+ * Copyright (C) 2019 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
+ *
+ */
+
+#ifndef __UAPI_XILINX_SDIRXSS_H__
+#define __UAPI_XILINX_SDIRXSS_H__
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+/*
+ * This enum is used to prepare the bitmask of modes to be detected
+ */
+enum {
+	XSDIRX_MODE_SD_OFFSET = 0,
+	XSDIRX_MODE_HD_OFFSET,
+	XSDIRX_MODE_3G_OFFSET,
+	XSDIRX_MODE_6G_OFFSET,
+	XSDIRX_MODE_12GI_OFFSET,
+	XSDIRX_MODE_12GF_OFFSET,
+	XSDIRX_MODE_NUM_SUPPORTED,
+};
+
+#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
+					BIT(XSDIRX_MODE_HD_OFFSET) | \
+					BIT(XSDIRX_MODE_3G_OFFSET) | \
+					BIT(XSDIRX_MODE_6G_OFFSET) | \
+					BIT(XSDIRX_MODE_12GI_OFFSET) | \
+					BIT(XSDIRX_MODE_12GF_OFFSET))
+
+/*
+ * EDH Error Types
+ * ANC - Ancillary Data Packet Errors
+ * FF - Full Field Errors
+ * AP - Active Portion Errors
+ */
+
+#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
+#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
+#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
+#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
+#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
+#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
+#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
+#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
+#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
+#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
+#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
+#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
+#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
+#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
+#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
+#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
+
+#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
+
+#endif /* __UAPI_XILINX_SDIRXSS_H__ */
diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
index f023623..4c68a10 100644
--- a/include/uapi/linux/xilinx-v4l2-controls.h
+++ b/include/uapi/linux/xilinx-v4l2-controls.h
@@ -83,4 +83,34 @@
 /* Reset all event counters */
 #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
 
+/*
+ * Xilinx SDI Rx Subsystem
+ */
+
+/* Base ID */
+#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
+
+/* Framer Control */
+#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
+/* Video Lock Window Control */
+#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
+/* EDH Error Mask Control */
+#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
+/* Mode search Control */
+#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
+/* Get Detected Mode control */
+#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
+/* Get CRC error status */
+#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
+/* Get EDH error count control */
+#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
+/* Get EDH status control */
+#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
+/* Get Transport Interlaced status */
+#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
+/* Get Active Streams count */
+#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
+/* Is Mode 3GB */
+#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
+
 #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
index 2aa357c..7b47595 100644
--- a/include/uapi/linux/xilinx-v4l2-events.h
+++ b/include/uapi/linux/xilinx-v4l2-events.h
@@ -18,4 +18,13 @@
 /* Stream Line Buffer full */
 #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
 
+/* Xilinx SMPTE UHD-SDI RX Subsystem */
+#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
+/* Video Unlock event */
+#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
+/* Video in to AXI4 Stream core underflowed */
+#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
+/* Video in to AXI4 Stream core overflowed */
+#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
+
 #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
-- 
1.8.3.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-04 13:55   ` Vishal Sagar
@ 2019-06-05 12:57     ` Hans Verkuil
  -1 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2019-06-05 12:57 UTC (permalink / raw)
  To: Vishal Sagar, Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab,
	Michal Simek, Rob Herring, Mark Rutland
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari

On 6/4/19 3:55 PM, Vishal Sagar wrote:
> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> streams from SDI sources like SDI broadcast equipment like cameras and
> mixers. This block outputs either native SDI, native video or
> AXI4-Stream compliant data stream for further processing. Please refer
> to PG290 for details.
> 
> The driver is used to configure the IP to add framer, search for
> specific modes, get the detected mode, stream parameters, errors, etc.
> It also generates events for video lock/unlock, bridge over/under flow.
> 
> The driver supports only 10 bpc YUV 422 media bus format. It also
> decodes the stream parameters based on the ST352 packet embedded in the
> stream. In case the ST352 packet isn't present in the stream, the core's
> detected properties are used to set stream properties.
> 
> The driver currently supports only the AXI4-Stream configuration.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig          |   11 +
>  drivers/media/platform/xilinx/Makefile         |    1 +
>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +

My comments for the MIPI CSI-2 Rx driver apply here as well with respect
to the naming of controls/events and documenting them.

Also, the contents of xilinx-v4l2-*.h can just be moved to xilinx-sdirxss.h.

>  6 files changed, 1960 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
>  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index cd1a0fd..0c68caa 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
>  	  Bridge. The driver is used to set the number of active lanes and
>  	  get short packet data.
>  
> +config VIDEO_XILINX_SDIRXSS
> +	tristate "Xilinx UHD SDI Rx Subsystem"
> +	help
> +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> +	  based driver that takes input from a SDI source like SDI camera and
> +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> +	  AXI4-Stream bridge. The driver is used to set different stream
> +	  detection modes and identify stream properties to properly configure
> +	  downstream.
> +
>  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 6119a34..223f2ea 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> new file mode 100644
> index 0000000..ba2d9d0
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,1846 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.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/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-sdirxss.h>
> +#include <linux/xilinx-v4l2-events.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"
> +
> +/*
> + * SDI Rx register map, bitmask and offsets
> + */
> +#define XSDIRX_RST_CTRL_REG		0x00
> +#define XSDIRX_MDL_CTRL_REG		0x04
> +#define XSDIRX_GLBL_IER_REG		0x0C
> +#define XSDIRX_ISR_REG			0x10
> +#define XSDIRX_IER_REG			0x14
> +#define XSDIRX_ST352_VALID_REG		0x18
> +#define XSDIRX_ST352_DS1_REG		0x1C
> +#define XSDIRX_ST352_DS3_REG		0x20
> +#define XSDIRX_ST352_DS5_REG		0x24
> +#define XSDIRX_ST352_DS7_REG		0x28
> +#define XSDIRX_ST352_DS9_REG		0x2C
> +#define XSDIRX_ST352_DS11_REG		0x30
> +#define XSDIRX_ST352_DS13_REG		0x34
> +#define XSDIRX_ST352_DS15_REG		0x38
> +#define XSDIRX_VERSION_REG		0x3C
> +#define XSDIRX_SS_CONFIG_REG		0x40
> +#define XSDIRX_MODE_DET_STAT_REG	0x44
> +#define XSDIRX_TS_DET_STAT_REG		0x48
> +#define XSDIRX_EDH_STAT_REG		0x4C
> +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> +#define XSDIRX_EDH_ERRCNT_REG		0x54
> +#define XSDIRX_CRC_ERRCNT_REG		0x58
> +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> +#define XSDIRX_SB_RX_STS_REG		0x60
> +
> +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
> +
> +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
> +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> +
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
> +
> +#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
> +
> +#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
> +#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
> +#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
> +#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
> +
> +#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
> +				XSDIRX_INTR_VIDUNLOCK_MASK |\
> +				XSDIRX_INTR_OVERFLOW_MASK |\
> +				XSDIRX_INTR_UNDERFLOW_MASK)
> +
> +#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
> +#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
> +#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
> +#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
> +#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
> +#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
> +#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
> +#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
> +
> +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
> +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
> +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
> +
> +#define XSDIRX_ACTIVE_STREAMS_1		0x0
> +#define XSDIRX_ACTIVE_STREAMS_2		0x1
> +#define XSDIRX_ACTIVE_STREAMS_4		0x2
> +#define XSDIRX_ACTIVE_STREAMS_8		0x3
> +#define XSDIRX_ACTIVE_STREAMS_16	0x4
> +
> +#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
> +#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
> +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
> +#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
> +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
> +#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
> +#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
> +
> +#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
> +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
> +#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
> +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
> +#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
> +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
> +#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
> +#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
> +#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
> +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
> +#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
> +
> +#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
> +#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
> +#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
> +#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
> +#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
> +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
> +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
> +
> +#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
> +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
> +#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
> +#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
> +#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
> +#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
> +
> +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
> +
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
> +
> +/* Number of media pads */
> +#define XSDIRX_MEDIA_PADS	(1)
> +
> +#define XSDIRX_DEFAULT_WIDTH	(1920)
> +#define XSDIRX_DEFAULT_HEIGHT	(1080)
> +
> +#define XSDIRX_MAX_STR_LENGTH	16
> +
> +#define XSDIRXSS_SDI_STD_3G		0
> +#define XSDIRXSS_SDI_STD_6G		1
> +#define XSDIRXSS_SDI_STD_12G_8DS	2
> +
> +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
> +
> +#define XSDIRX_MODE_HD_MASK	0x0
> +#define XSDIRX_MODE_SD_MASK	0x1
> +#define XSDIRX_MODE_3G_MASK	0x2
> +#define XSDIRX_MODE_6G_MASK	0x4
> +#define XSDIRX_MODE_12GI_MASK	0x5
> +#define XSDIRX_MODE_12GF_MASK	0x6
> +
> +/*
> + * Maximum number of events per file handle.
> + */
> +#define XSDIRX_MAX_EVENTS	(128)
> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE_MASK	0xFF
> +#define XST352_PAYLOAD_BYTE1_SHIFT	0
> +#define XST352_PAYLOAD_BYTE2_SHIFT	8
> +#define XST352_PAYLOAD_BYTE3_SHIFT	16
> +#define XST352_PAYLOAD_BYTE4_SHIFT	24
> +
> +#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
> +#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
> +#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
> +#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
> +#define XST352_BYTE1_ST372_DL_3GB		0x8A
> +#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
> +#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
> +#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
> +#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
> +#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
> +
> +#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
> +#define XST352_BYTE2_TS_TYPE_OFFSET		15
> +#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
> +#define XST352_BYTE2_PIC_TYPE_OFFSET		14
> +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
> +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
> +
> +#define XST352_BYTE2_FPS_MASK			0xF
> +#define XST352_BYTE2_FPS_SHIFT			8
> +#define XST352_BYTE2_FPS_24F			0x2
> +#define XST352_BYTE2_FPS_24			0x3
> +#define XST352_BYTE2_FPS_48F			0x4
> +#define XST352_BYTE2_FPS_25			0x5
> +#define XST352_BYTE2_FPS_30F			0x6
> +#define XST352_BYTE2_FPS_30			0x7
> +#define XST352_BYTE2_FPS_48			0x8
> +#define XST352_BYTE2_FPS_50			0x9
> +#define XST352_BYTE2_FPS_60F			0xA
> +#define XST352_BYTE2_FPS_60			0xB
> +/* Table 4 ST 2081-10:2015 */
> +#define XST352_BYTE2_FPS_96			0xC
> +#define XST352_BYTE2_FPS_100			0xD
> +#define XST352_BYTE2_FPS_120			0xE
> +#define XST352_BYTE2_FPS_120F			0xF
> +
> +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
> +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
> +
> +#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
> +#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
> +#define XST352_BYTE3_COLOR_FORMAT_422		0x0
> +#define XST352_BYTE3_COLOR_FORMAT_420		0x3
> +
> +/**
> + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
> + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
> + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> + */
> +enum sdi_family_enc {
> +	XSDIRX_SMPTE_ST_274	= 0,
> +	XSDIRX_SMPTE_ST_296	= 1,
> +	XSDIRX_SMPTE_ST_2048_2	= 2,
> +	XSDIRX_SMPTE_ST_295	= 3,
> +	XSDIRX_NTSC		= 8,
> +	XSDIRX_PAL		= 9,
> +	XSDIRX_TS_UNKNOWN	= 15
> +};
> +
> +/**
> + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @include_edh: EDH processor presence
> + * @mode: 3G/6G/12G mode
> + * @axi_clk: Axi lite interface clock
> + * @sdirx_clk: SDI Rx GT clock
> + * @vidout_clk: Video clock
> + */
> +struct xsdirxss_core {
> +	struct device *dev;
> +	void __iomem *iomem;
> +	int irq;
> +	bool include_edh;
> +	int mode;
> +	struct clk *axi_clk;
> +	struct clk *sdirx_clk;
> +	struct clk *vidout_clk;
> +};
> +
> +/**
> + * struct xsdirxss_state - SDI Rx Subsystem device structure
> + * @core: Core structure for MIPI SDI Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @event: Holds the video unlock event
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @vip_format: format information corresponding to the active format
> + * @pads: media pads
> + * @streaming: Flag for storing streaming state
> + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xsdirxss_state {
> +	struct xsdirxss_core core;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_event event;
> +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> +	struct v4l2_mbus_framefmt default_format;
> +	struct v4l2_fract frame_interval;
> +	const struct xvip_video_format *vip_format;
> +	struct media_pad pads[XSDIRX_MEDIA_PADS];
> +	bool streaming;
> +	bool vidlocked;
> +	bool ts_is_interlaced;
> +};
> +
> +static inline struct xsdirxss_state *
> +to_xsdirxssstate(struct v4l2_subdev *subdev)
> +{
> +	return container_of(subdev, struct xsdirxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> +{
> +	return ioread32(xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> +				  u32 value)
> +{
> +	iowrite32(value, xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 clr)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> +}
> +
> +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 set)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> +}
> +
> +static void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static void xsdirx_core_enable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> +{
> +	u32 i, val;
> +
> +	mask &= XSDIRX_DETECT_ALL_MODES;
> +	if (!mask) {
> +		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> +
> +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> +
> +	if (hweight16(mask) > 1) {
> +		/* Multi mode detection as more than 1 bit set in mask */
> +		dev_dbg(core->dev, "Detect multiple modes\n");
> +		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> +			switch (mask & (1 << i)) {
> +			case BIT(XSDIRX_MODE_SD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_HD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_3G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_6G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GI_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GF_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +				break;
> +			}
> +		}
> +		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> +	} else {
> +		/* Fixed Mode */
> +		u32 forced_mode_mask = 0;
> +
> +		dev_dbg(core->dev, "Detect fixed mode\n");
> +
> +		/* Find offset of first bit set */
> +		switch (__ffs(mask)) {
> +		case XSDIRX_MODE_SD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_SD_MASK;
> +			break;
> +		case XSDIRX_MODE_HD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_HD_MASK;
> +			break;
> +		case XSDIRX_MODE_3G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_3G_MASK;
> +			break;
> +		case XSDIRX_MODE_6G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_6G_MASK;
> +			break;
> +		case XSDIRX_MODE_12GI_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> +			break;
> +		case XSDIRX_MODE_12GF_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> +			break;
> +		}
> +		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> +			forced_mode_mask);
> +		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> +	}
> +
> +	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> +		val);
> +	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> +
> +	return 0;
> +}
> +
> +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
> +{
> +	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> +	val = enable & XSDIRX_EDH_ALLERR_MASK;
> +
> +	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> +{
> +	/*
> +	 * The video lock window is the amount of time for which the
> +	 * the mode and transport stream should be locked to get the
> +	 * video lock interrupt.
> +	 */
> +	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> +}
> +
> +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +}
> +
> +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}
> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +}
> +
> +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +}
> +
> +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
> +{
> +	/* The sdi to native bridge is followed by native to axis4 bridge */
> +	if (enable) {
> +		xsdirx_axis4_bridge_control(core, enable);
> +		xsdirx_vid_bridge_control(core, enable);
> +	} else {
> +		xsdirx_vid_bridge_control(core, enable);
> +		xsdirx_axis4_bridge_control(core, enable);
> +	}
> +}
> +
> +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> +{
> +	xsdirx_streamflow_control(core, false);
> +}
> +
> +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> +				   u32 framerate)
> +{
> +	switch (framerate) {
> +	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 25000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_48HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 50000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 60000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 60000;
> +		break;
> +	default:
> +		frame_interval->numerator = 1;
> +		frame_interval->denominator = 1;
> +	}
> +}
> +
> +/**
> + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> + * @state: pointer to driver state
> + *
> + * This function decodes the stream's ST352 payload (if available) to get
> + * stream properties like width, height, picture type (interlaced/progressive),
> + * etc.
> + *
> + * Return: 0 for success else errors
> + */
> +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> +{
> +	struct xsdirxss_core *core = &state->core;
> +	u32 mode, payload = 0, val, family, valid, tscan;
> +	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> +	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> +	struct v4l2_mbus_framefmt *format = &state->formats[0];
> +
> +	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +
> +	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> +		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
> +		return -EINVAL;
> +	}
> +
> +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> +				XST352_PAYLOAD_BYTE_MASK;
> +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> +				XST352_BYTE2_PIC_TYPE_OFFSET;
> +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> +				XST352_BYTE2_FPS_MASK;
> +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> +				XST352_BYTE2_TS_TYPE_OFFSET;
> +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> +	} else {
> +		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> +			mode);
> +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> +	}
> +
> +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> +	state->ts_is_interlaced = tscan ? false : true;
> +
> +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> +		state->ts_is_interlaced, family);
> +
> +	switch (mode) {
> +	case XSDIRX_MODE_HD_MASK:
> +		if (!valid) {
> +			/* No payload obtained */
> +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> +				framerate, tscan);
> +			/*
> +			 * NOTE : A progressive segmented frame pSF will be
> +			 * reported incorrectly as Interlaced as we rely on IP's
> +			 * transport scan locked bit.
> +			 */
> +			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
> +
> +			switch (framerate) {
> +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +				if (family == XSDIRX_SMPTE_ST_296) {
> +					format->width = 1280;
> +					format->height = 720;
> +					format->field = V4L2_FIELD_NONE;
> +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> +					format->width = 2048;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				} else {
> +					format->width = 1920;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				}
> +				break;
> +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +				if (family == XSDIRX_SMPTE_ST_274) {
> +					format->width = 1920;
> +					format->height = 1080;
> +				} else {
> +					format->width = 1280;
> +					format->height = 720;
> +				}
> +				format->field = V4L2_FIELD_NONE;
> +				break;
> +			default:
> +				format->width = 1920;
> +				format->height = 1080;
> +				format->field = V4L2_FIELD_NONE;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "Got the payload\n");
> +			switch (byte1) {
> +			case XST352_BYTE1_ST292_1x720L_1_5G:
> +				/* SMPTE ST 292-1 for 720 line payloads */
> +				format->width = 1280;
> +				format->height = 720;
> +				break;
> +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> +				/* SMPTE ST 292-1 for 1080 line payloads */
> +				format->height = 1080;
> +				if (active_luma)
> +					format->width = 2048;
> +				else
> +					format->width = 1920;
> +				break;
> +			default:
> +				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case XSDIRX_MODE_SD_MASK:
> +		format->field = V4L2_FIELD_ALTERNATE;
> +
> +		switch (family) {
> +		case XSDIRX_NTSC:
> +			format->width = 720;
> +			format->height = 486;
> +			break;
> +		case XSDIRX_PAL:
> +			format->width = 720;
> +			format->height = 576;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_3G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST425_2008_750L_3GB:
> +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x720L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->width = 1280;
> +			format->height = 720;
> +			break;
> +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> +			/* ST352 Table SMPTE 425-1 */
> +		case XST352_BYTE1_ST372_DL_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x1080L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->height = 1080;
> +			if (active_luma)
> +				format->width = 2048;
> +			else
> +				format->width = 1920;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_6G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> +			/* Dual link 6G */
> +		case XST352_BYTE1_ST2081_10_2160L_6G:
> +			/* Table 3 SMPTE ST 2081-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_12GI_MASK:
> +	case XSDIRX_MODE_12GF_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2082_10_2160L_12G:
> +			/* Section 4.3.1 SMPTE ST 2082-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		dev_err(core->dev, "Invalid Mode\n");
> +		return -EINVAL;
> +	}
> +
> +	if (valid) {
> +		if (pic_type)
> +			format->field = V4L2_FIELD_NONE;
> +		else
> +			format->field = V4L2_FIELD_ALTERNATE;
> +	}
> +
> +	if (format->field == V4L2_FIELD_ALTERNATE)
> +		format->height = format->height / 2;
> +
> +	switch (sampling) {
> +	case XST352_BYTE3_COLOR_FORMAT_422:
> +		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +		break;
> +	default:
> +		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> +		return -EINVAL;
> +	}
> +
> +	xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
> +		format->width, format->height, format->field, payload, val);
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		state->frame_interval.numerator,
> +		state->frame_interval.denominator);
> +	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> + * in the interrupt clear register. The interrupt status register is read only.
> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> +{
> +	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> +	struct xsdirxss_core *core = &state->core;
> +	u32 status;
> +
> +	status = xsdirxss_read(core, XSDIRX_ISR_REG);
> +	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> +		u32 val1, val2;
> +
> +		dev_dbg(core->dev, "video lock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> +
> +		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +
> +		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> +		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> +			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> +				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> +
> +			dev_dbg(core->dev, "mode & ts lock occurred\n");
> +
> +			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> +			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +
> +			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +
> +			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> +			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> +
> +			if (!xsdirx_get_stream_properties(state)) {
> +				memset(&state->event, 0, sizeof(state->event));
> +				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> +				state->event.u.src_change.changes =
> +					V4L2_EVENT_SRC_CH_RESOLUTION;
> +				v4l2_subdev_notify_event(&state->subdev,
> +							 &state->event);
> +
> +				state->vidlocked = true;
> +			} else {
> +				dev_err(core->dev, "Unable to get stream properties!\n");
> +				state->vidlocked = false;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "video unlock before video lock!\n");
> +			state->vidlocked = false;
> +		}
> +	}
> +
> +	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> +		dev_dbg(core->dev, "video unlock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> +		xsdirx_streamdowncb(core);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +
> +		state->vidlocked = false;
> +	}
> +
> +	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +
> +	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	int ret;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (sub->type) {
> +	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> +	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> +	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> +		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> +		break;
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		ret = v4l2_src_change_event_subscribe(fh, sub);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> +				      struct v4l2_fh *fh,
> +				      struct v4l2_event_subscription *sub)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> +	return v4l2_event_unsubscribe(fh, sub);
> +}
> +
> +/**
> + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> + * Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	int ret = 0;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +
> +	if (xsdirxss->streaming) {
> +		dev_err(core->dev, "Cannot set controls while streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	xsdirx_core_disable(core);
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_FRAMER:
> +		xsdirx_framer(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> +		xsdirx_setvidlockwindow(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> +		xsdirx_setedherrcnttrigger(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> +		if (ctrl->val) {
> +			if (core->mode == XSDIRXSS_SDI_STD_3G) {
> +				dev_dbg(core->dev, "Upto 3G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			if (core->mode == XSDIRXSS_SDI_STD_6G) {
> +				dev_dbg(core->dev, "Upto 6G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			ret = xsdirx_set_modedetect(core, ctrl->val);
> +		} else {
> +			dev_err(core->dev, "Select at least one mode!\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SS_EN_MASK);
> +		return -EINVAL;
> +	}
> +	xsdirx_core_enable(core);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	u32 val;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +		switch (val) {
> +		case XSDIRX_MODE_SD_MASK:
> +			ctrl->val = XSDIRX_MODE_SD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_HD_MASK:
> +			ctrl->val = XSDIRX_MODE_HD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_3G_MASK:
> +			ctrl->val = XSDIRX_MODE_3G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_6G_MASK:
> +			ctrl->val = XSDIRX_MODE_6G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GI_MASK:
> +			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GF_MASK:
> +			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> +			break;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_CRC:
> +		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> +		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		ctrl->val = xsdirxss->ts_is_interlaced;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> +		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> +		ctrl->val = 1 << val;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_IS_3GB:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> +		ctrl->val = val ? true : false;
> +		break;
> +	default:
> +		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx SDI Rx Subsystem
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	u32 data, i;
> +
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> +	for (i = 0; i < 0x28; i++) {
> +		data = xsdirxss_read(core, i * 4);
> +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> +			  i * 4, data);
> +	}
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> +	return 0;
> +}
> +
> +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, true);
> +}
> +
> +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, false);
> +}
> +
> +/**
> + * xsdirxss_g_frame_interval - Get the frame interval
> + * @sd: V4L2 Sub device
> + * @fi: Pointer to V4l2 Sub device frame interval structure
> + *
> + * This function is used to get the frame interval.
> + * The frame rate can be integral or fractional.
> + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
> + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fi->interval = xsdirxss->frame_interval;
> +
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		xsdirxss->frame_interval.numerator,
> +		xsdirxss->frame_interval.denominator);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 SDI Rx Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (enable) {
> +		if (!xsdirxss->vidlocked) {
> +			dev_dbg(core->dev, "Video is not locked\n");
> +			return -EINVAL;
> +		}
> +		if (xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Already streaming\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_start_stream(xsdirxss);
> +		xsdirxss->streaming = true;
> +		dev_dbg(core->dev, "Streaming started\n");
> +	} else {
> +		if (!xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Stopped streaming already\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_stop_stream(xsdirxss);
> +		xsdirxss->streaming = false;
> +		dev_dbg(core->dev, "Streaming stopped\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> +			  struct v4l2_subdev_pad_config *cfg,
> +				unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &xsdirxss->formats[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_format *fmt)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> +						 fmt->pad, fmt->which);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> +		fmt->format.width, fmt->format.height, fmt->format.field);
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *__format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	dev_dbg(xsdirxss->core.dev,
> +		"set width %d height %d code %d field %d colorspace %d\n",
> +		fmt->format.width, fmt->format.height,
> +		fmt->format.code, fmt->format.field,
> +		fmt->format.colorspace);
> +
> +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> +					     fmt->pad, fmt->which);
> +
> +	/* Currently reset the code to one fixed in hardware */
> +	/* TODO : Add checks for width height */
> +	fmt->format.code = __format->code;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 pad.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_open(struct v4l2_subdev *sd,
> +			 struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	*format = xsdirxss->default_format;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_close - Called on v4l2_close()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_close().
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_close(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_fh *fh)
> +{
> +	return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xsdirxss_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> +	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> +	.s_ctrl	= xsdirxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> +		.name	= "SDI Rx : EDH Error Count Enable",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_EDH_ALLERR_MASK,
> +		.def	= 0,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> +		.name	= "SDI Rx : EDH Error Count",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> +		.name	= "SDI Rx : EDH Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
> +		.name	= "SDI Rx : Enable Framer",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.step	= 1,
> +		.def	= true,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> +		.name	= "SDI Rx : Video Lock Window",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> +		.name	= "SDI Rx : Modes search Mask",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_DETECT_ALL_MODES,
> +		.def	= XSDIRX_DETECT_ALL_MODES,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> +		.name	= "SDI Rx : Mode Detect Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= XSDIRX_MODE_SD_OFFSET,
> +		.max	= XSDIRX_MODE_12GF_OFFSET,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_CRC,
> +		.name	= "SDI Rx : CRC Error status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> +		.name	= "SDI Rx : TS is Interlaced",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> +		.name	= "SDI Rx : Active Streams",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 1,
> +		.max	= 16,
> +		.def	= 1,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
> +		.name	= "SDI Rx : Is 3GB",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> +	.log_status = xsdirxss_log_status,
> +	.subscribe_event = xsdirxss_subscribe_event,
> +	.unsubscribe_event = xsdirxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> +	.g_frame_interval = xsdirxss_g_frame_interval,
> +	.s_stream = xsdirxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> +	.get_fmt = xsdirxss_get_format,
> +	.set_fmt = xsdirxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xsdirxss_ops = {
> +	.core = &xsdirxss_core_ops,
> +	.video = &xsdirxss_video_ops,
> +	.pad = &xsdirxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> +	.open = xsdirxss_open,
> +	.close = xsdirxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> +	struct device_node *node = xsdirxss->core.dev->of_node;
> +	struct device_node *ports = NULL;
> +	struct device_node *port = NULL;
> +	unsigned int nports = 0;
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	int ret;
> +	const char *sdi_std;
> +
> +	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> +	dev_dbg(core->dev, "EDH property = %s\n",
> +		core->include_edh ? "Present" : "Absent");
> +
> +	ret = of_property_read_string(node, "xlnx,line-rate",
> +				      &sdi_std);
> +	if (ret < 0) {
> +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> +		return ret;
> +	}
> +
> +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_6G;
> +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_3G;
> +	} else {
> +		dev_err(core->dev, "Invalid Line Rate\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> +		core->mode);
> +
> +	ports = of_get_child_by_name(node, "ports");
> +	if (!ports)
> +		ports = node;
> +
> +	for_each_child_of_node(ports, port) {
> +		const struct xvip_video_format *format;
> +		struct device_node *endpoint;
> +
> +		if (!port->name || of_node_cmp(port->name, "port"))
> +			continue;
> +
> +		format = xvip_of_get_format(port);
> +		if (IS_ERR(format)) {
> +			dev_err(core->dev, "invalid format in DT");
> +			return PTR_ERR(format);
> +		}
> +
> +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> +			format->vf_code, format->width, format->bpp);
> +
> +		if (format->vf_code != XVIP_VF_YUV_422 &&
> +		    format->vf_code != XVIP_VF_YUV_420) {
> +			dev_err(core->dev, "Incorrect UG934 video format set.\n");
> +			return -EINVAL;
> +		}
> +		xsdirxss->vip_format = format;
> +
> +		endpoint = of_get_next_child(port, NULL);
> +		if (!endpoint) {
> +			dev_err(core->dev, "No port at\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Count the number of ports. */
> +		nports++;
> +	}
> +
> +	if (nports != 1) {
> +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> +		return -EINVAL;
> +	}
> +
> +	/* Register interrupt handler */
> +	core->irq = irq_of_parse_and_map(node, 0);
> +
> +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> +	if (ret) {
> +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> +	struct v4l2_subdev *subdev;
> +	struct xsdirxss_state *xsdirxss;
> +	struct xsdirxss_core *core;
> +	struct resource *res;
> +	int ret;
> +	unsigned int num_ctrls, num_edh_ctrls = 0, i;
> +
> +	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> +	if (!xsdirxss)
> +		return -ENOMEM;
> +
> +	xsdirxss->core.dev = &pdev->dev;
> +	core = &xsdirxss->core;
> +
> +	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> +	if (IS_ERR(core->axi_clk)) {
> +		ret = PTR_ERR(core->axi_clk);
> +		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> +	if (IS_ERR(core->sdirx_clk)) {
> +		ret = PTR_ERR(core->sdirx_clk);
> +		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> +	if (IS_ERR(core->vidout_clk)) {
> +		ret = PTR_ERR(core->vidout_clk);
> +		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->axi_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->sdirx_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> +		goto rx_clk_err;
> +	}
> +
> +	ret = clk_prepare_enable(core->vidout_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> +		goto vidout_clk_err;
> +	}
> +
> +	ret = xsdirxss_parse_of(xsdirxss);
> +	if (ret < 0)
> +		goto clk_err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> +	if (IS_ERR(xsdirxss->core.iomem)) {
> +		ret = PTR_ERR(xsdirxss->core.iomem);
> +		goto clk_err;
> +	}
> +
> +	/* Reset the core */
> +	xsdirx_streamflow_control(core, false);
> +	xsdirx_core_disable(core);
> +	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_globalintr(core, true);
> +	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Initialize the default format */
> +	xsdirxss->default_format.code = xsdirxss->vip_format->code;
> +	xsdirxss->default_format.field = V4L2_FIELD_NONE;
> +	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> +	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> +	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> +	xsdirxss->formats[0] = xsdirxss->default_format;
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	subdev = &xsdirxss->subdev;
> +	v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> +	subdev->dev = &pdev->dev;
> +	subdev->internal_ops = &xsdirxss_internal_ops;
> +	strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));

Use strscpy.

> +
> +	subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	subdev->entity.ops = &xsdirxss_media_ops;
> +
> +	v4l2_set_subdevdata(subdev, xsdirxss);
> +
> +	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialise and register the controls */
> +	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> +	if (xsdirxss->core.include_edh)
> +		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> +
> +	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> +			       (num_ctrls + num_edh_ctrls));
> +
> +	for (i = 0; i < num_ctrls; i++) {
> +		struct v4l2_ctrl *ctrl;
> +
> +		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> +
> +		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +					    &xsdirxss_ctrls[i], NULL);
> +		if (!ctrl) {
> +			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +				xsdirxss_ctrls[i].name);
> +			goto error;
> +		}
> +	}
> +
> +	if (xsdirxss->core.include_edh) {
> +		for (i = 0; i < num_edh_ctrls; i++) {
> +			struct v4l2_ctrl *ctrl;
> +
> +			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +				i, xsdirxss_edh_ctrls[i].name,
> +				xsdirxss_edh_ctrls[i].id);
> +
> +			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +						    &xsdirxss_edh_ctrls[i],
> +						    NULL);
> +			if (!ctrl) {
> +				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +					xsdirxss_edh_ctrls[i].name);
> +				goto error;
> +			}
> +		}
> +	} else {
> +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");
> +	}
> +
> +	if (xsdirxss->ctrl_handler.error) {
> +		dev_err(&pdev->dev, "failed to add controls\n");
> +		ret = xsdirxss->ctrl_handler.error;
> +		goto error;
> +	}
> +
> +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> +
> +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to set controls\n");
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, xsdirxss);
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register subdev\n");
> +		goto error;
> +	}
> +
> +	xsdirxss->streaming = false;
> +
> +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> +
> +	xsdirx_core_enable(core);
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +
> +clk_err:
> +	clk_disable_unprepare(core->vidout_clk);
> +vidout_clk_err:
> +	clk_disable_unprepare(core->sdirx_clk);
> +rx_clk_err:
> +	clk_disable_unprepare(core->axi_clk);
> +	return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> +
> +static struct platform_driver xsdirxss_driver = {
> +	.driver = {
> +		.name		= "xilinx-sdirxss",
> +		.of_match_table	= xsdirxss_of_id_table,
> +	},
> +	.probe			= xsdirxss_probe,
> +	.remove			= xsdirxss_remove,
> +};
> +
> +module_platform_driver(xsdirxss_driver);
> +
> +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
> new file mode 100644
> index 0000000..983a43b
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx SDI Rx Subsystem mode and flag definitions.
> + *
> + * Copyright (C) 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * This enum is used to prepare the bitmask of modes to be detected
> + */
> +enum {
> +	XSDIRX_MODE_SD_OFFSET = 0,
> +	XSDIRX_MODE_HD_OFFSET,
> +	XSDIRX_MODE_3G_OFFSET,
> +	XSDIRX_MODE_6G_OFFSET,
> +	XSDIRX_MODE_12GI_OFFSET,
> +	XSDIRX_MODE_12GF_OFFSET,
> +	XSDIRX_MODE_NUM_SUPPORTED,
> +};
> +
> +#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
> +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GF_OFFSET))
> +
> +/*
> + * EDH Error Types
> + * ANC - Ancillary Data Packet Errors
> + * FF - Full Field Errors
> + * AP - Active Portion Errors
> + */
> +
> +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> +
> +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index f023623..4c68a10 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -83,4 +83,34 @@
>  /* Reset all event counters */
>  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
>  
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
> +
> +/* Framer Control */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
> +/* Video Lock Window Control */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
> +/* Mode search Control */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
> +/* Get Detected Mode control */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
> +/* Get CRC error status */
> +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
> +/* Get EDH error count control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
> +/* Get EDH status control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
> +/* Get Transport Interlaced status */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
> +/* Get Active Streams count */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
> +/* Is Mode 3GB */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
> +
>  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
> index 2aa357c..7b47595 100644
> --- a/include/uapi/linux/xilinx-v4l2-events.h
> +++ b/include/uapi/linux/xilinx-v4l2-events.h
> @@ -18,4 +18,13 @@
>  /* Stream Line Buffer full */
>  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
>  
> +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
> +/* Video Unlock event */
> +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> +/* Video in to AXI4 Stream core underflowed */
> +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> +/* Video in to AXI4 Stream core overflowed */
> +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> +
>  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> 

I am concerned about this driver: I see that none of the *_dv_timings callbacks
are implemented. I would expect to see that for a video receiver. There is also
no g_input_status implemented.

Take a look at another SDI driver: drivers/media/spi/gs1662.c

Some of the controls you add in this driver can likely be dropped. Especially
those controls that are not specific to the Xilinx implementation but are
generic for any SDI receiver, should be looked at closely: those are
candidates for becoming standard controls.

But the documentation above is simply insufficient for me to tell what is
SDI specific and what is implementation specific.

Also, I'm no SDI expert, certainly not for the UHD-SDI.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-05 12:57     ` Hans Verkuil
  0 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2019-06-05 12:57 UTC (permalink / raw)
  To: Vishal Sagar, Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab,
	Michal Simek, Rob Herring, Mark Rutland
  Cc: Sandip Kothari, devicetree, linux-kernel, Dinesh Kumar,
	linux-arm-kernel, linux-media

On 6/4/19 3:55 PM, Vishal Sagar wrote:
> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> streams from SDI sources like SDI broadcast equipment like cameras and
> mixers. This block outputs either native SDI, native video or
> AXI4-Stream compliant data stream for further processing. Please refer
> to PG290 for details.
> 
> The driver is used to configure the IP to add framer, search for
> specific modes, get the detected mode, stream parameters, errors, etc.
> It also generates events for video lock/unlock, bridge over/under flow.
> 
> The driver supports only 10 bpc YUV 422 media bus format. It also
> decodes the stream parameters based on the ST352 packet embedded in the
> stream. In case the ST352 packet isn't present in the stream, the core's
> detected properties are used to set stream properties.
> 
> The driver currently supports only the AXI4-Stream configuration.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig          |   11 +
>  drivers/media/platform/xilinx/Makefile         |    1 +
>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +

My comments for the MIPI CSI-2 Rx driver apply here as well with respect
to the naming of controls/events and documenting them.

Also, the contents of xilinx-v4l2-*.h can just be moved to xilinx-sdirxss.h.

>  6 files changed, 1960 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
>  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index cd1a0fd..0c68caa 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
>  	  Bridge. The driver is used to set the number of active lanes and
>  	  get short packet data.
>  
> +config VIDEO_XILINX_SDIRXSS
> +	tristate "Xilinx UHD SDI Rx Subsystem"
> +	help
> +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> +	  based driver that takes input from a SDI source like SDI camera and
> +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> +	  AXI4-Stream bridge. The driver is used to set different stream
> +	  detection modes and identify stream properties to properly configure
> +	  downstream.
> +
>  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 6119a34..223f2ea 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> new file mode 100644
> index 0000000..ba2d9d0
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,1846 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.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/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-sdirxss.h>
> +#include <linux/xilinx-v4l2-events.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"
> +
> +/*
> + * SDI Rx register map, bitmask and offsets
> + */
> +#define XSDIRX_RST_CTRL_REG		0x00
> +#define XSDIRX_MDL_CTRL_REG		0x04
> +#define XSDIRX_GLBL_IER_REG		0x0C
> +#define XSDIRX_ISR_REG			0x10
> +#define XSDIRX_IER_REG			0x14
> +#define XSDIRX_ST352_VALID_REG		0x18
> +#define XSDIRX_ST352_DS1_REG		0x1C
> +#define XSDIRX_ST352_DS3_REG		0x20
> +#define XSDIRX_ST352_DS5_REG		0x24
> +#define XSDIRX_ST352_DS7_REG		0x28
> +#define XSDIRX_ST352_DS9_REG		0x2C
> +#define XSDIRX_ST352_DS11_REG		0x30
> +#define XSDIRX_ST352_DS13_REG		0x34
> +#define XSDIRX_ST352_DS15_REG		0x38
> +#define XSDIRX_VERSION_REG		0x3C
> +#define XSDIRX_SS_CONFIG_REG		0x40
> +#define XSDIRX_MODE_DET_STAT_REG	0x44
> +#define XSDIRX_TS_DET_STAT_REG		0x48
> +#define XSDIRX_EDH_STAT_REG		0x4C
> +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> +#define XSDIRX_EDH_ERRCNT_REG		0x54
> +#define XSDIRX_CRC_ERRCNT_REG		0x58
> +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> +#define XSDIRX_SB_RX_STS_REG		0x60
> +
> +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
> +
> +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
> +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> +
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
> +
> +#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
> +
> +#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
> +#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
> +#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
> +#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
> +
> +#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
> +				XSDIRX_INTR_VIDUNLOCK_MASK |\
> +				XSDIRX_INTR_OVERFLOW_MASK |\
> +				XSDIRX_INTR_UNDERFLOW_MASK)
> +
> +#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
> +#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
> +#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
> +#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
> +#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
> +#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
> +#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
> +#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
> +
> +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
> +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
> +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
> +
> +#define XSDIRX_ACTIVE_STREAMS_1		0x0
> +#define XSDIRX_ACTIVE_STREAMS_2		0x1
> +#define XSDIRX_ACTIVE_STREAMS_4		0x2
> +#define XSDIRX_ACTIVE_STREAMS_8		0x3
> +#define XSDIRX_ACTIVE_STREAMS_16	0x4
> +
> +#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
> +#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
> +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
> +#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
> +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
> +#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
> +#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
> +
> +#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
> +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
> +#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
> +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
> +#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
> +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
> +#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
> +#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
> +#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
> +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
> +#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
> +
> +#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
> +#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
> +#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
> +#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
> +#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
> +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
> +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
> +
> +#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
> +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
> +#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
> +#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
> +#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
> +#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
> +
> +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
> +
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
> +
> +/* Number of media pads */
> +#define XSDIRX_MEDIA_PADS	(1)
> +
> +#define XSDIRX_DEFAULT_WIDTH	(1920)
> +#define XSDIRX_DEFAULT_HEIGHT	(1080)
> +
> +#define XSDIRX_MAX_STR_LENGTH	16
> +
> +#define XSDIRXSS_SDI_STD_3G		0
> +#define XSDIRXSS_SDI_STD_6G		1
> +#define XSDIRXSS_SDI_STD_12G_8DS	2
> +
> +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
> +
> +#define XSDIRX_MODE_HD_MASK	0x0
> +#define XSDIRX_MODE_SD_MASK	0x1
> +#define XSDIRX_MODE_3G_MASK	0x2
> +#define XSDIRX_MODE_6G_MASK	0x4
> +#define XSDIRX_MODE_12GI_MASK	0x5
> +#define XSDIRX_MODE_12GF_MASK	0x6
> +
> +/*
> + * Maximum number of events per file handle.
> + */
> +#define XSDIRX_MAX_EVENTS	(128)
> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE_MASK	0xFF
> +#define XST352_PAYLOAD_BYTE1_SHIFT	0
> +#define XST352_PAYLOAD_BYTE2_SHIFT	8
> +#define XST352_PAYLOAD_BYTE3_SHIFT	16
> +#define XST352_PAYLOAD_BYTE4_SHIFT	24
> +
> +#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
> +#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
> +#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
> +#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
> +#define XST352_BYTE1_ST372_DL_3GB		0x8A
> +#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
> +#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
> +#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
> +#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
> +#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
> +
> +#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
> +#define XST352_BYTE2_TS_TYPE_OFFSET		15
> +#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
> +#define XST352_BYTE2_PIC_TYPE_OFFSET		14
> +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
> +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
> +
> +#define XST352_BYTE2_FPS_MASK			0xF
> +#define XST352_BYTE2_FPS_SHIFT			8
> +#define XST352_BYTE2_FPS_24F			0x2
> +#define XST352_BYTE2_FPS_24			0x3
> +#define XST352_BYTE2_FPS_48F			0x4
> +#define XST352_BYTE2_FPS_25			0x5
> +#define XST352_BYTE2_FPS_30F			0x6
> +#define XST352_BYTE2_FPS_30			0x7
> +#define XST352_BYTE2_FPS_48			0x8
> +#define XST352_BYTE2_FPS_50			0x9
> +#define XST352_BYTE2_FPS_60F			0xA
> +#define XST352_BYTE2_FPS_60			0xB
> +/* Table 4 ST 2081-10:2015 */
> +#define XST352_BYTE2_FPS_96			0xC
> +#define XST352_BYTE2_FPS_100			0xD
> +#define XST352_BYTE2_FPS_120			0xE
> +#define XST352_BYTE2_FPS_120F			0xF
> +
> +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
> +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
> +
> +#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
> +#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
> +#define XST352_BYTE3_COLOR_FORMAT_422		0x0
> +#define XST352_BYTE3_COLOR_FORMAT_420		0x3
> +
> +/**
> + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
> + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
> + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> + */
> +enum sdi_family_enc {
> +	XSDIRX_SMPTE_ST_274	= 0,
> +	XSDIRX_SMPTE_ST_296	= 1,
> +	XSDIRX_SMPTE_ST_2048_2	= 2,
> +	XSDIRX_SMPTE_ST_295	= 3,
> +	XSDIRX_NTSC		= 8,
> +	XSDIRX_PAL		= 9,
> +	XSDIRX_TS_UNKNOWN	= 15
> +};
> +
> +/**
> + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @include_edh: EDH processor presence
> + * @mode: 3G/6G/12G mode
> + * @axi_clk: Axi lite interface clock
> + * @sdirx_clk: SDI Rx GT clock
> + * @vidout_clk: Video clock
> + */
> +struct xsdirxss_core {
> +	struct device *dev;
> +	void __iomem *iomem;
> +	int irq;
> +	bool include_edh;
> +	int mode;
> +	struct clk *axi_clk;
> +	struct clk *sdirx_clk;
> +	struct clk *vidout_clk;
> +};
> +
> +/**
> + * struct xsdirxss_state - SDI Rx Subsystem device structure
> + * @core: Core structure for MIPI SDI Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @event: Holds the video unlock event
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @vip_format: format information corresponding to the active format
> + * @pads: media pads
> + * @streaming: Flag for storing streaming state
> + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xsdirxss_state {
> +	struct xsdirxss_core core;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_event event;
> +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> +	struct v4l2_mbus_framefmt default_format;
> +	struct v4l2_fract frame_interval;
> +	const struct xvip_video_format *vip_format;
> +	struct media_pad pads[XSDIRX_MEDIA_PADS];
> +	bool streaming;
> +	bool vidlocked;
> +	bool ts_is_interlaced;
> +};
> +
> +static inline struct xsdirxss_state *
> +to_xsdirxssstate(struct v4l2_subdev *subdev)
> +{
> +	return container_of(subdev, struct xsdirxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> +{
> +	return ioread32(xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> +				  u32 value)
> +{
> +	iowrite32(value, xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 clr)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> +}
> +
> +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 set)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> +}
> +
> +static void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static void xsdirx_core_enable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> +{
> +	u32 i, val;
> +
> +	mask &= XSDIRX_DETECT_ALL_MODES;
> +	if (!mask) {
> +		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> +
> +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> +
> +	if (hweight16(mask) > 1) {
> +		/* Multi mode detection as more than 1 bit set in mask */
> +		dev_dbg(core->dev, "Detect multiple modes\n");
> +		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> +			switch (mask & (1 << i)) {
> +			case BIT(XSDIRX_MODE_SD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_HD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_3G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_6G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GI_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GF_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +				break;
> +			}
> +		}
> +		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> +	} else {
> +		/* Fixed Mode */
> +		u32 forced_mode_mask = 0;
> +
> +		dev_dbg(core->dev, "Detect fixed mode\n");
> +
> +		/* Find offset of first bit set */
> +		switch (__ffs(mask)) {
> +		case XSDIRX_MODE_SD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_SD_MASK;
> +			break;
> +		case XSDIRX_MODE_HD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_HD_MASK;
> +			break;
> +		case XSDIRX_MODE_3G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_3G_MASK;
> +			break;
> +		case XSDIRX_MODE_6G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_6G_MASK;
> +			break;
> +		case XSDIRX_MODE_12GI_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> +			break;
> +		case XSDIRX_MODE_12GF_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> +			break;
> +		}
> +		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> +			forced_mode_mask);
> +		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> +	}
> +
> +	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> +		val);
> +	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> +
> +	return 0;
> +}
> +
> +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
> +{
> +	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> +	val = enable & XSDIRX_EDH_ALLERR_MASK;
> +
> +	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> +{
> +	/*
> +	 * The video lock window is the amount of time for which the
> +	 * the mode and transport stream should be locked to get the
> +	 * video lock interrupt.
> +	 */
> +	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> +}
> +
> +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +}
> +
> +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}
> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +}
> +
> +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +}
> +
> +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
> +{
> +	/* The sdi to native bridge is followed by native to axis4 bridge */
> +	if (enable) {
> +		xsdirx_axis4_bridge_control(core, enable);
> +		xsdirx_vid_bridge_control(core, enable);
> +	} else {
> +		xsdirx_vid_bridge_control(core, enable);
> +		xsdirx_axis4_bridge_control(core, enable);
> +	}
> +}
> +
> +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> +{
> +	xsdirx_streamflow_control(core, false);
> +}
> +
> +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> +				   u32 framerate)
> +{
> +	switch (framerate) {
> +	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 25000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_48HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 50000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 60000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 60000;
> +		break;
> +	default:
> +		frame_interval->numerator = 1;
> +		frame_interval->denominator = 1;
> +	}
> +}
> +
> +/**
> + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> + * @state: pointer to driver state
> + *
> + * This function decodes the stream's ST352 payload (if available) to get
> + * stream properties like width, height, picture type (interlaced/progressive),
> + * etc.
> + *
> + * Return: 0 for success else errors
> + */
> +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> +{
> +	struct xsdirxss_core *core = &state->core;
> +	u32 mode, payload = 0, val, family, valid, tscan;
> +	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> +	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> +	struct v4l2_mbus_framefmt *format = &state->formats[0];
> +
> +	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +
> +	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> +		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
> +		return -EINVAL;
> +	}
> +
> +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> +				XST352_PAYLOAD_BYTE_MASK;
> +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> +				XST352_BYTE2_PIC_TYPE_OFFSET;
> +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> +				XST352_BYTE2_FPS_MASK;
> +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> +				XST352_BYTE2_TS_TYPE_OFFSET;
> +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> +	} else {
> +		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> +			mode);
> +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> +	}
> +
> +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> +	state->ts_is_interlaced = tscan ? false : true;
> +
> +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> +		state->ts_is_interlaced, family);
> +
> +	switch (mode) {
> +	case XSDIRX_MODE_HD_MASK:
> +		if (!valid) {
> +			/* No payload obtained */
> +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> +				framerate, tscan);
> +			/*
> +			 * NOTE : A progressive segmented frame pSF will be
> +			 * reported incorrectly as Interlaced as we rely on IP's
> +			 * transport scan locked bit.
> +			 */
> +			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
> +
> +			switch (framerate) {
> +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +				if (family == XSDIRX_SMPTE_ST_296) {
> +					format->width = 1280;
> +					format->height = 720;
> +					format->field = V4L2_FIELD_NONE;
> +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> +					format->width = 2048;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				} else {
> +					format->width = 1920;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				}
> +				break;
> +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +				if (family == XSDIRX_SMPTE_ST_274) {
> +					format->width = 1920;
> +					format->height = 1080;
> +				} else {
> +					format->width = 1280;
> +					format->height = 720;
> +				}
> +				format->field = V4L2_FIELD_NONE;
> +				break;
> +			default:
> +				format->width = 1920;
> +				format->height = 1080;
> +				format->field = V4L2_FIELD_NONE;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "Got the payload\n");
> +			switch (byte1) {
> +			case XST352_BYTE1_ST292_1x720L_1_5G:
> +				/* SMPTE ST 292-1 for 720 line payloads */
> +				format->width = 1280;
> +				format->height = 720;
> +				break;
> +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> +				/* SMPTE ST 292-1 for 1080 line payloads */
> +				format->height = 1080;
> +				if (active_luma)
> +					format->width = 2048;
> +				else
> +					format->width = 1920;
> +				break;
> +			default:
> +				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case XSDIRX_MODE_SD_MASK:
> +		format->field = V4L2_FIELD_ALTERNATE;
> +
> +		switch (family) {
> +		case XSDIRX_NTSC:
> +			format->width = 720;
> +			format->height = 486;
> +			break;
> +		case XSDIRX_PAL:
> +			format->width = 720;
> +			format->height = 576;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_3G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST425_2008_750L_3GB:
> +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x720L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->width = 1280;
> +			format->height = 720;
> +			break;
> +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> +			/* ST352 Table SMPTE 425-1 */
> +		case XST352_BYTE1_ST372_DL_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x1080L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->height = 1080;
> +			if (active_luma)
> +				format->width = 2048;
> +			else
> +				format->width = 1920;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_6G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> +			/* Dual link 6G */
> +		case XST352_BYTE1_ST2081_10_2160L_6G:
> +			/* Table 3 SMPTE ST 2081-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_12GI_MASK:
> +	case XSDIRX_MODE_12GF_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2082_10_2160L_12G:
> +			/* Section 4.3.1 SMPTE ST 2082-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		dev_err(core->dev, "Invalid Mode\n");
> +		return -EINVAL;
> +	}
> +
> +	if (valid) {
> +		if (pic_type)
> +			format->field = V4L2_FIELD_NONE;
> +		else
> +			format->field = V4L2_FIELD_ALTERNATE;
> +	}
> +
> +	if (format->field == V4L2_FIELD_ALTERNATE)
> +		format->height = format->height / 2;
> +
> +	switch (sampling) {
> +	case XST352_BYTE3_COLOR_FORMAT_422:
> +		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +		break;
> +	default:
> +		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> +		return -EINVAL;
> +	}
> +
> +	xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
> +		format->width, format->height, format->field, payload, val);
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		state->frame_interval.numerator,
> +		state->frame_interval.denominator);
> +	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> + * in the interrupt clear register. The interrupt status register is read only.
> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> +{
> +	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> +	struct xsdirxss_core *core = &state->core;
> +	u32 status;
> +
> +	status = xsdirxss_read(core, XSDIRX_ISR_REG);
> +	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> +		u32 val1, val2;
> +
> +		dev_dbg(core->dev, "video lock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> +
> +		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +
> +		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> +		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> +			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> +				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> +
> +			dev_dbg(core->dev, "mode & ts lock occurred\n");
> +
> +			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> +			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +
> +			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +
> +			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> +			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> +
> +			if (!xsdirx_get_stream_properties(state)) {
> +				memset(&state->event, 0, sizeof(state->event));
> +				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> +				state->event.u.src_change.changes =
> +					V4L2_EVENT_SRC_CH_RESOLUTION;
> +				v4l2_subdev_notify_event(&state->subdev,
> +							 &state->event);
> +
> +				state->vidlocked = true;
> +			} else {
> +				dev_err(core->dev, "Unable to get stream properties!\n");
> +				state->vidlocked = false;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "video unlock before video lock!\n");
> +			state->vidlocked = false;
> +		}
> +	}
> +
> +	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> +		dev_dbg(core->dev, "video unlock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> +		xsdirx_streamdowncb(core);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +
> +		state->vidlocked = false;
> +	}
> +
> +	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +
> +	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	int ret;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (sub->type) {
> +	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> +	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> +	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> +		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> +		break;
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		ret = v4l2_src_change_event_subscribe(fh, sub);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> +				      struct v4l2_fh *fh,
> +				      struct v4l2_event_subscription *sub)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> +	return v4l2_event_unsubscribe(fh, sub);
> +}
> +
> +/**
> + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> + * Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	int ret = 0;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +
> +	if (xsdirxss->streaming) {
> +		dev_err(core->dev, "Cannot set controls while streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	xsdirx_core_disable(core);
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_FRAMER:
> +		xsdirx_framer(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> +		xsdirx_setvidlockwindow(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> +		xsdirx_setedherrcnttrigger(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> +		if (ctrl->val) {
> +			if (core->mode == XSDIRXSS_SDI_STD_3G) {
> +				dev_dbg(core->dev, "Upto 3G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			if (core->mode == XSDIRXSS_SDI_STD_6G) {
> +				dev_dbg(core->dev, "Upto 6G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			ret = xsdirx_set_modedetect(core, ctrl->val);
> +		} else {
> +			dev_err(core->dev, "Select at least one mode!\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SS_EN_MASK);
> +		return -EINVAL;
> +	}
> +	xsdirx_core_enable(core);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	u32 val;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +		switch (val) {
> +		case XSDIRX_MODE_SD_MASK:
> +			ctrl->val = XSDIRX_MODE_SD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_HD_MASK:
> +			ctrl->val = XSDIRX_MODE_HD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_3G_MASK:
> +			ctrl->val = XSDIRX_MODE_3G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_6G_MASK:
> +			ctrl->val = XSDIRX_MODE_6G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GI_MASK:
> +			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GF_MASK:
> +			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> +			break;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_CRC:
> +		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> +		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		ctrl->val = xsdirxss->ts_is_interlaced;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> +		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> +		ctrl->val = 1 << val;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_IS_3GB:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> +		ctrl->val = val ? true : false;
> +		break;
> +	default:
> +		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx SDI Rx Subsystem
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	u32 data, i;
> +
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> +	for (i = 0; i < 0x28; i++) {
> +		data = xsdirxss_read(core, i * 4);
> +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> +			  i * 4, data);
> +	}
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> +	return 0;
> +}
> +
> +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, true);
> +}
> +
> +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, false);
> +}
> +
> +/**
> + * xsdirxss_g_frame_interval - Get the frame interval
> + * @sd: V4L2 Sub device
> + * @fi: Pointer to V4l2 Sub device frame interval structure
> + *
> + * This function is used to get the frame interval.
> + * The frame rate can be integral or fractional.
> + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
> + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fi->interval = xsdirxss->frame_interval;
> +
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		xsdirxss->frame_interval.numerator,
> +		xsdirxss->frame_interval.denominator);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 SDI Rx Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (enable) {
> +		if (!xsdirxss->vidlocked) {
> +			dev_dbg(core->dev, "Video is not locked\n");
> +			return -EINVAL;
> +		}
> +		if (xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Already streaming\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_start_stream(xsdirxss);
> +		xsdirxss->streaming = true;
> +		dev_dbg(core->dev, "Streaming started\n");
> +	} else {
> +		if (!xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Stopped streaming already\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_stop_stream(xsdirxss);
> +		xsdirxss->streaming = false;
> +		dev_dbg(core->dev, "Streaming stopped\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> +			  struct v4l2_subdev_pad_config *cfg,
> +				unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &xsdirxss->formats[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_format *fmt)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> +						 fmt->pad, fmt->which);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> +		fmt->format.width, fmt->format.height, fmt->format.field);
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *__format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	dev_dbg(xsdirxss->core.dev,
> +		"set width %d height %d code %d field %d colorspace %d\n",
> +		fmt->format.width, fmt->format.height,
> +		fmt->format.code, fmt->format.field,
> +		fmt->format.colorspace);
> +
> +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> +					     fmt->pad, fmt->which);
> +
> +	/* Currently reset the code to one fixed in hardware */
> +	/* TODO : Add checks for width height */
> +	fmt->format.code = __format->code;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 pad.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_open(struct v4l2_subdev *sd,
> +			 struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	*format = xsdirxss->default_format;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_close - Called on v4l2_close()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_close().
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_close(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_fh *fh)
> +{
> +	return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xsdirxss_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> +	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> +	.s_ctrl	= xsdirxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> +		.name	= "SDI Rx : EDH Error Count Enable",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_EDH_ALLERR_MASK,
> +		.def	= 0,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> +		.name	= "SDI Rx : EDH Error Count",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> +		.name	= "SDI Rx : EDH Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
> +		.name	= "SDI Rx : Enable Framer",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.step	= 1,
> +		.def	= true,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> +		.name	= "SDI Rx : Video Lock Window",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> +		.name	= "SDI Rx : Modes search Mask",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_DETECT_ALL_MODES,
> +		.def	= XSDIRX_DETECT_ALL_MODES,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> +		.name	= "SDI Rx : Mode Detect Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= XSDIRX_MODE_SD_OFFSET,
> +		.max	= XSDIRX_MODE_12GF_OFFSET,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_CRC,
> +		.name	= "SDI Rx : CRC Error status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> +		.name	= "SDI Rx : TS is Interlaced",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> +		.name	= "SDI Rx : Active Streams",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 1,
> +		.max	= 16,
> +		.def	= 1,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
> +		.name	= "SDI Rx : Is 3GB",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> +	.log_status = xsdirxss_log_status,
> +	.subscribe_event = xsdirxss_subscribe_event,
> +	.unsubscribe_event = xsdirxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> +	.g_frame_interval = xsdirxss_g_frame_interval,
> +	.s_stream = xsdirxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> +	.get_fmt = xsdirxss_get_format,
> +	.set_fmt = xsdirxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xsdirxss_ops = {
> +	.core = &xsdirxss_core_ops,
> +	.video = &xsdirxss_video_ops,
> +	.pad = &xsdirxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> +	.open = xsdirxss_open,
> +	.close = xsdirxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> +	struct device_node *node = xsdirxss->core.dev->of_node;
> +	struct device_node *ports = NULL;
> +	struct device_node *port = NULL;
> +	unsigned int nports = 0;
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	int ret;
> +	const char *sdi_std;
> +
> +	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> +	dev_dbg(core->dev, "EDH property = %s\n",
> +		core->include_edh ? "Present" : "Absent");
> +
> +	ret = of_property_read_string(node, "xlnx,line-rate",
> +				      &sdi_std);
> +	if (ret < 0) {
> +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> +		return ret;
> +	}
> +
> +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_6G;
> +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_3G;
> +	} else {
> +		dev_err(core->dev, "Invalid Line Rate\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> +		core->mode);
> +
> +	ports = of_get_child_by_name(node, "ports");
> +	if (!ports)
> +		ports = node;
> +
> +	for_each_child_of_node(ports, port) {
> +		const struct xvip_video_format *format;
> +		struct device_node *endpoint;
> +
> +		if (!port->name || of_node_cmp(port->name, "port"))
> +			continue;
> +
> +		format = xvip_of_get_format(port);
> +		if (IS_ERR(format)) {
> +			dev_err(core->dev, "invalid format in DT");
> +			return PTR_ERR(format);
> +		}
> +
> +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> +			format->vf_code, format->width, format->bpp);
> +
> +		if (format->vf_code != XVIP_VF_YUV_422 &&
> +		    format->vf_code != XVIP_VF_YUV_420) {
> +			dev_err(core->dev, "Incorrect UG934 video format set.\n");
> +			return -EINVAL;
> +		}
> +		xsdirxss->vip_format = format;
> +
> +		endpoint = of_get_next_child(port, NULL);
> +		if (!endpoint) {
> +			dev_err(core->dev, "No port at\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Count the number of ports. */
> +		nports++;
> +	}
> +
> +	if (nports != 1) {
> +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> +		return -EINVAL;
> +	}
> +
> +	/* Register interrupt handler */
> +	core->irq = irq_of_parse_and_map(node, 0);
> +
> +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> +	if (ret) {
> +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> +	struct v4l2_subdev *subdev;
> +	struct xsdirxss_state *xsdirxss;
> +	struct xsdirxss_core *core;
> +	struct resource *res;
> +	int ret;
> +	unsigned int num_ctrls, num_edh_ctrls = 0, i;
> +
> +	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> +	if (!xsdirxss)
> +		return -ENOMEM;
> +
> +	xsdirxss->core.dev = &pdev->dev;
> +	core = &xsdirxss->core;
> +
> +	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> +	if (IS_ERR(core->axi_clk)) {
> +		ret = PTR_ERR(core->axi_clk);
> +		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> +	if (IS_ERR(core->sdirx_clk)) {
> +		ret = PTR_ERR(core->sdirx_clk);
> +		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> +	if (IS_ERR(core->vidout_clk)) {
> +		ret = PTR_ERR(core->vidout_clk);
> +		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->axi_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->sdirx_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> +		goto rx_clk_err;
> +	}
> +
> +	ret = clk_prepare_enable(core->vidout_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> +		goto vidout_clk_err;
> +	}
> +
> +	ret = xsdirxss_parse_of(xsdirxss);
> +	if (ret < 0)
> +		goto clk_err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> +	if (IS_ERR(xsdirxss->core.iomem)) {
> +		ret = PTR_ERR(xsdirxss->core.iomem);
> +		goto clk_err;
> +	}
> +
> +	/* Reset the core */
> +	xsdirx_streamflow_control(core, false);
> +	xsdirx_core_disable(core);
> +	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_globalintr(core, true);
> +	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Initialize the default format */
> +	xsdirxss->default_format.code = xsdirxss->vip_format->code;
> +	xsdirxss->default_format.field = V4L2_FIELD_NONE;
> +	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> +	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> +	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> +	xsdirxss->formats[0] = xsdirxss->default_format;
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	subdev = &xsdirxss->subdev;
> +	v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> +	subdev->dev = &pdev->dev;
> +	subdev->internal_ops = &xsdirxss_internal_ops;
> +	strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));

Use strscpy.

> +
> +	subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	subdev->entity.ops = &xsdirxss_media_ops;
> +
> +	v4l2_set_subdevdata(subdev, xsdirxss);
> +
> +	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialise and register the controls */
> +	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> +	if (xsdirxss->core.include_edh)
> +		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> +
> +	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> +			       (num_ctrls + num_edh_ctrls));
> +
> +	for (i = 0; i < num_ctrls; i++) {
> +		struct v4l2_ctrl *ctrl;
> +
> +		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> +
> +		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +					    &xsdirxss_ctrls[i], NULL);
> +		if (!ctrl) {
> +			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +				xsdirxss_ctrls[i].name);
> +			goto error;
> +		}
> +	}
> +
> +	if (xsdirxss->core.include_edh) {
> +		for (i = 0; i < num_edh_ctrls; i++) {
> +			struct v4l2_ctrl *ctrl;
> +
> +			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +				i, xsdirxss_edh_ctrls[i].name,
> +				xsdirxss_edh_ctrls[i].id);
> +
> +			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +						    &xsdirxss_edh_ctrls[i],
> +						    NULL);
> +			if (!ctrl) {
> +				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +					xsdirxss_edh_ctrls[i].name);
> +				goto error;
> +			}
> +		}
> +	} else {
> +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");
> +	}
> +
> +	if (xsdirxss->ctrl_handler.error) {
> +		dev_err(&pdev->dev, "failed to add controls\n");
> +		ret = xsdirxss->ctrl_handler.error;
> +		goto error;
> +	}
> +
> +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> +
> +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to set controls\n");
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, xsdirxss);
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register subdev\n");
> +		goto error;
> +	}
> +
> +	xsdirxss->streaming = false;
> +
> +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> +
> +	xsdirx_core_enable(core);
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +
> +clk_err:
> +	clk_disable_unprepare(core->vidout_clk);
> +vidout_clk_err:
> +	clk_disable_unprepare(core->sdirx_clk);
> +rx_clk_err:
> +	clk_disable_unprepare(core->axi_clk);
> +	return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> +
> +static struct platform_driver xsdirxss_driver = {
> +	.driver = {
> +		.name		= "xilinx-sdirxss",
> +		.of_match_table	= xsdirxss_of_id_table,
> +	},
> +	.probe			= xsdirxss_probe,
> +	.remove			= xsdirxss_remove,
> +};
> +
> +module_platform_driver(xsdirxss_driver);
> +
> +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
> new file mode 100644
> index 0000000..983a43b
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx SDI Rx Subsystem mode and flag definitions.
> + *
> + * Copyright (C) 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * This enum is used to prepare the bitmask of modes to be detected
> + */
> +enum {
> +	XSDIRX_MODE_SD_OFFSET = 0,
> +	XSDIRX_MODE_HD_OFFSET,
> +	XSDIRX_MODE_3G_OFFSET,
> +	XSDIRX_MODE_6G_OFFSET,
> +	XSDIRX_MODE_12GI_OFFSET,
> +	XSDIRX_MODE_12GF_OFFSET,
> +	XSDIRX_MODE_NUM_SUPPORTED,
> +};
> +
> +#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
> +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GF_OFFSET))
> +
> +/*
> + * EDH Error Types
> + * ANC - Ancillary Data Packet Errors
> + * FF - Full Field Errors
> + * AP - Active Portion Errors
> + */
> +
> +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> +
> +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index f023623..4c68a10 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -83,4 +83,34 @@
>  /* Reset all event counters */
>  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
>  
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
> +
> +/* Framer Control */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
> +/* Video Lock Window Control */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
> +/* Mode search Control */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
> +/* Get Detected Mode control */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
> +/* Get CRC error status */
> +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
> +/* Get EDH error count control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
> +/* Get EDH status control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
> +/* Get Transport Interlaced status */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
> +/* Get Active Streams count */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
> +/* Is Mode 3GB */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
> +
>  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
> index 2aa357c..7b47595 100644
> --- a/include/uapi/linux/xilinx-v4l2-events.h
> +++ b/include/uapi/linux/xilinx-v4l2-events.h
> @@ -18,4 +18,13 @@
>  /* Stream Line Buffer full */
>  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
>  
> +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
> +/* Video Unlock event */
> +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> +/* Video in to AXI4 Stream core underflowed */
> +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> +/* Video in to AXI4 Stream core overflowed */
> +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> +
>  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> 

I am concerned about this driver: I see that none of the *_dv_timings callbacks
are implemented. I would expect to see that for a video receiver. There is also
no g_input_status implemented.

Take a look at another SDI driver: drivers/media/spi/gs1662.c

Some of the controls you add in this driver can likely be dropped. Especially
those controls that are not specific to the Xilinx implementation but are
generic for any SDI receiver, should be looked at closely: those are
candidates for becoming standard controls.

But the documentation above is simply insufficient for me to tell what is
SDI specific and what is implementation specific.

Also, I'm no SDI expert, certainly not for the UHD-SDI.

Regards,

	Hans

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-04 13:55   ` Vishal Sagar
  (?)
@ 2019-06-13 22:05     ` Hyun Kwon
  -1 siblings, 0 replies; 41+ messages in thread
From: Hyun Kwon @ 2019-06-13 22:05 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari

Hi Vishal,

Thanks for the patch.

On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> streams from SDI sources like SDI broadcast equipment like cameras and
> mixers. This block outputs either native SDI, native video or
> AXI4-Stream compliant data stream for further processing. Please refer
> to PG290 for details.
> 
> The driver is used to configure the IP to add framer, search for
> specific modes, get the detected mode, stream parameters, errors, etc.
> It also generates events for video lock/unlock, bridge over/under flow.
> 
> The driver supports only 10 bpc YUV 422 media bus format. It also
> decodes the stream parameters based on the ST352 packet embedded in the
> stream. In case the ST352 packet isn't present in the stream, the core's
> detected properties are used to set stream properties.
> 
> The driver currently supports only the AXI4-Stream configuration.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig          |   11 +
>  drivers/media/platform/xilinx/Makefile         |    1 +
>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>  6 files changed, 1960 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
>  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index cd1a0fd..0c68caa 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
>  	  Bridge. The driver is used to set the number of active lanes and
>  	  get short packet data.
>  
> +config VIDEO_XILINX_SDIRXSS
> +	tristate "Xilinx UHD SDI Rx Subsystem"
> +	help
> +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> +	  based driver that takes input from a SDI source like SDI camera and
> +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> +	  AXI4-Stream bridge. The driver is used to set different stream
> +	  detection modes and identify stream properties to properly configure
> +	  downstream.
> +
>  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 6119a34..223f2ea 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> new file mode 100644
> index 0000000..ba2d9d0
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,1846 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.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/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-sdirxss.h>
> +#include <linux/xilinx-v4l2-events.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"
> +
> +/*
> + * SDI Rx register map, bitmask and offsets
> + */
> +#define XSDIRX_RST_CTRL_REG		0x00
> +#define XSDIRX_MDL_CTRL_REG		0x04
> +#define XSDIRX_GLBL_IER_REG		0x0C
> +#define XSDIRX_ISR_REG			0x10
> +#define XSDIRX_IER_REG			0x14
> +#define XSDIRX_ST352_VALID_REG		0x18
> +#define XSDIRX_ST352_DS1_REG		0x1C
> +#define XSDIRX_ST352_DS3_REG		0x20
> +#define XSDIRX_ST352_DS5_REG		0x24
> +#define XSDIRX_ST352_DS7_REG		0x28
> +#define XSDIRX_ST352_DS9_REG		0x2C
> +#define XSDIRX_ST352_DS11_REG		0x30
> +#define XSDIRX_ST352_DS13_REG		0x34
> +#define XSDIRX_ST352_DS15_REG		0x38
> +#define XSDIRX_VERSION_REG		0x3C
> +#define XSDIRX_SS_CONFIG_REG		0x40
> +#define XSDIRX_MODE_DET_STAT_REG	0x44
> +#define XSDIRX_TS_DET_STAT_REG		0x48
> +#define XSDIRX_EDH_STAT_REG		0x4C
> +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> +#define XSDIRX_EDH_ERRCNT_REG		0x54
> +#define XSDIRX_CRC_ERRCNT_REG		0x58
> +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> +#define XSDIRX_SB_RX_STS_REG		0x60
> +
> +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)

I'd rather put these under corresponding offsets. In that way, it'd be easier
to follow. Up to you.

> +
> +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)

These are not actually mask, so I'd remove _MASK. But it maybe just me.

> +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> +
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
> +
> +#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
> +
> +#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
> +#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
> +#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
> +#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
> +
> +#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
> +				XSDIRX_INTR_VIDUNLOCK_MASK |\
> +				XSDIRX_INTR_OVERFLOW_MASK |\
> +				XSDIRX_INTR_UNDERFLOW_MASK)
> +
> +#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
> +#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
> +#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
> +#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
> +#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
> +#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
> +#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
> +#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
> +
> +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
> +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
> +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
> +
> +#define XSDIRX_ACTIVE_STREAMS_1		0x0
> +#define XSDIRX_ACTIVE_STREAMS_2		0x1
> +#define XSDIRX_ACTIVE_STREAMS_4		0x2
> +#define XSDIRX_ACTIVE_STREAMS_8		0x3
> +#define XSDIRX_ACTIVE_STREAMS_16	0x4
> +
> +#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
> +#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
> +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
> +#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
> +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
> +#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
> +#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
> +
> +#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
> +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
> +#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
> +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
> +#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
> +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
> +#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
> +#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
> +#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
> +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
> +#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
> +
> +#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
> +#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
> +#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
> +#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
> +#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
> +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
> +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
> +
> +#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
> +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
> +#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
> +#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
> +#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
> +#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
> +
> +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
> +
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
> +
> +/* Number of media pads */
> +#define XSDIRX_MEDIA_PADS	(1)
> +
> +#define XSDIRX_DEFAULT_WIDTH	(1920)
> +#define XSDIRX_DEFAULT_HEIGHT	(1080)
> +
> +#define XSDIRX_MAX_STR_LENGTH	16
> +
> +#define XSDIRXSS_SDI_STD_3G		0
> +#define XSDIRXSS_SDI_STD_6G		1
> +#define XSDIRXSS_SDI_STD_12G_8DS	2
> +
> +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
> +
> +#define XSDIRX_MODE_HD_MASK	0x0
> +#define XSDIRX_MODE_SD_MASK	0x1
> +#define XSDIRX_MODE_3G_MASK	0x2
> +#define XSDIRX_MODE_6G_MASK	0x4
> +#define XSDIRX_MODE_12GI_MASK	0x5
> +#define XSDIRX_MODE_12GF_MASK	0x6
> +
> +/*
> + * Maximum number of events per file handle.
> + */

Nit, This doesn't have to be multieline comment.

> +#define XSDIRX_MAX_EVENTS	(128)
> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE_MASK	0xFF
> +#define XST352_PAYLOAD_BYTE1_SHIFT	0
> +#define XST352_PAYLOAD_BYTE2_SHIFT	8
> +#define XST352_PAYLOAD_BYTE3_SHIFT	16
> +#define XST352_PAYLOAD_BYTE4_SHIFT	24
> +
> +#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
> +#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
> +#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
> +#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
> +#define XST352_BYTE1_ST372_DL_3GB		0x8A
> +#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
> +#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
> +#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
> +#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
> +#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
> +
> +#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
> +#define XST352_BYTE2_TS_TYPE_OFFSET		15
> +#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
> +#define XST352_BYTE2_PIC_TYPE_OFFSET		14
> +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
> +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
> +
> +#define XST352_BYTE2_FPS_MASK			0xF
> +#define XST352_BYTE2_FPS_SHIFT			8
> +#define XST352_BYTE2_FPS_24F			0x2
> +#define XST352_BYTE2_FPS_24			0x3
> +#define XST352_BYTE2_FPS_48F			0x4
> +#define XST352_BYTE2_FPS_25			0x5
> +#define XST352_BYTE2_FPS_30F			0x6
> +#define XST352_BYTE2_FPS_30			0x7
> +#define XST352_BYTE2_FPS_48			0x8
> +#define XST352_BYTE2_FPS_50			0x9
> +#define XST352_BYTE2_FPS_60F			0xA
> +#define XST352_BYTE2_FPS_60			0xB
> +/* Table 4 ST 2081-10:2015 */
> +#define XST352_BYTE2_FPS_96			0xC
> +#define XST352_BYTE2_FPS_100			0xD
> +#define XST352_BYTE2_FPS_120			0xE
> +#define XST352_BYTE2_FPS_120F			0xF
> +
> +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
> +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
> +
> +#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
> +#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
> +#define XST352_BYTE3_COLOR_FORMAT_422		0x0
> +#define XST352_BYTE3_COLOR_FORMAT_420		0x3
> +
> +/**
> + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
> + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
> + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> + */
> +enum sdi_family_enc {
> +	XSDIRX_SMPTE_ST_274	= 0,
> +	XSDIRX_SMPTE_ST_296	= 1,
> +	XSDIRX_SMPTE_ST_2048_2	= 2,
> +	XSDIRX_SMPTE_ST_295	= 3,
> +	XSDIRX_NTSC		= 8,
> +	XSDIRX_PAL		= 9,
> +	XSDIRX_TS_UNKNOWN	= 15
> +};
> +
> +/**
> + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @include_edh: EDH processor presence
> + * @mode: 3G/6G/12G mode
> + * @axi_clk: Axi lite interface clock
> + * @sdirx_clk: SDI Rx GT clock
> + * @vidout_clk: Video clock
> + */
> +struct xsdirxss_core {
> +	struct device *dev;
> +	void __iomem *iomem;
> +	int irq;
> +	bool include_edh;

I wonder if this is required. I guess it's not critical anyway, so up to you.

> +	int mode;
> +	struct clk *axi_clk;
> +	struct clk *sdirx_clk;
> +	struct clk *vidout_clk;
> +};
> +
> +/**
> + * struct xsdirxss_state - SDI Rx Subsystem device structure
> + * @core: Core structure for MIPI SDI Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @event: Holds the video unlock event
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @vip_format: format information corresponding to the active format
> + * @pads: media pads
> + * @streaming: Flag for storing streaming state
> + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xsdirxss_state {
> +	struct xsdirxss_core core;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_event event;
> +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];

It's always single port, so this doesn't have to be an array.

> +	struct v4l2_mbus_framefmt default_format;
> +	struct v4l2_fract frame_interval;
> +	const struct xvip_video_format *vip_format;
> +	struct media_pad pads[XSDIRX_MEDIA_PADS];

Ditto.

> +	bool streaming;
> +	bool vidlocked;
> +	bool ts_is_interlaced;
> +};
> +
> +static inline struct xsdirxss_state *
> +to_xsdirxssstate(struct v4l2_subdev *subdev)
> +{
> +	return container_of(subdev, struct xsdirxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> +{
> +	return ioread32(xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> +				  u32 value)
> +{
> +	iowrite32(value, xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 clr)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> +}
> +
> +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 set)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> +}
> +
> +static void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static void xsdirx_core_enable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> +{
> +	u32 i, val;
> +
> +	mask &= XSDIRX_DETECT_ALL_MODES;
> +	if (!mask) {
> +		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> +
> +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);

() is not needed here. I'd avoid unneeded ().

> +
> +	if (hweight16(mask) > 1) {
> +		/* Multi mode detection as more than 1 bit set in mask */
> +		dev_dbg(core->dev, "Detect multiple modes\n");
> +		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> +			switch (mask & (1 << i)) {
> +			case BIT(XSDIRX_MODE_SD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_HD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_3G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_6G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GI_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GF_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +				break;
> +			}
> +		}
> +		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> +	} else {
> +		/* Fixed Mode */
> +		u32 forced_mode_mask = 0;

Setting this under default below would make it clearer. But up to you.

> +
> +		dev_dbg(core->dev, "Detect fixed mode\n");
> +
> +		/* Find offset of first bit set */
> +		switch (__ffs(mask)) {
> +		case XSDIRX_MODE_SD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_SD_MASK;
> +			break;
> +		case XSDIRX_MODE_HD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_HD_MASK;
> +			break;
> +		case XSDIRX_MODE_3G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_3G_MASK;
> +			break;
> +		case XSDIRX_MODE_6G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_6G_MASK;
> +			break;
> +		case XSDIRX_MODE_12GI_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> +			break;
> +		case XSDIRX_MODE_12GF_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> +			break;
> +		}
> +		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> +			forced_mode_mask);
> +		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> +	}
> +
> +	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> +		val);
> +	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> +
> +	return 0;
> +}
> +
> +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
> +{
> +	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> +	val = enable & XSDIRX_EDH_ALLERR_MASK;
> +
> +	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> +{
> +	/*
> +	 * The video lock window is the amount of time for which the
> +	 * the mode and transport stream should be locked to get the
> +	 * video lock interrupt.
> +	 */
> +	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> +}
> +
> +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)

The flag can be determined at build time, so this 'if' isn't needed. Then
the disabling call can be added to xsdirxss_remove().

> +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +}
> +
> +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}
> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);

Even these can be inlined. I don't see need for having dynamic check.

> +}
> +
> +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);

Ditto.

> +}
> +
> +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
> +{
> +	/* The sdi to native bridge is followed by native to axis4 bridge */
> +	if (enable) {
> +		xsdirx_axis4_bridge_control(core, enable);
> +		xsdirx_vid_bridge_control(core, enable);
> +	} else {
> +		xsdirx_vid_bridge_control(core, enable);
> +		xsdirx_axis4_bridge_control(core, enable);
> +	}
> +}
> +
> +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> +{
> +	xsdirx_streamflow_control(core, false);
> +}

These thin wrapers don't seem useful. Wouldn't it be better to inline most of
these?

> +
> +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> +				   u32 framerate)
> +{
> +	switch (framerate) {
> +	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 25000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_48HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 50000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 60000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 60000;
> +		break;
> +	default:
> +		frame_interval->numerator = 1;
> +		frame_interval->denominator = 1;
> +	}
> +}
> +
> +/**
> + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> + * @state: pointer to driver state
> + *
> + * This function decodes the stream's ST352 payload (if available) to get
> + * stream properties like width, height, picture type (interlaced/progressive),
> + * etc.
> + *
> + * Return: 0 for success else errors
> + */
> +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> +{
> +	struct xsdirxss_core *core = &state->core;
> +	u32 mode, payload = 0, val, family, valid, tscan;
> +	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> +	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> +	struct v4l2_mbus_framefmt *format = &state->formats[0];
> +
> +	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +
> +	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> +		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
> +		return -EINVAL;
> +	}
> +
> +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> +				XST352_PAYLOAD_BYTE_MASK;
> +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> +				XST352_BYTE2_PIC_TYPE_OFFSET;
> +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> +				XST352_BYTE2_FPS_MASK;
> +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> +				XST352_BYTE2_TS_TYPE_OFFSET;

Please align consistently throughout the patch. I believe the checkpatch
--strict warns on these.

> +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> +	} else {
> +		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> +			mode);
> +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> +	}
> +
> +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;

Just nit. Alignment.

> +	state->ts_is_interlaced = tscan ? false : true;
> +
> +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> +		state->ts_is_interlaced, family);
> +
> +	switch (mode) {
> +	case XSDIRX_MODE_HD_MASK:
> +		if (!valid) {
> +			/* No payload obtained */
> +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> +				framerate, tscan);
> +			/*
> +			 * NOTE : A progressive segmented frame pSF will be
> +			 * reported incorrectly as Interlaced as we rely on IP's
> +			 * transport scan locked bit.
> +			 */
> +			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
> +
> +			switch (framerate) {
> +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +				if (family == XSDIRX_SMPTE_ST_296) {
> +					format->width = 1280;
> +					format->height = 720;
> +					format->field = V4L2_FIELD_NONE;
> +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> +					format->width = 2048;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				} else {
> +					format->width = 1920;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				}
> +				break;
> +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +				if (family == XSDIRX_SMPTE_ST_274) {
> +					format->width = 1920;
> +					format->height = 1080;
> +				} else {
> +					format->width = 1280;
> +					format->height = 720;
> +				}
> +				format->field = V4L2_FIELD_NONE;
> +				break;
> +			default:
> +				format->width = 1920;
> +				format->height = 1080;
> +				format->field = V4L2_FIELD_NONE;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "Got the payload\n");
> +			switch (byte1) {
> +			case XST352_BYTE1_ST292_1x720L_1_5G:
> +				/* SMPTE ST 292-1 for 720 line payloads */
> +				format->width = 1280;
> +				format->height = 720;
> +				break;
> +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> +				/* SMPTE ST 292-1 for 1080 line payloads */
> +				format->height = 1080;
> +				if (active_luma)
> +					format->width = 2048;
> +				else
> +					format->width = 1920;
> +				break;
> +			default:
> +				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case XSDIRX_MODE_SD_MASK:
> +		format->field = V4L2_FIELD_ALTERNATE;
> +
> +		switch (family) {
> +		case XSDIRX_NTSC:
> +			format->width = 720;
> +			format->height = 486;
> +			break;
> +		case XSDIRX_PAL:
> +			format->width = 720;
> +			format->height = 576;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_3G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST425_2008_750L_3GB:
> +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x720L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->width = 1280;
> +			format->height = 720;
> +			break;
> +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> +			/* ST352 Table SMPTE 425-1 */
> +		case XST352_BYTE1_ST372_DL_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x1080L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->height = 1080;
> +			if (active_luma)
> +				format->width = 2048;
> +			else
> +				format->width = 1920;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_6G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> +			/* Dual link 6G */
> +		case XST352_BYTE1_ST2081_10_2160L_6G:
> +			/* Table 3 SMPTE ST 2081-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_12GI_MASK:
> +	case XSDIRX_MODE_12GF_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2082_10_2160L_12G:
> +			/* Section 4.3.1 SMPTE ST 2082-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		dev_err(core->dev, "Invalid Mode\n");
> +		return -EINVAL;
> +	}

Could you please take another look if this can be organized simpler? This is
a little too many switch - case and if statements.

> +
> +	if (valid) {
> +		if (pic_type)
> +			format->field = V4L2_FIELD_NONE;
> +		else
> +			format->field = V4L2_FIELD_ALTERNATE;
> +	}
> +
> +	if (format->field == V4L2_FIELD_ALTERNATE)
> +		format->height = format->height / 2;
> +
> +	switch (sampling) {
> +	case XST352_BYTE3_COLOR_FORMAT_422:
> +		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +		break;
> +	default:
> +		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> +		return -EINVAL;
> +	}
> +
> +	xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
> +		format->width, format->height, format->field, payload, val);
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		state->frame_interval.numerator,
> +		state->frame_interval.denominator);
> +	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> + * in the interrupt clear register. The interrupt status register is read only.

Thanks for the doc. I was about to ask. So I guess it's read - to -clear.

> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> +{
> +	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> +	struct xsdirxss_core *core = &state->core;
> +	u32 status;
> +
> +	status = xsdirxss_read(core, XSDIRX_ISR_REG);
> +	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> +		u32 val1, val2;
> +
> +		dev_dbg(core->dev, "video lock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> +
> +		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +
> +		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> +		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> +			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> +				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> +
> +			dev_dbg(core->dev, "mode & ts lock occurred\n");
> +
> +			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> +			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +
> +			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +
> +			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> +			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> +
> +			if (!xsdirx_get_stream_properties(state)) {
> +				memset(&state->event, 0, sizeof(state->event));
> +				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> +				state->event.u.src_change.changes =
> +					V4L2_EVENT_SRC_CH_RESOLUTION;
> +				v4l2_subdev_notify_event(&state->subdev,
> +							 &state->event);
> +
> +				state->vidlocked = true;
> +			} else {
> +				dev_err(core->dev, "Unable to get stream properties!\n");
> +				state->vidlocked = false;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "video unlock before video lock!\n");
> +			state->vidlocked = false;
> +		}
> +	}
> +
> +	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> +		dev_dbg(core->dev, "video unlock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> +		xsdirx_streamdowncb(core);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +
> +		state->vidlocked = false;
> +	}
> +
> +	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +
> +	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	int ret;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (sub->type) {
> +	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> +	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> +	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> +		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> +		break;
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		ret = v4l2_src_change_event_subscribe(fh, sub);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> +				      struct v4l2_fh *fh,
> +				      struct v4l2_event_subscription *sub)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> +	return v4l2_event_unsubscribe(fh, sub);
> +}
> +
> +/**
> + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> + * Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	int ret = 0;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,

This doesn't have to be a new line.

> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +
> +	if (xsdirxss->streaming) {
> +		dev_err(core->dev, "Cannot set controls while streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	xsdirx_core_disable(core);

Not sure why this is needed here. Can this be part of s_stream?

> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_FRAMER:
> +		xsdirx_framer(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> +		xsdirx_setvidlockwindow(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> +		xsdirx_setedherrcnttrigger(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> +		if (ctrl->val) {
> +			if (core->mode == XSDIRXSS_SDI_STD_3G) {
> +				dev_dbg(core->dev, "Upto 3G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			if (core->mode == XSDIRXSS_SDI_STD_6G) {
> +				dev_dbg(core->dev, "Upto 6G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			ret = xsdirx_set_modedetect(core, ctrl->val);
> +		} else {
> +			dev_err(core->dev, "Select at least one mode!\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SS_EN_MASK);
> +		return -EINVAL;
> +	}
> +	xsdirx_core_enable(core);

Ditto.

> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	u32 val;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +		switch (val) {
> +		case XSDIRX_MODE_SD_MASK:
> +			ctrl->val = XSDIRX_MODE_SD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_HD_MASK:
> +			ctrl->val = XSDIRX_MODE_HD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_3G_MASK:
> +			ctrl->val = XSDIRX_MODE_3G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_6G_MASK:
> +			ctrl->val = XSDIRX_MODE_6G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GI_MASK:
> +			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GF_MASK:
> +			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> +			break;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_CRC:
> +		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> +		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		ctrl->val = xsdirxss->ts_is_interlaced;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> +		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> +		ctrl->val = 1 << val;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_IS_3GB:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> +		ctrl->val = val ? true : false;
> +		break;
> +	default:
> +		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx SDI Rx Subsystem
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	u32 data, i;

'data' can be declared inside the for loop below.

> +
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> +	for (i = 0; i < 0x28; i++) {
> +		data = xsdirxss_read(core, i * 4);
> +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> +			  i * 4, data);
> +	}
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> +	return 0;
> +}
> +
> +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, true);
> +}
> +
> +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, false);
> +}

Ditto. Not sure if these single liner wrappers are useful.

> +
> +/**
> + * xsdirxss_g_frame_interval - Get the frame interval
> + * @sd: V4L2 Sub device
> + * @fi: Pointer to V4l2 Sub device frame interval structure
> + *
> + * This function is used to get the frame interval.
> + * The frame rate can be integral or fractional.
> + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
> + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fi->interval = xsdirxss->frame_interval;
> +
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		xsdirxss->frame_interval.numerator,
> +		xsdirxss->frame_interval.denominator);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 SDI Rx Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (enable) {
> +		if (!xsdirxss->vidlocked) {
> +			dev_dbg(core->dev, "Video is not locked\n");
> +			return -EINVAL;
> +		}
> +		if (xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Already streaming\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_start_stream(xsdirxss);
> +		xsdirxss->streaming = true;
> +		dev_dbg(core->dev, "Streaming started\n");
> +	} else {
> +		if (!xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Stopped streaming already\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_stop_stream(xsdirxss);
> +		xsdirxss->streaming = false;
> +		dev_dbg(core->dev, "Streaming stopped\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> +			  struct v4l2_subdev_pad_config *cfg,
> +				unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &xsdirxss->formats[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_format *fmt)

Please check the alignment.

> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> +						 fmt->pad, fmt->which);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> +		fmt->format.width, fmt->format.height, fmt->format.field);
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_format *fmt)

Please check the alignment.

> +{
> +	struct v4l2_mbus_framefmt *__format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	dev_dbg(xsdirxss->core.dev,
> +		"set width %d height %d code %d field %d colorspace %d\n",
> +		fmt->format.width, fmt->format.height,
> +		fmt->format.code, fmt->format.field,
> +		fmt->format.colorspace);
> +
> +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> +					     fmt->pad, fmt->which);
> +
> +	/* Currently reset the code to one fixed in hardware */
> +	/* TODO : Add checks for width height */
> +	fmt->format.code = __format->code;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 pad.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_open(struct v4l2_subdev *sd,
> +			 struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	*format = xsdirxss->default_format;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_close - Called on v4l2_close()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_close().
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_close(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_fh *fh)
> +{
> +	return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xsdirxss_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> +	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> +	.s_ctrl	= xsdirxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> +		.name	= "SDI Rx : EDH Error Count Enable",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_EDH_ALLERR_MASK,
> +		.def	= 0,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> +		.name	= "SDI Rx : EDH Error Count",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> +		.name	= "SDI Rx : EDH Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
> +		.name	= "SDI Rx : Enable Framer",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.step	= 1,
> +		.def	= true,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> +		.name	= "SDI Rx : Video Lock Window",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> +		.name	= "SDI Rx : Modes search Mask",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_DETECT_ALL_MODES,
> +		.def	= XSDIRX_DETECT_ALL_MODES,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> +		.name	= "SDI Rx : Mode Detect Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= XSDIRX_MODE_SD_OFFSET,
> +		.max	= XSDIRX_MODE_12GF_OFFSET,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_CRC,
> +		.name	= "SDI Rx : CRC Error status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> +		.name	= "SDI Rx : TS is Interlaced",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> +		.name	= "SDI Rx : Active Streams",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 1,
> +		.max	= 16,
> +		.def	= 1,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
> +		.name	= "SDI Rx : Is 3GB",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> +	.log_status = xsdirxss_log_status,
> +	.subscribe_event = xsdirxss_subscribe_event,
> +	.unsubscribe_event = xsdirxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> +	.g_frame_interval = xsdirxss_g_frame_interval,
> +	.s_stream = xsdirxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> +	.get_fmt = xsdirxss_get_format,
> +	.set_fmt = xsdirxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xsdirxss_ops = {
> +	.core = &xsdirxss_core_ops,
> +	.video = &xsdirxss_video_ops,
> +	.pad = &xsdirxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> +	.open = xsdirxss_open,
> +	.close = xsdirxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> +	struct device_node *node = xsdirxss->core.dev->of_node;
> +	struct device_node *ports = NULL;
> +	struct device_node *port = NULL;
> +	unsigned int nports = 0;
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	int ret;
> +	const char *sdi_std;
> +
> +	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> +	dev_dbg(core->dev, "EDH property = %s\n",
> +		core->include_edh ? "Present" : "Absent");
> +
> +	ret = of_property_read_string(node, "xlnx,line-rate",
> +				      &sdi_std);

Single line is enough.

> +	if (ret < 0) {
> +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> +		return ret;
> +	}
> +
> +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_6G;
> +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_3G;
> +	} else {
> +		dev_err(core->dev, "Invalid Line Rate\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> +		core->mode);
> +
> +	ports = of_get_child_by_name(node, "ports");
> +	if (!ports)
> +		ports = node;
> +
> +	for_each_child_of_node(ports, port) {
> +		const struct xvip_video_format *format;
> +		struct device_node *endpoint;
> +
> +		if (!port->name || of_node_cmp(port->name, "port"))
> +			continue;
> +
> +		format = xvip_of_get_format(port);
> +		if (IS_ERR(format)) {
> +			dev_err(core->dev, "invalid format in DT");
> +			return PTR_ERR(format);
> +		}
> +
> +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> +			format->vf_code, format->width, format->bpp);

The commit mentions this driver supports only 10bpc. Wouldn't it be better to
check it here?

> +
> +		if (format->vf_code != XVIP_VF_YUV_422 &&
> +		    format->vf_code != XVIP_VF_YUV_420) {
> +			dev_err(core->dev, "Incorrect UG934 video format set.\n");

You can make this single line as below,

	dev_err(core->dev,
		"Incorrect UG934 video format set.\n");

> +			return -EINVAL;
> +		}
> +		xsdirxss->vip_format = format;
> +
> +		endpoint = of_get_next_child(port, NULL);
> +		if (!endpoint) {
> +			dev_err(core->dev, "No port at\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Count the number of ports. */
> +		nports++;
> +	}
> +
> +	if (nports != 1) {
> +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> +		return -EINVAL;
> +	}
> +
> +	/* Register interrupt handler */
> +	core->irq = irq_of_parse_and_map(node, 0);
> +
> +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> +	if (ret) {
> +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> +	struct v4l2_subdev *subdev;
> +	struct xsdirxss_state *xsdirxss;
> +	struct xsdirxss_core *core;
> +	struct resource *res;
> +	int ret;
> +	unsigned int num_ctrls, num_edh_ctrls = 0, i;
> +
> +	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> +	if (!xsdirxss)
> +		return -ENOMEM;
> +
> +	xsdirxss->core.dev = &pdev->dev;
> +	core = &xsdirxss->core;
> +
> +	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> +	if (IS_ERR(core->axi_clk)) {
> +		ret = PTR_ERR(core->axi_clk);
> +		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> +	if (IS_ERR(core->sdirx_clk)) {
> +		ret = PTR_ERR(core->sdirx_clk);
> +		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> +	if (IS_ERR(core->vidout_clk)) {
> +		ret = PTR_ERR(core->vidout_clk);
> +		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->axi_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->sdirx_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> +		goto rx_clk_err;
> +	}
> +
> +	ret = clk_prepare_enable(core->vidout_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> +		goto vidout_clk_err;
> +	}
> +
> +	ret = xsdirxss_parse_of(xsdirxss);
> +	if (ret < 0)
> +		goto clk_err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> +	if (IS_ERR(xsdirxss->core.iomem)) {
> +		ret = PTR_ERR(xsdirxss->core.iomem);
> +		goto clk_err;
> +	}
> +
> +	/* Reset the core */
> +	xsdirx_streamflow_control(core, false);
> +	xsdirx_core_disable(core);
> +	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_globalintr(core, true);
> +	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Initialize the default format */
> +	xsdirxss->default_format.code = xsdirxss->vip_format->code;
> +	xsdirxss->default_format.field = V4L2_FIELD_NONE;
> +	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> +	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> +	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> +	xsdirxss->formats[0] = xsdirxss->default_format;
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	subdev = &xsdirxss->subdev;
> +	v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> +	subdev->dev = &pdev->dev;
> +	subdev->internal_ops = &xsdirxss_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 = &xsdirxss_media_ops;
> +
> +	v4l2_set_subdevdata(subdev, xsdirxss);
> +
> +	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialise and register the controls */
> +	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> +	if (xsdirxss->core.include_edh)
> +		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> +
> +	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> +			       (num_ctrls + num_edh_ctrls));
> +
> +	for (i = 0; i < num_ctrls; i++) {
> +		struct v4l2_ctrl *ctrl;
> +
> +		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> +
> +		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +					    &xsdirxss_ctrls[i], NULL);
> +		if (!ctrl) {
> +			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +				xsdirxss_ctrls[i].name);
> +			goto error;
> +		}
> +	}
> +
> +	if (xsdirxss->core.include_edh) {
> +		for (i = 0; i < num_edh_ctrls; i++) {
> +			struct v4l2_ctrl *ctrl;
> +
> +			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +				i, xsdirxss_edh_ctrls[i].name,
> +				xsdirxss_edh_ctrls[i].id);
> +
> +			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +						    &xsdirxss_edh_ctrls[i],
> +						    NULL);
> +			if (!ctrl) {
> +				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +					xsdirxss_edh_ctrls[i].name);
> +				goto error;
> +			}
> +		}
> +	} else {
> +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");

Is this worth being here? There's already an equivalent print for dt is parsed.

> +	}
> +
> +	if (xsdirxss->ctrl_handler.error) {
> +		dev_err(&pdev->dev, "failed to add controls\n");
> +		ret = xsdirxss->ctrl_handler.error;
> +		goto error;
> +	}
> +
> +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> +
> +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to set controls\n");
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, xsdirxss);
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register subdev\n");
> +		goto error;
> +	}
> +
> +	xsdirxss->streaming = false;
> +
> +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> +
> +	xsdirx_core_enable(core);
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +
> +clk_err:
> +	clk_disable_unprepare(core->vidout_clk);
> +vidout_clk_err:
> +	clk_disable_unprepare(core->sdirx_clk);
> +rx_clk_err:
> +	clk_disable_unprepare(core->axi_clk);
> +	return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> +
> +static struct platform_driver xsdirxss_driver = {
> +	.driver = {
> +		.name		= "xilinx-sdirxss",
> +		.of_match_table	= xsdirxss_of_id_table,
> +	},
> +	.probe			= xsdirxss_probe,
> +	.remove			= xsdirxss_remove,
> +};
> +
> +module_platform_driver(xsdirxss_driver);
> +
> +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
> new file mode 100644
> index 0000000..983a43b
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx SDI Rx Subsystem mode and flag definitions.
> + *
> + * Copyright (C) 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * This enum is used to prepare the bitmask of modes to be detected
> + */
> +enum {
> +	XSDIRX_MODE_SD_OFFSET = 0,
> +	XSDIRX_MODE_HD_OFFSET,
> +	XSDIRX_MODE_3G_OFFSET,
> +	XSDIRX_MODE_6G_OFFSET,
> +	XSDIRX_MODE_12GI_OFFSET,
> +	XSDIRX_MODE_12GF_OFFSET,
> +	XSDIRX_MODE_NUM_SUPPORTED,
> +};
> +
> +#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
> +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GF_OFFSET))
> +
> +/*
> + * EDH Error Types
> + * ANC - Ancillary Data Packet Errors
> + * FF - Full Field Errors
> + * AP - Active Portion Errors
> + */
> +
> +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> +
> +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index f023623..4c68a10 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -83,4 +83,34 @@
>  /* Reset all event counters */
>  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
>  
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
> +
> +/* Framer Control */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
> +/* Video Lock Window Control */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
> +/* Mode search Control */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
> +/* Get Detected Mode control */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
> +/* Get CRC error status */
> +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
> +/* Get EDH error count control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
> +/* Get EDH status control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
> +/* Get Transport Interlaced status */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
> +/* Get Active Streams count */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
> +/* Is Mode 3GB */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
> +
>  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
> index 2aa357c..7b47595 100644
> --- a/include/uapi/linux/xilinx-v4l2-events.h
> +++ b/include/uapi/linux/xilinx-v4l2-events.h
> @@ -18,4 +18,13 @@
>  /* Stream Line Buffer full */
>  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
>  
> +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
> +/* Video Unlock event */
> +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> +/* Video in to AXI4 Stream core underflowed */
> +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> +/* Video in to AXI4 Stream core overflowed */
> +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)

Could you please double-check if all events / controls are hard-required? More
of these will make generic v4l applications harder to run on this driver. It'd
be nicer if some of these (ex, from sdi spec) can be shared for any sdi if
possible?

Thanks,
-hyun

> +
>  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> -- 
> 1.8.3.1
> 
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-13 22:05     ` Hyun Kwon
  0 siblings, 0 replies; 41+ messages in thread
From: Hyun Kwon @ 2019-06-13 22:05 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari

Hi Vishal,

Thanks for the patch.

On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> streams from SDI sources like SDI broadcast equipment like cameras and
> mixers. This block outputs either native SDI, native video or
> AXI4-Stream compliant data stream for further processing. Please refer
> to PG290 for details.
> 
> The driver is used to configure the IP to add framer, search for
> specific modes, get the detected mode, stream parameters, errors, etc.
> It also generates events for video lock/unlock, bridge over/under flow.
> 
> The driver supports only 10 bpc YUV 422 media bus format. It also
> decodes the stream parameters based on the ST352 packet embedded in the
> stream. In case the ST352 packet isn't present in the stream, the core's
> detected properties are used to set stream properties.
> 
> The driver currently supports only the AXI4-Stream configuration.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig          |   11 +
>  drivers/media/platform/xilinx/Makefile         |    1 +
>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>  6 files changed, 1960 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
>  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index cd1a0fd..0c68caa 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
>  	  Bridge. The driver is used to set the number of active lanes and
>  	  get short packet data.
>  
> +config VIDEO_XILINX_SDIRXSS
> +	tristate "Xilinx UHD SDI Rx Subsystem"
> +	help
> +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> +	  based driver that takes input from a SDI source like SDI camera and
> +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> +	  AXI4-Stream bridge. The driver is used to set different stream
> +	  detection modes and identify stream properties to properly configure
> +	  downstream.
> +
>  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 6119a34..223f2ea 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> new file mode 100644
> index 0000000..ba2d9d0
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,1846 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.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/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-sdirxss.h>
> +#include <linux/xilinx-v4l2-events.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"
> +
> +/*
> + * SDI Rx register map, bitmask and offsets
> + */
> +#define XSDIRX_RST_CTRL_REG		0x00
> +#define XSDIRX_MDL_CTRL_REG		0x04
> +#define XSDIRX_GLBL_IER_REG		0x0C
> +#define XSDIRX_ISR_REG			0x10
> +#define XSDIRX_IER_REG			0x14
> +#define XSDIRX_ST352_VALID_REG		0x18
> +#define XSDIRX_ST352_DS1_REG		0x1C
> +#define XSDIRX_ST352_DS3_REG		0x20
> +#define XSDIRX_ST352_DS5_REG		0x24
> +#define XSDIRX_ST352_DS7_REG		0x28
> +#define XSDIRX_ST352_DS9_REG		0x2C
> +#define XSDIRX_ST352_DS11_REG		0x30
> +#define XSDIRX_ST352_DS13_REG		0x34
> +#define XSDIRX_ST352_DS15_REG		0x38
> +#define XSDIRX_VERSION_REG		0x3C
> +#define XSDIRX_SS_CONFIG_REG		0x40
> +#define XSDIRX_MODE_DET_STAT_REG	0x44
> +#define XSDIRX_TS_DET_STAT_REG		0x48
> +#define XSDIRX_EDH_STAT_REG		0x4C
> +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> +#define XSDIRX_EDH_ERRCNT_REG		0x54
> +#define XSDIRX_CRC_ERRCNT_REG		0x58
> +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> +#define XSDIRX_SB_RX_STS_REG		0x60
> +
> +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)

I'd rather put these under corresponding offsets. In that way, it'd be easier
to follow. Up to you.

> +
> +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)

These are not actually mask, so I'd remove _MASK. But it maybe just me.

> +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> +
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
> +
> +#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
> +
> +#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
> +#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
> +#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
> +#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
> +
> +#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
> +				XSDIRX_INTR_VIDUNLOCK_MASK |\
> +				XSDIRX_INTR_OVERFLOW_MASK |\
> +				XSDIRX_INTR_UNDERFLOW_MASK)
> +
> +#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
> +#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
> +#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
> +#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
> +#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
> +#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
> +#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
> +#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
> +
> +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
> +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
> +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
> +
> +#define XSDIRX_ACTIVE_STREAMS_1		0x0
> +#define XSDIRX_ACTIVE_STREAMS_2		0x1
> +#define XSDIRX_ACTIVE_STREAMS_4		0x2
> +#define XSDIRX_ACTIVE_STREAMS_8		0x3
> +#define XSDIRX_ACTIVE_STREAMS_16	0x4
> +
> +#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
> +#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
> +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
> +#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
> +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
> +#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
> +#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
> +
> +#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
> +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
> +#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
> +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
> +#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
> +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
> +#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
> +#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
> +#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
> +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
> +#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
> +
> +#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
> +#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
> +#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
> +#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
> +#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
> +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
> +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
> +
> +#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
> +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
> +#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
> +#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
> +#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
> +#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
> +
> +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
> +
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
> +
> +/* Number of media pads */
> +#define XSDIRX_MEDIA_PADS	(1)
> +
> +#define XSDIRX_DEFAULT_WIDTH	(1920)
> +#define XSDIRX_DEFAULT_HEIGHT	(1080)
> +
> +#define XSDIRX_MAX_STR_LENGTH	16
> +
> +#define XSDIRXSS_SDI_STD_3G		0
> +#define XSDIRXSS_SDI_STD_6G		1
> +#define XSDIRXSS_SDI_STD_12G_8DS	2
> +
> +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
> +
> +#define XSDIRX_MODE_HD_MASK	0x0
> +#define XSDIRX_MODE_SD_MASK	0x1
> +#define XSDIRX_MODE_3G_MASK	0x2
> +#define XSDIRX_MODE_6G_MASK	0x4
> +#define XSDIRX_MODE_12GI_MASK	0x5
> +#define XSDIRX_MODE_12GF_MASK	0x6
> +
> +/*
> + * Maximum number of events per file handle.
> + */

Nit, This doesn't have to be multieline comment.

> +#define XSDIRX_MAX_EVENTS	(128)
> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE_MASK	0xFF
> +#define XST352_PAYLOAD_BYTE1_SHIFT	0
> +#define XST352_PAYLOAD_BYTE2_SHIFT	8
> +#define XST352_PAYLOAD_BYTE3_SHIFT	16
> +#define XST352_PAYLOAD_BYTE4_SHIFT	24
> +
> +#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
> +#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
> +#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
> +#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
> +#define XST352_BYTE1_ST372_DL_3GB		0x8A
> +#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
> +#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
> +#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
> +#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
> +#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
> +
> +#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
> +#define XST352_BYTE2_TS_TYPE_OFFSET		15
> +#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
> +#define XST352_BYTE2_PIC_TYPE_OFFSET		14
> +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
> +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
> +
> +#define XST352_BYTE2_FPS_MASK			0xF
> +#define XST352_BYTE2_FPS_SHIFT			8
> +#define XST352_BYTE2_FPS_24F			0x2
> +#define XST352_BYTE2_FPS_24			0x3
> +#define XST352_BYTE2_FPS_48F			0x4
> +#define XST352_BYTE2_FPS_25			0x5
> +#define XST352_BYTE2_FPS_30F			0x6
> +#define XST352_BYTE2_FPS_30			0x7
> +#define XST352_BYTE2_FPS_48			0x8
> +#define XST352_BYTE2_FPS_50			0x9
> +#define XST352_BYTE2_FPS_60F			0xA
> +#define XST352_BYTE2_FPS_60			0xB
> +/* Table 4 ST 2081-10:2015 */
> +#define XST352_BYTE2_FPS_96			0xC
> +#define XST352_BYTE2_FPS_100			0xD
> +#define XST352_BYTE2_FPS_120			0xE
> +#define XST352_BYTE2_FPS_120F			0xF
> +
> +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
> +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
> +
> +#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
> +#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
> +#define XST352_BYTE3_COLOR_FORMAT_422		0x0
> +#define XST352_BYTE3_COLOR_FORMAT_420		0x3
> +
> +/**
> + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
> + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
> + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> + */
> +enum sdi_family_enc {
> +	XSDIRX_SMPTE_ST_274	= 0,
> +	XSDIRX_SMPTE_ST_296	= 1,
> +	XSDIRX_SMPTE_ST_2048_2	= 2,
> +	XSDIRX_SMPTE_ST_295	= 3,
> +	XSDIRX_NTSC		= 8,
> +	XSDIRX_PAL		= 9,
> +	XSDIRX_TS_UNKNOWN	= 15
> +};
> +
> +/**
> + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @include_edh: EDH processor presence
> + * @mode: 3G/6G/12G mode
> + * @axi_clk: Axi lite interface clock
> + * @sdirx_clk: SDI Rx GT clock
> + * @vidout_clk: Video clock
> + */
> +struct xsdirxss_core {
> +	struct device *dev;
> +	void __iomem *iomem;
> +	int irq;
> +	bool include_edh;

I wonder if this is required. I guess it's not critical anyway, so up to you.

> +	int mode;
> +	struct clk *axi_clk;
> +	struct clk *sdirx_clk;
> +	struct clk *vidout_clk;
> +};
> +
> +/**
> + * struct xsdirxss_state - SDI Rx Subsystem device structure
> + * @core: Core structure for MIPI SDI Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @event: Holds the video unlock event
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @vip_format: format information corresponding to the active format
> + * @pads: media pads
> + * @streaming: Flag for storing streaming state
> + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xsdirxss_state {
> +	struct xsdirxss_core core;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_event event;
> +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];

It's always single port, so this doesn't have to be an array.

> +	struct v4l2_mbus_framefmt default_format;
> +	struct v4l2_fract frame_interval;
> +	const struct xvip_video_format *vip_format;
> +	struct media_pad pads[XSDIRX_MEDIA_PADS];

Ditto.

> +	bool streaming;
> +	bool vidlocked;
> +	bool ts_is_interlaced;
> +};
> +
> +static inline struct xsdirxss_state *
> +to_xsdirxssstate(struct v4l2_subdev *subdev)
> +{
> +	return container_of(subdev, struct xsdirxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> +{
> +	return ioread32(xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> +				  u32 value)
> +{
> +	iowrite32(value, xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 clr)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> +}
> +
> +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 set)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> +}
> +
> +static void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static void xsdirx_core_enable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> +{
> +	u32 i, val;
> +
> +	mask &= XSDIRX_DETECT_ALL_MODES;
> +	if (!mask) {
> +		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> +
> +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);

() is not needed here. I'd avoid unneeded ().

> +
> +	if (hweight16(mask) > 1) {
> +		/* Multi mode detection as more than 1 bit set in mask */
> +		dev_dbg(core->dev, "Detect multiple modes\n");
> +		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> +			switch (mask & (1 << i)) {
> +			case BIT(XSDIRX_MODE_SD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_HD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_3G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_6G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GI_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GF_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +				break;
> +			}
> +		}
> +		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> +	} else {
> +		/* Fixed Mode */
> +		u32 forced_mode_mask = 0;

Setting this under default below would make it clearer. But up to you.

> +
> +		dev_dbg(core->dev, "Detect fixed mode\n");
> +
> +		/* Find offset of first bit set */
> +		switch (__ffs(mask)) {
> +		case XSDIRX_MODE_SD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_SD_MASK;
> +			break;
> +		case XSDIRX_MODE_HD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_HD_MASK;
> +			break;
> +		case XSDIRX_MODE_3G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_3G_MASK;
> +			break;
> +		case XSDIRX_MODE_6G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_6G_MASK;
> +			break;
> +		case XSDIRX_MODE_12GI_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> +			break;
> +		case XSDIRX_MODE_12GF_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> +			break;
> +		}
> +		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> +			forced_mode_mask);
> +		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> +	}
> +
> +	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> +		val);
> +	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> +
> +	return 0;
> +}
> +
> +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
> +{
> +	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> +	val = enable & XSDIRX_EDH_ALLERR_MASK;
> +
> +	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> +{
> +	/*
> +	 * The video lock window is the amount of time for which the
> +	 * the mode and transport stream should be locked to get the
> +	 * video lock interrupt.
> +	 */
> +	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> +}
> +
> +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)

The flag can be determined at build time, so this 'if' isn't needed. Then
the disabling call can be added to xsdirxss_remove().

> +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +}
> +
> +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}
> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);

Even these can be inlined. I don't see need for having dynamic check.

> +}
> +
> +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);

Ditto.

> +}
> +
> +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
> +{
> +	/* The sdi to native bridge is followed by native to axis4 bridge */
> +	if (enable) {
> +		xsdirx_axis4_bridge_control(core, enable);
> +		xsdirx_vid_bridge_control(core, enable);
> +	} else {
> +		xsdirx_vid_bridge_control(core, enable);
> +		xsdirx_axis4_bridge_control(core, enable);
> +	}
> +}
> +
> +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> +{
> +	xsdirx_streamflow_control(core, false);
> +}

These thin wrapers don't seem useful. Wouldn't it be better to inline most of
these?

> +
> +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> +				   u32 framerate)
> +{
> +	switch (framerate) {
> +	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 25000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_48HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 50000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 60000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 60000;
> +		break;
> +	default:
> +		frame_interval->numerator = 1;
> +		frame_interval->denominator = 1;
> +	}
> +}
> +
> +/**
> + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> + * @state: pointer to driver state
> + *
> + * This function decodes the stream's ST352 payload (if available) to get
> + * stream properties like width, height, picture type (interlaced/progressive),
> + * etc.
> + *
> + * Return: 0 for success else errors
> + */
> +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> +{
> +	struct xsdirxss_core *core = &state->core;
> +	u32 mode, payload = 0, val, family, valid, tscan;
> +	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> +	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> +	struct v4l2_mbus_framefmt *format = &state->formats[0];
> +
> +	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +
> +	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> +		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
> +		return -EINVAL;
> +	}
> +
> +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> +				XST352_PAYLOAD_BYTE_MASK;
> +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> +				XST352_BYTE2_PIC_TYPE_OFFSET;
> +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> +				XST352_BYTE2_FPS_MASK;
> +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> +				XST352_BYTE2_TS_TYPE_OFFSET;

Please align consistently throughout the patch. I believe the checkpatch
--strict warns on these.

> +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> +	} else {
> +		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> +			mode);
> +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> +	}
> +
> +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;

Just nit. Alignment.

> +	state->ts_is_interlaced = tscan ? false : true;
> +
> +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> +		state->ts_is_interlaced, family);
> +
> +	switch (mode) {
> +	case XSDIRX_MODE_HD_MASK:
> +		if (!valid) {
> +			/* No payload obtained */
> +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> +				framerate, tscan);
> +			/*
> +			 * NOTE : A progressive segmented frame pSF will be
> +			 * reported incorrectly as Interlaced as we rely on IP's
> +			 * transport scan locked bit.
> +			 */
> +			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
> +
> +			switch (framerate) {
> +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +				if (family == XSDIRX_SMPTE_ST_296) {
> +					format->width = 1280;
> +					format->height = 720;
> +					format->field = V4L2_FIELD_NONE;
> +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> +					format->width = 2048;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				} else {
> +					format->width = 1920;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				}
> +				break;
> +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +				if (family == XSDIRX_SMPTE_ST_274) {
> +					format->width = 1920;
> +					format->height = 1080;
> +				} else {
> +					format->width = 1280;
> +					format->height = 720;
> +				}
> +				format->field = V4L2_FIELD_NONE;
> +				break;
> +			default:
> +				format->width = 1920;
> +				format->height = 1080;
> +				format->field = V4L2_FIELD_NONE;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "Got the payload\n");
> +			switch (byte1) {
> +			case XST352_BYTE1_ST292_1x720L_1_5G:
> +				/* SMPTE ST 292-1 for 720 line payloads */
> +				format->width = 1280;
> +				format->height = 720;
> +				break;
> +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> +				/* SMPTE ST 292-1 for 1080 line payloads */
> +				format->height = 1080;
> +				if (active_luma)
> +					format->width = 2048;
> +				else
> +					format->width = 1920;
> +				break;
> +			default:
> +				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case XSDIRX_MODE_SD_MASK:
> +		format->field = V4L2_FIELD_ALTERNATE;
> +
> +		switch (family) {
> +		case XSDIRX_NTSC:
> +			format->width = 720;
> +			format->height = 486;
> +			break;
> +		case XSDIRX_PAL:
> +			format->width = 720;
> +			format->height = 576;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_3G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST425_2008_750L_3GB:
> +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x720L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->width = 1280;
> +			format->height = 720;
> +			break;
> +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> +			/* ST352 Table SMPTE 425-1 */
> +		case XST352_BYTE1_ST372_DL_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x1080L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->height = 1080;
> +			if (active_luma)
> +				format->width = 2048;
> +			else
> +				format->width = 1920;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_6G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> +			/* Dual link 6G */
> +		case XST352_BYTE1_ST2081_10_2160L_6G:
> +			/* Table 3 SMPTE ST 2081-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_12GI_MASK:
> +	case XSDIRX_MODE_12GF_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2082_10_2160L_12G:
> +			/* Section 4.3.1 SMPTE ST 2082-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		dev_err(core->dev, "Invalid Mode\n");
> +		return -EINVAL;
> +	}

Could you please take another look if this can be organized simpler? This is
a little too many switch - case and if statements.

> +
> +	if (valid) {
> +		if (pic_type)
> +			format->field = V4L2_FIELD_NONE;
> +		else
> +			format->field = V4L2_FIELD_ALTERNATE;
> +	}
> +
> +	if (format->field == V4L2_FIELD_ALTERNATE)
> +		format->height = format->height / 2;
> +
> +	switch (sampling) {
> +	case XST352_BYTE3_COLOR_FORMAT_422:
> +		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +		break;
> +	default:
> +		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> +		return -EINVAL;
> +	}
> +
> +	xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
> +		format->width, format->height, format->field, payload, val);
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		state->frame_interval.numerator,
> +		state->frame_interval.denominator);
> +	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> + * in the interrupt clear register. The interrupt status register is read only.

Thanks for the doc. I was about to ask. So I guess it's read - to -clear.

> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> +{
> +	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> +	struct xsdirxss_core *core = &state->core;
> +	u32 status;
> +
> +	status = xsdirxss_read(core, XSDIRX_ISR_REG);
> +	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> +		u32 val1, val2;
> +
> +		dev_dbg(core->dev, "video lock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> +
> +		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +
> +		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> +		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> +			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> +				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> +
> +			dev_dbg(core->dev, "mode & ts lock occurred\n");
> +
> +			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> +			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +
> +			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +
> +			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> +			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> +
> +			if (!xsdirx_get_stream_properties(state)) {
> +				memset(&state->event, 0, sizeof(state->event));
> +				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> +				state->event.u.src_change.changes =
> +					V4L2_EVENT_SRC_CH_RESOLUTION;
> +				v4l2_subdev_notify_event(&state->subdev,
> +							 &state->event);
> +
> +				state->vidlocked = true;
> +			} else {
> +				dev_err(core->dev, "Unable to get stream properties!\n");
> +				state->vidlocked = false;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "video unlock before video lock!\n");
> +			state->vidlocked = false;
> +		}
> +	}
> +
> +	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> +		dev_dbg(core->dev, "video unlock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> +		xsdirx_streamdowncb(core);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +
> +		state->vidlocked = false;
> +	}
> +
> +	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +
> +	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	int ret;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (sub->type) {
> +	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> +	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> +	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> +		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> +		break;
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		ret = v4l2_src_change_event_subscribe(fh, sub);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> +				      struct v4l2_fh *fh,
> +				      struct v4l2_event_subscription *sub)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> +	return v4l2_event_unsubscribe(fh, sub);
> +}
> +
> +/**
> + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> + * Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	int ret = 0;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,

This doesn't have to be a new line.

> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +
> +	if (xsdirxss->streaming) {
> +		dev_err(core->dev, "Cannot set controls while streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	xsdirx_core_disable(core);

Not sure why this is needed here. Can this be part of s_stream?

> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_FRAMER:
> +		xsdirx_framer(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> +		xsdirx_setvidlockwindow(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> +		xsdirx_setedherrcnttrigger(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> +		if (ctrl->val) {
> +			if (core->mode == XSDIRXSS_SDI_STD_3G) {
> +				dev_dbg(core->dev, "Upto 3G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			if (core->mode == XSDIRXSS_SDI_STD_6G) {
> +				dev_dbg(core->dev, "Upto 6G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			ret = xsdirx_set_modedetect(core, ctrl->val);
> +		} else {
> +			dev_err(core->dev, "Select at least one mode!\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SS_EN_MASK);
> +		return -EINVAL;
> +	}
> +	xsdirx_core_enable(core);

Ditto.

> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	u32 val;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +		switch (val) {
> +		case XSDIRX_MODE_SD_MASK:
> +			ctrl->val = XSDIRX_MODE_SD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_HD_MASK:
> +			ctrl->val = XSDIRX_MODE_HD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_3G_MASK:
> +			ctrl->val = XSDIRX_MODE_3G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_6G_MASK:
> +			ctrl->val = XSDIRX_MODE_6G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GI_MASK:
> +			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GF_MASK:
> +			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> +			break;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_CRC:
> +		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> +		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		ctrl->val = xsdirxss->ts_is_interlaced;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> +		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> +		ctrl->val = 1 << val;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_IS_3GB:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> +		ctrl->val = val ? true : false;
> +		break;
> +	default:
> +		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx SDI Rx Subsystem
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	u32 data, i;

'data' can be declared inside the for loop below.

> +
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> +	for (i = 0; i < 0x28; i++) {
> +		data = xsdirxss_read(core, i * 4);
> +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> +			  i * 4, data);
> +	}
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> +	return 0;
> +}
> +
> +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, true);
> +}
> +
> +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, false);
> +}

Ditto. Not sure if these single liner wrappers are useful.

> +
> +/**
> + * xsdirxss_g_frame_interval - Get the frame interval
> + * @sd: V4L2 Sub device
> + * @fi: Pointer to V4l2 Sub device frame interval structure
> + *
> + * This function is used to get the frame interval.
> + * The frame rate can be integral or fractional.
> + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
> + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fi->interval = xsdirxss->frame_interval;
> +
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		xsdirxss->frame_interval.numerator,
> +		xsdirxss->frame_interval.denominator);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 SDI Rx Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (enable) {
> +		if (!xsdirxss->vidlocked) {
> +			dev_dbg(core->dev, "Video is not locked\n");
> +			return -EINVAL;
> +		}
> +		if (xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Already streaming\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_start_stream(xsdirxss);
> +		xsdirxss->streaming = true;
> +		dev_dbg(core->dev, "Streaming started\n");
> +	} else {
> +		if (!xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Stopped streaming already\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_stop_stream(xsdirxss);
> +		xsdirxss->streaming = false;
> +		dev_dbg(core->dev, "Streaming stopped\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> +			  struct v4l2_subdev_pad_config *cfg,
> +				unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &xsdirxss->formats[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_format *fmt)

Please check the alignment.

> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> +						 fmt->pad, fmt->which);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> +		fmt->format.width, fmt->format.height, fmt->format.field);
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_format *fmt)

Please check the alignment.

> +{
> +	struct v4l2_mbus_framefmt *__format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	dev_dbg(xsdirxss->core.dev,
> +		"set width %d height %d code %d field %d colorspace %d\n",
> +		fmt->format.width, fmt->format.height,
> +		fmt->format.code, fmt->format.field,
> +		fmt->format.colorspace);
> +
> +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> +					     fmt->pad, fmt->which);
> +
> +	/* Currently reset the code to one fixed in hardware */
> +	/* TODO : Add checks for width height */
> +	fmt->format.code = __format->code;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 pad.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_open(struct v4l2_subdev *sd,
> +			 struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	*format = xsdirxss->default_format;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_close - Called on v4l2_close()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_close().
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_close(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_fh *fh)
> +{
> +	return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xsdirxss_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> +	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> +	.s_ctrl	= xsdirxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> +		.name	= "SDI Rx : EDH Error Count Enable",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_EDH_ALLERR_MASK,
> +		.def	= 0,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> +		.name	= "SDI Rx : EDH Error Count",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> +		.name	= "SDI Rx : EDH Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
> +		.name	= "SDI Rx : Enable Framer",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.step	= 1,
> +		.def	= true,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> +		.name	= "SDI Rx : Video Lock Window",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> +		.name	= "SDI Rx : Modes search Mask",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_DETECT_ALL_MODES,
> +		.def	= XSDIRX_DETECT_ALL_MODES,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> +		.name	= "SDI Rx : Mode Detect Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= XSDIRX_MODE_SD_OFFSET,
> +		.max	= XSDIRX_MODE_12GF_OFFSET,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_CRC,
> +		.name	= "SDI Rx : CRC Error status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> +		.name	= "SDI Rx : TS is Interlaced",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> +		.name	= "SDI Rx : Active Streams",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 1,
> +		.max	= 16,
> +		.def	= 1,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
> +		.name	= "SDI Rx : Is 3GB",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> +	.log_status = xsdirxss_log_status,
> +	.subscribe_event = xsdirxss_subscribe_event,
> +	.unsubscribe_event = xsdirxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> +	.g_frame_interval = xsdirxss_g_frame_interval,
> +	.s_stream = xsdirxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> +	.get_fmt = xsdirxss_get_format,
> +	.set_fmt = xsdirxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xsdirxss_ops = {
> +	.core = &xsdirxss_core_ops,
> +	.video = &xsdirxss_video_ops,
> +	.pad = &xsdirxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> +	.open = xsdirxss_open,
> +	.close = xsdirxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> +	struct device_node *node = xsdirxss->core.dev->of_node;
> +	struct device_node *ports = NULL;
> +	struct device_node *port = NULL;
> +	unsigned int nports = 0;
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	int ret;
> +	const char *sdi_std;
> +
> +	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> +	dev_dbg(core->dev, "EDH property = %s\n",
> +		core->include_edh ? "Present" : "Absent");
> +
> +	ret = of_property_read_string(node, "xlnx,line-rate",
> +				      &sdi_std);

Single line is enough.

> +	if (ret < 0) {
> +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> +		return ret;
> +	}
> +
> +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_6G;
> +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_3G;
> +	} else {
> +		dev_err(core->dev, "Invalid Line Rate\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> +		core->mode);
> +
> +	ports = of_get_child_by_name(node, "ports");
> +	if (!ports)
> +		ports = node;
> +
> +	for_each_child_of_node(ports, port) {
> +		const struct xvip_video_format *format;
> +		struct device_node *endpoint;
> +
> +		if (!port->name || of_node_cmp(port->name, "port"))
> +			continue;
> +
> +		format = xvip_of_get_format(port);
> +		if (IS_ERR(format)) {
> +			dev_err(core->dev, "invalid format in DT");
> +			return PTR_ERR(format);
> +		}
> +
> +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> +			format->vf_code, format->width, format->bpp);

The commit mentions this driver supports only 10bpc. Wouldn't it be better to
check it here?

> +
> +		if (format->vf_code != XVIP_VF_YUV_422 &&
> +		    format->vf_code != XVIP_VF_YUV_420) {
> +			dev_err(core->dev, "Incorrect UG934 video format set.\n");

You can make this single line as below,

	dev_err(core->dev,
		"Incorrect UG934 video format set.\n");

> +			return -EINVAL;
> +		}
> +		xsdirxss->vip_format = format;
> +
> +		endpoint = of_get_next_child(port, NULL);
> +		if (!endpoint) {
> +			dev_err(core->dev, "No port at\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Count the number of ports. */
> +		nports++;
> +	}
> +
> +	if (nports != 1) {
> +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> +		return -EINVAL;
> +	}
> +
> +	/* Register interrupt handler */
> +	core->irq = irq_of_parse_and_map(node, 0);
> +
> +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> +	if (ret) {
> +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> +	struct v4l2_subdev *subdev;
> +	struct xsdirxss_state *xsdirxss;
> +	struct xsdirxss_core *core;
> +	struct resource *res;
> +	int ret;
> +	unsigned int num_ctrls, num_edh_ctrls = 0, i;
> +
> +	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> +	if (!xsdirxss)
> +		return -ENOMEM;
> +
> +	xsdirxss->core.dev = &pdev->dev;
> +	core = &xsdirxss->core;
> +
> +	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> +	if (IS_ERR(core->axi_clk)) {
> +		ret = PTR_ERR(core->axi_clk);
> +		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> +	if (IS_ERR(core->sdirx_clk)) {
> +		ret = PTR_ERR(core->sdirx_clk);
> +		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> +	if (IS_ERR(core->vidout_clk)) {
> +		ret = PTR_ERR(core->vidout_clk);
> +		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->axi_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->sdirx_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> +		goto rx_clk_err;
> +	}
> +
> +	ret = clk_prepare_enable(core->vidout_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> +		goto vidout_clk_err;
> +	}
> +
> +	ret = xsdirxss_parse_of(xsdirxss);
> +	if (ret < 0)
> +		goto clk_err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> +	if (IS_ERR(xsdirxss->core.iomem)) {
> +		ret = PTR_ERR(xsdirxss->core.iomem);
> +		goto clk_err;
> +	}
> +
> +	/* Reset the core */
> +	xsdirx_streamflow_control(core, false);
> +	xsdirx_core_disable(core);
> +	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_globalintr(core, true);
> +	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Initialize the default format */
> +	xsdirxss->default_format.code = xsdirxss->vip_format->code;
> +	xsdirxss->default_format.field = V4L2_FIELD_NONE;
> +	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> +	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> +	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> +	xsdirxss->formats[0] = xsdirxss->default_format;
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	subdev = &xsdirxss->subdev;
> +	v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> +	subdev->dev = &pdev->dev;
> +	subdev->internal_ops = &xsdirxss_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 = &xsdirxss_media_ops;
> +
> +	v4l2_set_subdevdata(subdev, xsdirxss);
> +
> +	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialise and register the controls */
> +	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> +	if (xsdirxss->core.include_edh)
> +		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> +
> +	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> +			       (num_ctrls + num_edh_ctrls));
> +
> +	for (i = 0; i < num_ctrls; i++) {
> +		struct v4l2_ctrl *ctrl;
> +
> +		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> +
> +		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +					    &xsdirxss_ctrls[i], NULL);
> +		if (!ctrl) {
> +			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +				xsdirxss_ctrls[i].name);
> +			goto error;
> +		}
> +	}
> +
> +	if (xsdirxss->core.include_edh) {
> +		for (i = 0; i < num_edh_ctrls; i++) {
> +			struct v4l2_ctrl *ctrl;
> +
> +			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +				i, xsdirxss_edh_ctrls[i].name,
> +				xsdirxss_edh_ctrls[i].id);
> +
> +			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +						    &xsdirxss_edh_ctrls[i],
> +						    NULL);
> +			if (!ctrl) {
> +				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +					xsdirxss_edh_ctrls[i].name);
> +				goto error;
> +			}
> +		}
> +	} else {
> +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");

Is this worth being here? There's already an equivalent print for dt is parsed.

> +	}
> +
> +	if (xsdirxss->ctrl_handler.error) {
> +		dev_err(&pdev->dev, "failed to add controls\n");
> +		ret = xsdirxss->ctrl_handler.error;
> +		goto error;
> +	}
> +
> +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> +
> +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to set controls\n");
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, xsdirxss);
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register subdev\n");
> +		goto error;
> +	}
> +
> +	xsdirxss->streaming = false;
> +
> +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> +
> +	xsdirx_core_enable(core);
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +
> +clk_err:
> +	clk_disable_unprepare(core->vidout_clk);
> +vidout_clk_err:
> +	clk_disable_unprepare(core->sdirx_clk);
> +rx_clk_err:
> +	clk_disable_unprepare(core->axi_clk);
> +	return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> +
> +static struct platform_driver xsdirxss_driver = {
> +	.driver = {
> +		.name		= "xilinx-sdirxss",
> +		.of_match_table	= xsdirxss_of_id_table,
> +	},
> +	.probe			= xsdirxss_probe,
> +	.remove			= xsdirxss_remove,
> +};
> +
> +module_platform_driver(xsdirxss_driver);
> +
> +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
> new file mode 100644
> index 0000000..983a43b
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx SDI Rx Subsystem mode and flag definitions.
> + *
> + * Copyright (C) 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * This enum is used to prepare the bitmask of modes to be detected
> + */
> +enum {
> +	XSDIRX_MODE_SD_OFFSET = 0,
> +	XSDIRX_MODE_HD_OFFSET,
> +	XSDIRX_MODE_3G_OFFSET,
> +	XSDIRX_MODE_6G_OFFSET,
> +	XSDIRX_MODE_12GI_OFFSET,
> +	XSDIRX_MODE_12GF_OFFSET,
> +	XSDIRX_MODE_NUM_SUPPORTED,
> +};
> +
> +#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
> +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GF_OFFSET))
> +
> +/*
> + * EDH Error Types
> + * ANC - Ancillary Data Packet Errors
> + * FF - Full Field Errors
> + * AP - Active Portion Errors
> + */
> +
> +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> +
> +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index f023623..4c68a10 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -83,4 +83,34 @@
>  /* Reset all event counters */
>  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
>  
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
> +
> +/* Framer Control */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
> +/* Video Lock Window Control */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
> +/* Mode search Control */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
> +/* Get Detected Mode control */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
> +/* Get CRC error status */
> +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
> +/* Get EDH error count control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
> +/* Get EDH status control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
> +/* Get Transport Interlaced status */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
> +/* Get Active Streams count */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
> +/* Is Mode 3GB */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
> +
>  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
> index 2aa357c..7b47595 100644
> --- a/include/uapi/linux/xilinx-v4l2-events.h
> +++ b/include/uapi/linux/xilinx-v4l2-events.h
> @@ -18,4 +18,13 @@
>  /* Stream Line Buffer full */
>  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
>  
> +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
> +/* Video Unlock event */
> +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> +/* Video in to AXI4 Stream core underflowed */
> +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> +/* Video in to AXI4 Stream core overflowed */
> +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)

Could you please double-check if all events / controls are hard-required? More
of these will make generic v4l applications harder to run on this driver. It'd
be nicer if some of these (ex, from sdi spec) can be shared for any sdi if
possible?

Thanks,
-hyun

> +
>  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> -- 
> 1.8.3.1
> 
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-13 22:05     ` Hyun Kwon
  0 siblings, 0 replies; 41+ messages in thread
From: Hyun Kwon @ 2019-06-13 22:05 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Mark Rutland, devicetree, Dinesh Kumar, Hyun Kwon,
	Sandip Kothari, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Mauro Carvalho Chehab, linux-arm-kernel,
	linux-media

Hi Vishal,

Thanks for the patch.

On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> streams from SDI sources like SDI broadcast equipment like cameras and
> mixers. This block outputs either native SDI, native video or
> AXI4-Stream compliant data stream for further processing. Please refer
> to PG290 for details.
> 
> The driver is used to configure the IP to add framer, search for
> specific modes, get the detected mode, stream parameters, errors, etc.
> It also generates events for video lock/unlock, bridge over/under flow.
> 
> The driver supports only 10 bpc YUV 422 media bus format. It also
> decodes the stream parameters based on the ST352 packet embedded in the
> stream. In case the ST352 packet isn't present in the stream, the core's
> detected properties are used to set stream properties.
> 
> The driver currently supports only the AXI4-Stream configuration.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig          |   11 +
>  drivers/media/platform/xilinx/Makefile         |    1 +
>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>  6 files changed, 1960 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
>  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index cd1a0fd..0c68caa 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
>  	  Bridge. The driver is used to set the number of active lanes and
>  	  get short packet data.
>  
> +config VIDEO_XILINX_SDIRXSS
> +	tristate "Xilinx UHD SDI Rx Subsystem"
> +	help
> +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> +	  based driver that takes input from a SDI source like SDI camera and
> +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> +	  AXI4-Stream bridge. The driver is used to set different stream
> +	  detection modes and identify stream properties to properly configure
> +	  downstream.
> +
>  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 6119a34..223f2ea 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> new file mode 100644
> index 0000000..ba2d9d0
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,1846 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.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/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-sdirxss.h>
> +#include <linux/xilinx-v4l2-events.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"
> +
> +/*
> + * SDI Rx register map, bitmask and offsets
> + */
> +#define XSDIRX_RST_CTRL_REG		0x00
> +#define XSDIRX_MDL_CTRL_REG		0x04
> +#define XSDIRX_GLBL_IER_REG		0x0C
> +#define XSDIRX_ISR_REG			0x10
> +#define XSDIRX_IER_REG			0x14
> +#define XSDIRX_ST352_VALID_REG		0x18
> +#define XSDIRX_ST352_DS1_REG		0x1C
> +#define XSDIRX_ST352_DS3_REG		0x20
> +#define XSDIRX_ST352_DS5_REG		0x24
> +#define XSDIRX_ST352_DS7_REG		0x28
> +#define XSDIRX_ST352_DS9_REG		0x2C
> +#define XSDIRX_ST352_DS11_REG		0x30
> +#define XSDIRX_ST352_DS13_REG		0x34
> +#define XSDIRX_ST352_DS15_REG		0x38
> +#define XSDIRX_VERSION_REG		0x3C
> +#define XSDIRX_SS_CONFIG_REG		0x40
> +#define XSDIRX_MODE_DET_STAT_REG	0x44
> +#define XSDIRX_TS_DET_STAT_REG		0x48
> +#define XSDIRX_EDH_STAT_REG		0x4C
> +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> +#define XSDIRX_EDH_ERRCNT_REG		0x54
> +#define XSDIRX_CRC_ERRCNT_REG		0x58
> +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> +#define XSDIRX_SB_RX_STS_REG		0x60
> +
> +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)

I'd rather put these under corresponding offsets. In that way, it'd be easier
to follow. Up to you.

> +
> +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)

These are not actually mask, so I'd remove _MASK. But it maybe just me.

> +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> +
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
> +
> +#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
> +
> +#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
> +#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
> +#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
> +#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
> +
> +#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
> +				XSDIRX_INTR_VIDUNLOCK_MASK |\
> +				XSDIRX_INTR_OVERFLOW_MASK |\
> +				XSDIRX_INTR_UNDERFLOW_MASK)
> +
> +#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
> +#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
> +#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
> +#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
> +#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
> +#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
> +#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
> +#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
> +
> +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
> +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
> +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
> +
> +#define XSDIRX_ACTIVE_STREAMS_1		0x0
> +#define XSDIRX_ACTIVE_STREAMS_2		0x1
> +#define XSDIRX_ACTIVE_STREAMS_4		0x2
> +#define XSDIRX_ACTIVE_STREAMS_8		0x3
> +#define XSDIRX_ACTIVE_STREAMS_16	0x4
> +
> +#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
> +#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
> +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
> +#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
> +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
> +#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
> +#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
> +
> +#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
> +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
> +#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
> +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
> +#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
> +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
> +#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
> +#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
> +#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
> +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
> +#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
> +
> +#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
> +#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
> +#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
> +#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
> +#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
> +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
> +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
> +
> +#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
> +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
> +#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
> +#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
> +#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
> +#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
> +
> +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
> +
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
> +
> +/* Number of media pads */
> +#define XSDIRX_MEDIA_PADS	(1)
> +
> +#define XSDIRX_DEFAULT_WIDTH	(1920)
> +#define XSDIRX_DEFAULT_HEIGHT	(1080)
> +
> +#define XSDIRX_MAX_STR_LENGTH	16
> +
> +#define XSDIRXSS_SDI_STD_3G		0
> +#define XSDIRXSS_SDI_STD_6G		1
> +#define XSDIRXSS_SDI_STD_12G_8DS	2
> +
> +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
> +
> +#define XSDIRX_MODE_HD_MASK	0x0
> +#define XSDIRX_MODE_SD_MASK	0x1
> +#define XSDIRX_MODE_3G_MASK	0x2
> +#define XSDIRX_MODE_6G_MASK	0x4
> +#define XSDIRX_MODE_12GI_MASK	0x5
> +#define XSDIRX_MODE_12GF_MASK	0x6
> +
> +/*
> + * Maximum number of events per file handle.
> + */

Nit, This doesn't have to be multieline comment.

> +#define XSDIRX_MAX_EVENTS	(128)
> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE_MASK	0xFF
> +#define XST352_PAYLOAD_BYTE1_SHIFT	0
> +#define XST352_PAYLOAD_BYTE2_SHIFT	8
> +#define XST352_PAYLOAD_BYTE3_SHIFT	16
> +#define XST352_PAYLOAD_BYTE4_SHIFT	24
> +
> +#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
> +#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
> +#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
> +#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
> +#define XST352_BYTE1_ST372_DL_3GB		0x8A
> +#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
> +#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
> +#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
> +#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
> +#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
> +
> +#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
> +#define XST352_BYTE2_TS_TYPE_OFFSET		15
> +#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
> +#define XST352_BYTE2_PIC_TYPE_OFFSET		14
> +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
> +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
> +
> +#define XST352_BYTE2_FPS_MASK			0xF
> +#define XST352_BYTE2_FPS_SHIFT			8
> +#define XST352_BYTE2_FPS_24F			0x2
> +#define XST352_BYTE2_FPS_24			0x3
> +#define XST352_BYTE2_FPS_48F			0x4
> +#define XST352_BYTE2_FPS_25			0x5
> +#define XST352_BYTE2_FPS_30F			0x6
> +#define XST352_BYTE2_FPS_30			0x7
> +#define XST352_BYTE2_FPS_48			0x8
> +#define XST352_BYTE2_FPS_50			0x9
> +#define XST352_BYTE2_FPS_60F			0xA
> +#define XST352_BYTE2_FPS_60			0xB
> +/* Table 4 ST 2081-10:2015 */
> +#define XST352_BYTE2_FPS_96			0xC
> +#define XST352_BYTE2_FPS_100			0xD
> +#define XST352_BYTE2_FPS_120			0xE
> +#define XST352_BYTE2_FPS_120F			0xF
> +
> +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
> +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
> +
> +#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
> +#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
> +#define XST352_BYTE3_COLOR_FORMAT_422		0x0
> +#define XST352_BYTE3_COLOR_FORMAT_420		0x3
> +
> +/**
> + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
> + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
> + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> + */
> +enum sdi_family_enc {
> +	XSDIRX_SMPTE_ST_274	= 0,
> +	XSDIRX_SMPTE_ST_296	= 1,
> +	XSDIRX_SMPTE_ST_2048_2	= 2,
> +	XSDIRX_SMPTE_ST_295	= 3,
> +	XSDIRX_NTSC		= 8,
> +	XSDIRX_PAL		= 9,
> +	XSDIRX_TS_UNKNOWN	= 15
> +};
> +
> +/**
> + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @include_edh: EDH processor presence
> + * @mode: 3G/6G/12G mode
> + * @axi_clk: Axi lite interface clock
> + * @sdirx_clk: SDI Rx GT clock
> + * @vidout_clk: Video clock
> + */
> +struct xsdirxss_core {
> +	struct device *dev;
> +	void __iomem *iomem;
> +	int irq;
> +	bool include_edh;

I wonder if this is required. I guess it's not critical anyway, so up to you.

> +	int mode;
> +	struct clk *axi_clk;
> +	struct clk *sdirx_clk;
> +	struct clk *vidout_clk;
> +};
> +
> +/**
> + * struct xsdirxss_state - SDI Rx Subsystem device structure
> + * @core: Core structure for MIPI SDI Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @event: Holds the video unlock event
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @vip_format: format information corresponding to the active format
> + * @pads: media pads
> + * @streaming: Flag for storing streaming state
> + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xsdirxss_state {
> +	struct xsdirxss_core core;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_event event;
> +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];

It's always single port, so this doesn't have to be an array.

> +	struct v4l2_mbus_framefmt default_format;
> +	struct v4l2_fract frame_interval;
> +	const struct xvip_video_format *vip_format;
> +	struct media_pad pads[XSDIRX_MEDIA_PADS];

Ditto.

> +	bool streaming;
> +	bool vidlocked;
> +	bool ts_is_interlaced;
> +};
> +
> +static inline struct xsdirxss_state *
> +to_xsdirxssstate(struct v4l2_subdev *subdev)
> +{
> +	return container_of(subdev, struct xsdirxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> +{
> +	return ioread32(xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> +				  u32 value)
> +{
> +	iowrite32(value, xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 clr)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> +}
> +
> +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 set)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> +}
> +
> +static void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static void xsdirx_core_enable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> +{
> +	u32 i, val;
> +
> +	mask &= XSDIRX_DETECT_ALL_MODES;
> +	if (!mask) {
> +		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> +
> +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);

() is not needed here. I'd avoid unneeded ().

> +
> +	if (hweight16(mask) > 1) {
> +		/* Multi mode detection as more than 1 bit set in mask */
> +		dev_dbg(core->dev, "Detect multiple modes\n");
> +		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> +			switch (mask & (1 << i)) {
> +			case BIT(XSDIRX_MODE_SD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_HD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_3G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_6G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GI_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GF_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +				break;
> +			}
> +		}
> +		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> +	} else {
> +		/* Fixed Mode */
> +		u32 forced_mode_mask = 0;

Setting this under default below would make it clearer. But up to you.

> +
> +		dev_dbg(core->dev, "Detect fixed mode\n");
> +
> +		/* Find offset of first bit set */
> +		switch (__ffs(mask)) {
> +		case XSDIRX_MODE_SD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_SD_MASK;
> +			break;
> +		case XSDIRX_MODE_HD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_HD_MASK;
> +			break;
> +		case XSDIRX_MODE_3G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_3G_MASK;
> +			break;
> +		case XSDIRX_MODE_6G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_6G_MASK;
> +			break;
> +		case XSDIRX_MODE_12GI_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> +			break;
> +		case XSDIRX_MODE_12GF_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> +			break;
> +		}
> +		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> +			forced_mode_mask);
> +		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> +	}
> +
> +	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> +		val);
> +	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> +
> +	return 0;
> +}
> +
> +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
> +{
> +	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> +	val = enable & XSDIRX_EDH_ALLERR_MASK;
> +
> +	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> +{
> +	/*
> +	 * The video lock window is the amount of time for which the
> +	 * the mode and transport stream should be locked to get the
> +	 * video lock interrupt.
> +	 */
> +	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> +}
> +
> +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)

The flag can be determined at build time, so this 'if' isn't needed. Then
the disabling call can be added to xsdirxss_remove().

> +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +}
> +
> +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}
> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);

Even these can be inlined. I don't see need for having dynamic check.

> +}
> +
> +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);

Ditto.

> +}
> +
> +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
> +{
> +	/* The sdi to native bridge is followed by native to axis4 bridge */
> +	if (enable) {
> +		xsdirx_axis4_bridge_control(core, enable);
> +		xsdirx_vid_bridge_control(core, enable);
> +	} else {
> +		xsdirx_vid_bridge_control(core, enable);
> +		xsdirx_axis4_bridge_control(core, enable);
> +	}
> +}
> +
> +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> +{
> +	xsdirx_streamflow_control(core, false);
> +}

These thin wrapers don't seem useful. Wouldn't it be better to inline most of
these?

> +
> +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> +				   u32 framerate)
> +{
> +	switch (framerate) {
> +	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 25000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_48HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 50000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 60000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 60000;
> +		break;
> +	default:
> +		frame_interval->numerator = 1;
> +		frame_interval->denominator = 1;
> +	}
> +}
> +
> +/**
> + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> + * @state: pointer to driver state
> + *
> + * This function decodes the stream's ST352 payload (if available) to get
> + * stream properties like width, height, picture type (interlaced/progressive),
> + * etc.
> + *
> + * Return: 0 for success else errors
> + */
> +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> +{
> +	struct xsdirxss_core *core = &state->core;
> +	u32 mode, payload = 0, val, family, valid, tscan;
> +	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> +	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> +	struct v4l2_mbus_framefmt *format = &state->formats[0];
> +
> +	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +
> +	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> +		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
> +		return -EINVAL;
> +	}
> +
> +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> +				XST352_PAYLOAD_BYTE_MASK;
> +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> +				XST352_BYTE2_PIC_TYPE_OFFSET;
> +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> +				XST352_BYTE2_FPS_MASK;
> +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> +				XST352_BYTE2_TS_TYPE_OFFSET;

Please align consistently throughout the patch. I believe the checkpatch
--strict warns on these.

> +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> +	} else {
> +		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> +			mode);
> +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> +	}
> +
> +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;

Just nit. Alignment.

> +	state->ts_is_interlaced = tscan ? false : true;
> +
> +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> +		state->ts_is_interlaced, family);
> +
> +	switch (mode) {
> +	case XSDIRX_MODE_HD_MASK:
> +		if (!valid) {
> +			/* No payload obtained */
> +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> +				framerate, tscan);
> +			/*
> +			 * NOTE : A progressive segmented frame pSF will be
> +			 * reported incorrectly as Interlaced as we rely on IP's
> +			 * transport scan locked bit.
> +			 */
> +			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
> +
> +			switch (framerate) {
> +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +				if (family == XSDIRX_SMPTE_ST_296) {
> +					format->width = 1280;
> +					format->height = 720;
> +					format->field = V4L2_FIELD_NONE;
> +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> +					format->width = 2048;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				} else {
> +					format->width = 1920;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				}
> +				break;
> +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +				if (family == XSDIRX_SMPTE_ST_274) {
> +					format->width = 1920;
> +					format->height = 1080;
> +				} else {
> +					format->width = 1280;
> +					format->height = 720;
> +				}
> +				format->field = V4L2_FIELD_NONE;
> +				break;
> +			default:
> +				format->width = 1920;
> +				format->height = 1080;
> +				format->field = V4L2_FIELD_NONE;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "Got the payload\n");
> +			switch (byte1) {
> +			case XST352_BYTE1_ST292_1x720L_1_5G:
> +				/* SMPTE ST 292-1 for 720 line payloads */
> +				format->width = 1280;
> +				format->height = 720;
> +				break;
> +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> +				/* SMPTE ST 292-1 for 1080 line payloads */
> +				format->height = 1080;
> +				if (active_luma)
> +					format->width = 2048;
> +				else
> +					format->width = 1920;
> +				break;
> +			default:
> +				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case XSDIRX_MODE_SD_MASK:
> +		format->field = V4L2_FIELD_ALTERNATE;
> +
> +		switch (family) {
> +		case XSDIRX_NTSC:
> +			format->width = 720;
> +			format->height = 486;
> +			break;
> +		case XSDIRX_PAL:
> +			format->width = 720;
> +			format->height = 576;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_3G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST425_2008_750L_3GB:
> +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x720L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->width = 1280;
> +			format->height = 720;
> +			break;
> +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> +			/* ST352 Table SMPTE 425-1 */
> +		case XST352_BYTE1_ST372_DL_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x1080L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->height = 1080;
> +			if (active_luma)
> +				format->width = 2048;
> +			else
> +				format->width = 1920;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_6G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> +			/* Dual link 6G */
> +		case XST352_BYTE1_ST2081_10_2160L_6G:
> +			/* Table 3 SMPTE ST 2081-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_12GI_MASK:
> +	case XSDIRX_MODE_12GF_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2082_10_2160L_12G:
> +			/* Section 4.3.1 SMPTE ST 2082-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		dev_err(core->dev, "Invalid Mode\n");
> +		return -EINVAL;
> +	}

Could you please take another look if this can be organized simpler? This is
a little too many switch - case and if statements.

> +
> +	if (valid) {
> +		if (pic_type)
> +			format->field = V4L2_FIELD_NONE;
> +		else
> +			format->field = V4L2_FIELD_ALTERNATE;
> +	}
> +
> +	if (format->field == V4L2_FIELD_ALTERNATE)
> +		format->height = format->height / 2;
> +
> +	switch (sampling) {
> +	case XST352_BYTE3_COLOR_FORMAT_422:
> +		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +		break;
> +	default:
> +		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> +		return -EINVAL;
> +	}
> +
> +	xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
> +		format->width, format->height, format->field, payload, val);
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		state->frame_interval.numerator,
> +		state->frame_interval.denominator);
> +	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> + * in the interrupt clear register. The interrupt status register is read only.

Thanks for the doc. I was about to ask. So I guess it's read - to -clear.

> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> +{
> +	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> +	struct xsdirxss_core *core = &state->core;
> +	u32 status;
> +
> +	status = xsdirxss_read(core, XSDIRX_ISR_REG);
> +	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> +		u32 val1, val2;
> +
> +		dev_dbg(core->dev, "video lock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> +
> +		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +
> +		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> +		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> +			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> +				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> +
> +			dev_dbg(core->dev, "mode & ts lock occurred\n");
> +
> +			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> +			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +
> +			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +
> +			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> +			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> +
> +			if (!xsdirx_get_stream_properties(state)) {
> +				memset(&state->event, 0, sizeof(state->event));
> +				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> +				state->event.u.src_change.changes =
> +					V4L2_EVENT_SRC_CH_RESOLUTION;
> +				v4l2_subdev_notify_event(&state->subdev,
> +							 &state->event);
> +
> +				state->vidlocked = true;
> +			} else {
> +				dev_err(core->dev, "Unable to get stream properties!\n");
> +				state->vidlocked = false;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "video unlock before video lock!\n");
> +			state->vidlocked = false;
> +		}
> +	}
> +
> +	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> +		dev_dbg(core->dev, "video unlock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> +		xsdirx_streamdowncb(core);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +
> +		state->vidlocked = false;
> +	}
> +
> +	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +
> +	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	int ret;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (sub->type) {
> +	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> +	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> +	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> +		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> +		break;
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		ret = v4l2_src_change_event_subscribe(fh, sub);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> +				      struct v4l2_fh *fh,
> +				      struct v4l2_event_subscription *sub)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> +	return v4l2_event_unsubscribe(fh, sub);
> +}
> +
> +/**
> + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> + * Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	int ret = 0;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,

This doesn't have to be a new line.

> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +
> +	if (xsdirxss->streaming) {
> +		dev_err(core->dev, "Cannot set controls while streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	xsdirx_core_disable(core);

Not sure why this is needed here. Can this be part of s_stream?

> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_FRAMER:
> +		xsdirx_framer(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> +		xsdirx_setvidlockwindow(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> +		xsdirx_setedherrcnttrigger(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> +		if (ctrl->val) {
> +			if (core->mode == XSDIRXSS_SDI_STD_3G) {
> +				dev_dbg(core->dev, "Upto 3G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			if (core->mode == XSDIRXSS_SDI_STD_6G) {
> +				dev_dbg(core->dev, "Upto 6G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			ret = xsdirx_set_modedetect(core, ctrl->val);
> +		} else {
> +			dev_err(core->dev, "Select at least one mode!\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SS_EN_MASK);
> +		return -EINVAL;
> +	}
> +	xsdirx_core_enable(core);

Ditto.

> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	u32 val;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +		switch (val) {
> +		case XSDIRX_MODE_SD_MASK:
> +			ctrl->val = XSDIRX_MODE_SD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_HD_MASK:
> +			ctrl->val = XSDIRX_MODE_HD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_3G_MASK:
> +			ctrl->val = XSDIRX_MODE_3G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_6G_MASK:
> +			ctrl->val = XSDIRX_MODE_6G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GI_MASK:
> +			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GF_MASK:
> +			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> +			break;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_CRC:
> +		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> +		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		ctrl->val = xsdirxss->ts_is_interlaced;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> +		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> +		ctrl->val = 1 << val;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_IS_3GB:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> +		ctrl->val = val ? true : false;
> +		break;
> +	default:
> +		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx SDI Rx Subsystem
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	u32 data, i;

'data' can be declared inside the for loop below.

> +
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> +	for (i = 0; i < 0x28; i++) {
> +		data = xsdirxss_read(core, i * 4);
> +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> +			  i * 4, data);
> +	}
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> +	return 0;
> +}
> +
> +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, true);
> +}
> +
> +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, false);
> +}

Ditto. Not sure if these single liner wrappers are useful.

> +
> +/**
> + * xsdirxss_g_frame_interval - Get the frame interval
> + * @sd: V4L2 Sub device
> + * @fi: Pointer to V4l2 Sub device frame interval structure
> + *
> + * This function is used to get the frame interval.
> + * The frame rate can be integral or fractional.
> + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
> + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fi->interval = xsdirxss->frame_interval;
> +
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		xsdirxss->frame_interval.numerator,
> +		xsdirxss->frame_interval.denominator);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 SDI Rx Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (enable) {
> +		if (!xsdirxss->vidlocked) {
> +			dev_dbg(core->dev, "Video is not locked\n");
> +			return -EINVAL;
> +		}
> +		if (xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Already streaming\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_start_stream(xsdirxss);
> +		xsdirxss->streaming = true;
> +		dev_dbg(core->dev, "Streaming started\n");
> +	} else {
> +		if (!xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Stopped streaming already\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_stop_stream(xsdirxss);
> +		xsdirxss->streaming = false;
> +		dev_dbg(core->dev, "Streaming stopped\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> +			  struct v4l2_subdev_pad_config *cfg,
> +				unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &xsdirxss->formats[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_format *fmt)

Please check the alignment.

> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> +						 fmt->pad, fmt->which);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> +		fmt->format.width, fmt->format.height, fmt->format.field);
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_format *fmt)

Please check the alignment.

> +{
> +	struct v4l2_mbus_framefmt *__format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	dev_dbg(xsdirxss->core.dev,
> +		"set width %d height %d code %d field %d colorspace %d\n",
> +		fmt->format.width, fmt->format.height,
> +		fmt->format.code, fmt->format.field,
> +		fmt->format.colorspace);
> +
> +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> +					     fmt->pad, fmt->which);
> +
> +	/* Currently reset the code to one fixed in hardware */
> +	/* TODO : Add checks for width height */
> +	fmt->format.code = __format->code;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 pad.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_open(struct v4l2_subdev *sd,
> +			 struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	*format = xsdirxss->default_format;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_close - Called on v4l2_close()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_close().
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_close(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_fh *fh)
> +{
> +	return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xsdirxss_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> +	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> +	.s_ctrl	= xsdirxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> +		.name	= "SDI Rx : EDH Error Count Enable",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_EDH_ALLERR_MASK,
> +		.def	= 0,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> +		.name	= "SDI Rx : EDH Error Count",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> +		.name	= "SDI Rx : EDH Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
> +		.name	= "SDI Rx : Enable Framer",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.step	= 1,
> +		.def	= true,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> +		.name	= "SDI Rx : Video Lock Window",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> +		.name	= "SDI Rx : Modes search Mask",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_DETECT_ALL_MODES,
> +		.def	= XSDIRX_DETECT_ALL_MODES,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> +		.name	= "SDI Rx : Mode Detect Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= XSDIRX_MODE_SD_OFFSET,
> +		.max	= XSDIRX_MODE_12GF_OFFSET,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_CRC,
> +		.name	= "SDI Rx : CRC Error status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> +		.name	= "SDI Rx : TS is Interlaced",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> +		.name	= "SDI Rx : Active Streams",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 1,
> +		.max	= 16,
> +		.def	= 1,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
> +		.name	= "SDI Rx : Is 3GB",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> +	.log_status = xsdirxss_log_status,
> +	.subscribe_event = xsdirxss_subscribe_event,
> +	.unsubscribe_event = xsdirxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> +	.g_frame_interval = xsdirxss_g_frame_interval,
> +	.s_stream = xsdirxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> +	.get_fmt = xsdirxss_get_format,
> +	.set_fmt = xsdirxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xsdirxss_ops = {
> +	.core = &xsdirxss_core_ops,
> +	.video = &xsdirxss_video_ops,
> +	.pad = &xsdirxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> +	.open = xsdirxss_open,
> +	.close = xsdirxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> +	struct device_node *node = xsdirxss->core.dev->of_node;
> +	struct device_node *ports = NULL;
> +	struct device_node *port = NULL;
> +	unsigned int nports = 0;
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	int ret;
> +	const char *sdi_std;
> +
> +	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> +	dev_dbg(core->dev, "EDH property = %s\n",
> +		core->include_edh ? "Present" : "Absent");
> +
> +	ret = of_property_read_string(node, "xlnx,line-rate",
> +				      &sdi_std);

Single line is enough.

> +	if (ret < 0) {
> +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> +		return ret;
> +	}
> +
> +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_6G;
> +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_3G;
> +	} else {
> +		dev_err(core->dev, "Invalid Line Rate\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> +		core->mode);
> +
> +	ports = of_get_child_by_name(node, "ports");
> +	if (!ports)
> +		ports = node;
> +
> +	for_each_child_of_node(ports, port) {
> +		const struct xvip_video_format *format;
> +		struct device_node *endpoint;
> +
> +		if (!port->name || of_node_cmp(port->name, "port"))
> +			continue;
> +
> +		format = xvip_of_get_format(port);
> +		if (IS_ERR(format)) {
> +			dev_err(core->dev, "invalid format in DT");
> +			return PTR_ERR(format);
> +		}
> +
> +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> +			format->vf_code, format->width, format->bpp);

The commit mentions this driver supports only 10bpc. Wouldn't it be better to
check it here?

> +
> +		if (format->vf_code != XVIP_VF_YUV_422 &&
> +		    format->vf_code != XVIP_VF_YUV_420) {
> +			dev_err(core->dev, "Incorrect UG934 video format set.\n");

You can make this single line as below,

	dev_err(core->dev,
		"Incorrect UG934 video format set.\n");

> +			return -EINVAL;
> +		}
> +		xsdirxss->vip_format = format;
> +
> +		endpoint = of_get_next_child(port, NULL);
> +		if (!endpoint) {
> +			dev_err(core->dev, "No port at\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Count the number of ports. */
> +		nports++;
> +	}
> +
> +	if (nports != 1) {
> +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> +		return -EINVAL;
> +	}
> +
> +	/* Register interrupt handler */
> +	core->irq = irq_of_parse_and_map(node, 0);
> +
> +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> +	if (ret) {
> +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> +	struct v4l2_subdev *subdev;
> +	struct xsdirxss_state *xsdirxss;
> +	struct xsdirxss_core *core;
> +	struct resource *res;
> +	int ret;
> +	unsigned int num_ctrls, num_edh_ctrls = 0, i;
> +
> +	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> +	if (!xsdirxss)
> +		return -ENOMEM;
> +
> +	xsdirxss->core.dev = &pdev->dev;
> +	core = &xsdirxss->core;
> +
> +	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> +	if (IS_ERR(core->axi_clk)) {
> +		ret = PTR_ERR(core->axi_clk);
> +		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> +	if (IS_ERR(core->sdirx_clk)) {
> +		ret = PTR_ERR(core->sdirx_clk);
> +		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> +	if (IS_ERR(core->vidout_clk)) {
> +		ret = PTR_ERR(core->vidout_clk);
> +		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->axi_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->sdirx_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> +		goto rx_clk_err;
> +	}
> +
> +	ret = clk_prepare_enable(core->vidout_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> +		goto vidout_clk_err;
> +	}
> +
> +	ret = xsdirxss_parse_of(xsdirxss);
> +	if (ret < 0)
> +		goto clk_err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> +	if (IS_ERR(xsdirxss->core.iomem)) {
> +		ret = PTR_ERR(xsdirxss->core.iomem);
> +		goto clk_err;
> +	}
> +
> +	/* Reset the core */
> +	xsdirx_streamflow_control(core, false);
> +	xsdirx_core_disable(core);
> +	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_globalintr(core, true);
> +	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Initialize the default format */
> +	xsdirxss->default_format.code = xsdirxss->vip_format->code;
> +	xsdirxss->default_format.field = V4L2_FIELD_NONE;
> +	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> +	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> +	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> +	xsdirxss->formats[0] = xsdirxss->default_format;
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	subdev = &xsdirxss->subdev;
> +	v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> +	subdev->dev = &pdev->dev;
> +	subdev->internal_ops = &xsdirxss_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 = &xsdirxss_media_ops;
> +
> +	v4l2_set_subdevdata(subdev, xsdirxss);
> +
> +	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialise and register the controls */
> +	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> +	if (xsdirxss->core.include_edh)
> +		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> +
> +	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> +			       (num_ctrls + num_edh_ctrls));
> +
> +	for (i = 0; i < num_ctrls; i++) {
> +		struct v4l2_ctrl *ctrl;
> +
> +		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> +
> +		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +					    &xsdirxss_ctrls[i], NULL);
> +		if (!ctrl) {
> +			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +				xsdirxss_ctrls[i].name);
> +			goto error;
> +		}
> +	}
> +
> +	if (xsdirxss->core.include_edh) {
> +		for (i = 0; i < num_edh_ctrls; i++) {
> +			struct v4l2_ctrl *ctrl;
> +
> +			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +				i, xsdirxss_edh_ctrls[i].name,
> +				xsdirxss_edh_ctrls[i].id);
> +
> +			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +						    &xsdirxss_edh_ctrls[i],
> +						    NULL);
> +			if (!ctrl) {
> +				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +					xsdirxss_edh_ctrls[i].name);
> +				goto error;
> +			}
> +		}
> +	} else {
> +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");

Is this worth being here? There's already an equivalent print for dt is parsed.

> +	}
> +
> +	if (xsdirxss->ctrl_handler.error) {
> +		dev_err(&pdev->dev, "failed to add controls\n");
> +		ret = xsdirxss->ctrl_handler.error;
> +		goto error;
> +	}
> +
> +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> +
> +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to set controls\n");
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, xsdirxss);
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register subdev\n");
> +		goto error;
> +	}
> +
> +	xsdirxss->streaming = false;
> +
> +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> +
> +	xsdirx_core_enable(core);
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +
> +clk_err:
> +	clk_disable_unprepare(core->vidout_clk);
> +vidout_clk_err:
> +	clk_disable_unprepare(core->sdirx_clk);
> +rx_clk_err:
> +	clk_disable_unprepare(core->axi_clk);
> +	return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> +
> +static struct platform_driver xsdirxss_driver = {
> +	.driver = {
> +		.name		= "xilinx-sdirxss",
> +		.of_match_table	= xsdirxss_of_id_table,
> +	},
> +	.probe			= xsdirxss_probe,
> +	.remove			= xsdirxss_remove,
> +};
> +
> +module_platform_driver(xsdirxss_driver);
> +
> +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
> new file mode 100644
> index 0000000..983a43b
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx SDI Rx Subsystem mode and flag definitions.
> + *
> + * Copyright (C) 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * This enum is used to prepare the bitmask of modes to be detected
> + */
> +enum {
> +	XSDIRX_MODE_SD_OFFSET = 0,
> +	XSDIRX_MODE_HD_OFFSET,
> +	XSDIRX_MODE_3G_OFFSET,
> +	XSDIRX_MODE_6G_OFFSET,
> +	XSDIRX_MODE_12GI_OFFSET,
> +	XSDIRX_MODE_12GF_OFFSET,
> +	XSDIRX_MODE_NUM_SUPPORTED,
> +};
> +
> +#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
> +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GF_OFFSET))
> +
> +/*
> + * EDH Error Types
> + * ANC - Ancillary Data Packet Errors
> + * FF - Full Field Errors
> + * AP - Active Portion Errors
> + */
> +
> +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> +
> +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index f023623..4c68a10 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -83,4 +83,34 @@
>  /* Reset all event counters */
>  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
>  
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
> +
> +/* Framer Control */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
> +/* Video Lock Window Control */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
> +/* Mode search Control */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
> +/* Get Detected Mode control */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
> +/* Get CRC error status */
> +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
> +/* Get EDH error count control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
> +/* Get EDH status control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
> +/* Get Transport Interlaced status */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
> +/* Get Active Streams count */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
> +/* Is Mode 3GB */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
> +
>  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
> index 2aa357c..7b47595 100644
> --- a/include/uapi/linux/xilinx-v4l2-events.h
> +++ b/include/uapi/linux/xilinx-v4l2-events.h
> @@ -18,4 +18,13 @@
>  /* Stream Line Buffer full */
>  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
>  
> +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
> +/* Video Unlock event */
> +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> +/* Video in to AXI4 Stream core underflowed */
> +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> +/* Video in to AXI4 Stream core overflowed */
> +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)

Could you please double-check if all events / controls are hard-required? More
of these will make generic v4l applications harder to run on this driver. It'd
be nicer if some of these (ex, from sdi spec) can be shared for any sdi if
possible?

Thanks,
-hyun

> +
>  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> -- 
> 1.8.3.1
> 
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-13 22:05     ` Hyun Kwon
  (?)
@ 2019-06-13 22:31       ` Joe Perches
  -1 siblings, 0 replies; 41+ messages in thread
From: Joe Perches @ 2019-06-13 22:31 UTC (permalink / raw)
  To: Hyun Kwon, Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari

On Thu, 2019-06-13 at 15:05 -0700, Hyun Kwon wrote:
> On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:

trivia:

> > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
[]
> > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > +{
[]
> > +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > +				XST352_PAYLOAD_BYTE_MASK;

Is XST352_PAYLOAD_BYTE_MASK correct ?
Should it be XST352_PAYLOAD_BYTE1_MASK ?

> > +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > +				XST352_BYTE2_PIC_TYPE_OFFSET;
> > +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > +				XST352_BYTE2_FPS_MASK;
> > +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > +				XST352_BYTE2_TS_TYPE_OFFSET;
> 
> Please align consistently throughout the patch. I believe the checkpatch
> --strict warns on these.

I believe not.

Another possibility would be to use a macro like:

#define mask_and_shift(val, type)	\
	((val) & (XST352_ ## type ## _MASK)) >> (XST352_ ## type ## _OFFSET))

> > +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> > +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;

So these could be something like:

		sampling = mask_and_shift(payload, BYTE3_COLOR_FORMAT);



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-13 22:31       ` Joe Perches
  0 siblings, 0 replies; 41+ messages in thread
From: Joe Perches @ 2019-06-13 22:31 UTC (permalink / raw)
  To: Hyun Kwon, Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari

On Thu, 2019-06-13 at 15:05 -0700, Hyun Kwon wrote:
> On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:

trivia:

> > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
[]
> > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > +{
[]
> > +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > +				XST352_PAYLOAD_BYTE_MASK;

Is XST352_PAYLOAD_BYTE_MASK correct ?
Should it be XST352_PAYLOAD_BYTE1_MASK ?

> > +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > +				XST352_BYTE2_PIC_TYPE_OFFSET;
> > +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > +				XST352_BYTE2_FPS_MASK;
> > +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > +				XST352_BYTE2_TS_TYPE_OFFSET;
> 
> Please align consistently throughout the patch. I believe the checkpatch
> --strict warns on these.

I believe not.

Another possibility would be to use a macro like:

#define mask_and_shift(val, type)	\
	((val) & (XST352_ ## type ## _MASK)) >> (XST352_ ## type ## _OFFSET))

> > +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> > +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;

So these could be something like:

		sampling = mask_and_shift(payload, BYTE3_COLOR_FORMAT);

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-13 22:31       ` Joe Perches
  0 siblings, 0 replies; 41+ messages in thread
From: Joe Perches @ 2019-06-13 22:31 UTC (permalink / raw)
  To: Hyun Kwon, Vishal Sagar
  Cc: Mark Rutland, devicetree, Dinesh Kumar, Hyun Kwon,
	Sandip Kothari, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Mauro Carvalho Chehab, linux-arm-kernel,
	linux-media

On Thu, 2019-06-13 at 15:05 -0700, Hyun Kwon wrote:
> On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:

trivia:

> > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
[]
> > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > +{
[]
> > +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > +				XST352_PAYLOAD_BYTE_MASK;

Is XST352_PAYLOAD_BYTE_MASK correct ?
Should it be XST352_PAYLOAD_BYTE1_MASK ?

> > +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > +				XST352_BYTE2_PIC_TYPE_OFFSET;
> > +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > +				XST352_BYTE2_FPS_MASK;
> > +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > +				XST352_BYTE2_TS_TYPE_OFFSET;
> 
> Please align consistently throughout the patch. I believe the checkpatch
> --strict warns on these.

I believe not.

Another possibility would be to use a macro like:

#define mask_and_shift(val, type)	\
	((val) & (XST352_ ## type ## _MASK)) >> (XST352_ ## type ## _OFFSET))

> > +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> > +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;

So these could be something like:

		sampling = mask_and_shift(payload, BYTE3_COLOR_FORMAT);



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-05 12:57     ` Hans Verkuil
@ 2019-06-14 11:44       ` Vishal Sagar
  -1 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-14 11:44 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus

Hi Hans,

Thanks for reviewing this patch.

> -----Original Message-----
> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> Sent: Wednesday, June 05, 2019 6:28 PM
> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>;
> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> EXTERNAL EMAIL
> 
> On 6/4/19 3:55 PM, Vishal Sagar wrote:
> > The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> > streams from SDI sources like SDI broadcast equipment like cameras and
> > mixers. This block outputs either native SDI, native video or
> > AXI4-Stream compliant data stream for further processing. Please refer
> > to PG290 for details.
> >
> > The driver is used to configure the IP to add framer, search for
> > specific modes, get the detected mode, stream parameters, errors, etc.
> > It also generates events for video lock/unlock, bridge over/under flow.
> >
> > The driver supports only 10 bpc YUV 422 media bus format. It also
> > decodes the stream parameters based on the ST352 packet embedded in the
> > stream. In case the ST352 packet isn't present in the stream, the core's
> > detected properties are used to set stream properties.
> >
> > The driver currently supports only the AXI4-Stream configuration.
> >
> > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > ---
> >  drivers/media/platform/xilinx/Kconfig          |   11 +
> >  drivers/media/platform/xilinx/Makefile         |    1 +
> >  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> ++++++++++++++++++++++++
> >  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> 
> My comments for the MIPI CSI-2 Rx driver apply here as well with respect
> to the naming of controls/events and documenting them.
> 
> Also, the contents of xilinx-v4l2-*.h can just be moved to xilinx-sdirxss.h.
> 

Ok I will update the patch series based on this and resend v2.

> >  6 files changed, 1960 insertions(+)
> >  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> >  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index cd1a0fd..0c68caa 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
> >         Bridge. The driver is used to set the number of active lanes and
> >         get short packet data.
> >
> > +config VIDEO_XILINX_SDIRXSS
> > +     tristate "Xilinx UHD SDI Rx Subsystem"
> > +     help
> > +       Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> > +       based driver that takes input from a SDI source like SDI camera and
> > +       converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> > +       UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> > +       AXI4-Stream bridge. The driver is used to set different stream
> > +       detection modes and identify stream properties to properly configure
> > +       downstream.
> > +
> >  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 6119a34..223f2ea 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > new file mode 100644
> > index 0000000..ba2d9d0
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,1846 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#include <dt-bindings/media/xilinx-vip.h>
> > +#include <linux/bitops.h>
> > +#include <linux/compiler.h>
> > +#include <linux/clk.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/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-sdirxss.h>
> > +#include <linux/xilinx-v4l2-events.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"
> > +
> > +/*
> > + * SDI Rx register map, bitmask and offsets
> > + */
> > +#define XSDIRX_RST_CTRL_REG          0x00
> > +#define XSDIRX_MDL_CTRL_REG          0x04
> > +#define XSDIRX_GLBL_IER_REG          0x0C
> > +#define XSDIRX_ISR_REG                       0x10
> > +#define XSDIRX_IER_REG                       0x14
> > +#define XSDIRX_ST352_VALID_REG               0x18
> > +#define XSDIRX_ST352_DS1_REG         0x1C
> > +#define XSDIRX_ST352_DS3_REG         0x20
> > +#define XSDIRX_ST352_DS5_REG         0x24
> > +#define XSDIRX_ST352_DS7_REG         0x28
> > +#define XSDIRX_ST352_DS9_REG         0x2C
> > +#define XSDIRX_ST352_DS11_REG                0x30
> > +#define XSDIRX_ST352_DS13_REG                0x34
> > +#define XSDIRX_ST352_DS15_REG                0x38
> > +#define XSDIRX_VERSION_REG           0x3C
> > +#define XSDIRX_SS_CONFIG_REG         0x40
> > +#define XSDIRX_MODE_DET_STAT_REG     0x44
> > +#define XSDIRX_TS_DET_STAT_REG               0x48
> > +#define XSDIRX_EDH_STAT_REG          0x4C
> > +#define XSDIRX_EDH_ERRCNT_EN_REG     0x50
> > +#define XSDIRX_EDH_ERRCNT_REG                0x54
> > +#define XSDIRX_CRC_ERRCNT_REG                0x58
> > +#define XSDIRX_VID_LOCK_WINDOW_REG   0x5C
> > +#define XSDIRX_SB_RX_STS_REG         0x60
> > +
> > +#define XSDIRX_RST_CTRL_SS_EN_MASK                   BIT(0)
> > +#define XSDIRX_RST_CTRL_SRST_MASK                    BIT(1)
> > +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK          BIT(2)
> > +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK          BIT(3)
> > +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK                BIT(8)
> > +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK     BIT(9)
> > +
> > +#define XSDIRX_MDL_CTRL_FRM_EN_MASK          BIT(4)
> > +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK     BIT(5)
> > +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK              BIT(8)
> > +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK              BIT(9)
> > +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK              BIT(10)
> > +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK              BIT(11)
> > +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK    BIT(12)
> > +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK    BIT(13)
> > +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK   GENMASK(13, 8)
> > +
> > +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET   16
> > +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK     GENMASK(18, 16)
> > +
> > +#define XSDIRX_GLBL_INTR_EN_MASK     BIT(0)
> > +
> > +#define XSDIRX_INTR_VIDLOCK_MASK     BIT(0)
> > +#define XSDIRX_INTR_VIDUNLOCK_MASK   BIT(1)
> > +#define XSDIRX_INTR_OVERFLOW_MASK    BIT(9)
> > +#define XSDIRX_INTR_UNDERFLOW_MASK   BIT(10)
> > +
> > +#define XSDIRX_INTR_ALL_MASK (XSDIRX_INTR_VIDLOCK_MASK |\
> > +                             XSDIRX_INTR_VIDUNLOCK_MASK |\
> > +                             XSDIRX_INTR_OVERFLOW_MASK |\
> > +                             XSDIRX_INTR_UNDERFLOW_MASK)
> > +
> > +#define XSDIRX_ST352_VALID_DS1_MASK  BIT(0)
> > +#define XSDIRX_ST352_VALID_DS3_MASK  BIT(1)
> > +#define XSDIRX_ST352_VALID_DS5_MASK  BIT(2)
> > +#define XSDIRX_ST352_VALID_DS7_MASK  BIT(3)
> > +#define XSDIRX_ST352_VALID_DS9_MASK  BIT(4)
> > +#define XSDIRX_ST352_VALID_DS11_MASK BIT(5)
> > +#define XSDIRX_ST352_VALID_DS13_MASK BIT(6)
> > +#define XSDIRX_ST352_VALID_DS15_MASK BIT(7)
> > +
> > +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK    GENMASK(2, 0)
> > +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK  BIT(3)
> > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK GENMASK(6, 4)
> > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET       4
> > +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK    BIT(7)
> > +
> > +#define XSDIRX_ACTIVE_STREAMS_1              0x0
> > +#define XSDIRX_ACTIVE_STREAMS_2              0x1
> > +#define XSDIRX_ACTIVE_STREAMS_4              0x2
> > +#define XSDIRX_ACTIVE_STREAMS_8              0x3
> > +#define XSDIRX_ACTIVE_STREAMS_16     0x4
> > +
> > +#define XSDIRX_TS_DET_STAT_LOCKED_MASK               BIT(0)
> > +#define XSDIRX_TS_DET_STAT_SCAN_MASK         BIT(1)
> > +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET               (1)
> > +#define XSDIRX_TS_DET_STAT_FAMILY_MASK               GENMASK(7, 4)
> > +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET     (4)
> > +#define XSDIRX_TS_DET_STAT_RATE_MASK         GENMASK(11, 8)
> > +#define XSDIRX_TS_DET_STAT_RATE_OFFSET               (8)
> > +
> > +#define XSDIRX_TS_DET_STAT_RATE_NONE         0x0
> > +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ              0x2
> > +#define XSDIRX_TS_DET_STAT_RATE_24HZ         0x3
> > +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ              0x4
> > +#define XSDIRX_TS_DET_STAT_RATE_25HZ         0x5
> > +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ              0x6
> > +#define XSDIRX_TS_DET_STAT_RATE_30HZ         0x7
> > +#define XSDIRX_TS_DET_STAT_RATE_48HZ         0x8
> > +#define XSDIRX_TS_DET_STAT_RATE_50HZ         0x9
> > +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ              0xA
> > +#define XSDIRX_TS_DET_STAT_RATE_60HZ         0xB
> > +
> > +#define XSDIRX_EDH_STAT_EDH_AP_MASK  BIT(0)
> > +#define XSDIRX_EDH_STAT_EDH_FF_MASK  BIT(1)
> > +#define XSDIRX_EDH_STAT_EDH_ANC_MASK BIT(2)
> > +#define XSDIRX_EDH_STAT_AP_FLAG_MASK GENMASK(8, 4)
> > +#define XSDIRX_EDH_STAT_FF_FLAG_MASK GENMASK(13, 9)
> > +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK        GENMASK(18, 14)
> > +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK        GENMASK(22, 19)
> > +
> > +#define XSDIRX_EDH_ERRCNT_COUNT_MASK GENMASK(15, 0)
> > +
> > +#define XSDIRX_CRC_ERRCNT_COUNT_MASK GENMASK(31, 16)
> > +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK        GENMASK(15, 0)
> > +
> > +#define XSDIRX_VERSION_REV_MASK              GENMASK(7, 0)
> > +#define XSDIRX_VERSION_PATCHID_MASK  GENMASK(11, 8)
> > +#define XSDIRX_VERSION_VER_REV_MASK  GENMASK(15, 12)
> > +#define XSDIRX_VERSION_VER_MIN_MASK  GENMASK(23, 16)
> > +#define XSDIRX_VERSION_VER_MAJ_MASK  GENMASK(31, 24)
> > +
> > +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK           BIT(1)
> > +
> > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK     BIT(0)
> > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK     BIT(1)
> > +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK    BIT(2)
> > +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK              BIT(3)
> > +
> > +/* Number of media pads */
> > +#define XSDIRX_MEDIA_PADS    (1)
> > +
> > +#define XSDIRX_DEFAULT_WIDTH (1920)
> > +#define XSDIRX_DEFAULT_HEIGHT        (1080)
> > +
> > +#define XSDIRX_MAX_STR_LENGTH        16
> > +
> > +#define XSDIRXSS_SDI_STD_3G          0
> > +#define XSDIRXSS_SDI_STD_6G          1
> > +#define XSDIRXSS_SDI_STD_12G_8DS     2
> > +
> > +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW     0x3000
> > +
> > +#define XSDIRX_MODE_HD_MASK  0x0
> > +#define XSDIRX_MODE_SD_MASK  0x1
> > +#define XSDIRX_MODE_3G_MASK  0x2
> > +#define XSDIRX_MODE_6G_MASK  0x4
> > +#define XSDIRX_MODE_12GI_MASK        0x5
> > +#define XSDIRX_MODE_12GF_MASK        0x6
> > +
> > +/*
> > + * Maximum number of events per file handle.
> > + */
> > +#define XSDIRX_MAX_EVENTS    (128)
> > +
> > +/* ST352 related macros */
> > +#define XST352_PAYLOAD_BYTE_MASK     0xFF
> > +#define XST352_PAYLOAD_BYTE1_SHIFT   0
> > +#define XST352_PAYLOAD_BYTE2_SHIFT   8
> > +#define XST352_PAYLOAD_BYTE3_SHIFT   16
> > +#define XST352_PAYLOAD_BYTE4_SHIFT   24
> > +
> > +#define XST352_BYTE1_ST292_1x720L_1_5G               0x84
> > +#define XST352_BYTE1_ST292_1x1080L_1_5G              0x85
> > +#define XST352_BYTE1_ST425_2008_750L_3GB     0x88
> > +#define XST352_BYTE1_ST425_2008_1125L_3GA    0x89
> > +#define XST352_BYTE1_ST372_DL_3GB            0x8A
> > +#define XST352_BYTE1_ST372_2x720L_3GB                0x8B
> > +#define XST352_BYTE1_ST372_2x1080L_3GB               0x8C
> > +#define XST352_BYTE1_ST2081_10_2160L_6G              0xC0
> > +#define XST352_BYTE1_ST2081_10_DL_2160L_6G   0xC2
> > +#define XST352_BYTE1_ST2082_10_2160L_12G     0xCE
> > +
> > +#define XST352_BYTE2_TS_TYPE_MASK            BIT(15)
> > +#define XST352_BYTE2_TS_TYPE_OFFSET          15
> > +#define XST352_BYTE2_PIC_TYPE_MASK           BIT(14)
> > +#define XST352_BYTE2_PIC_TYPE_OFFSET         14
> > +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED  0
> > +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE 1
> > +
> > +#define XST352_BYTE2_FPS_MASK                        0xF
> > +#define XST352_BYTE2_FPS_SHIFT                       8
> > +#define XST352_BYTE2_FPS_24F                 0x2
> > +#define XST352_BYTE2_FPS_24                  0x3
> > +#define XST352_BYTE2_FPS_48F                 0x4
> > +#define XST352_BYTE2_FPS_25                  0x5
> > +#define XST352_BYTE2_FPS_30F                 0x6
> > +#define XST352_BYTE2_FPS_30                  0x7
> > +#define XST352_BYTE2_FPS_48                  0x8
> > +#define XST352_BYTE2_FPS_50                  0x9
> > +#define XST352_BYTE2_FPS_60F                 0xA
> > +#define XST352_BYTE2_FPS_60                  0xB
> > +/* Table 4 ST 2081-10:2015 */
> > +#define XST352_BYTE2_FPS_96                  0xC
> > +#define XST352_BYTE2_FPS_100                 0xD
> > +#define XST352_BYTE2_FPS_120                 0xE
> > +#define XST352_BYTE2_FPS_120F                        0xF
> > +
> > +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK     BIT(22)
> > +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET   22
> > +
> > +#define XST352_BYTE3_COLOR_FORMAT_MASK               GENMASK(19, 16)
> > +#define XST352_BYTE3_COLOR_FORMAT_OFFSET     16
> > +#define XST352_BYTE3_COLOR_FORMAT_422                0x0
> > +#define XST352_BYTE3_COLOR_FORMAT_420                0x3
> > +
> > +/**
> > + * enum sdi_family_enc - SDI Transport Video Format Detected with Active
> Pixels
> > + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> > + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> > + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP
> 2048x1080
> > + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> > + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> > + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> > + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> > + */
> > +enum sdi_family_enc {
> > +     XSDIRX_SMPTE_ST_274     = 0,
> > +     XSDIRX_SMPTE_ST_296     = 1,
> > +     XSDIRX_SMPTE_ST_2048_2  = 2,
> > +     XSDIRX_SMPTE_ST_295     = 3,
> > +     XSDIRX_NTSC             = 8,
> > +     XSDIRX_PAL              = 9,
> > +     XSDIRX_TS_UNKNOWN       = 15
> > +};
> > +
> > +/**
> > + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device
> structure
> > + * @dev: Platform structure
> > + * @iomem: Base address of subsystem
> > + * @irq: requested irq number
> > + * @include_edh: EDH processor presence
> > + * @mode: 3G/6G/12G mode
> > + * @axi_clk: Axi lite interface clock
> > + * @sdirx_clk: SDI Rx GT clock
> > + * @vidout_clk: Video clock
> > + */
> > +struct xsdirxss_core {
> > +     struct device *dev;
> > +     void __iomem *iomem;
> > +     int irq;
> > +     bool include_edh;
> > +     int mode;
> > +     struct clk *axi_clk;
> > +     struct clk *sdirx_clk;
> > +     struct clk *vidout_clk;
> > +};
> > +
> > +/**
> > + * struct xsdirxss_state - SDI Rx Subsystem device structure
> > + * @core: Core structure for MIPI SDI Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @event: Holds the video unlock event
> > + * @formats: Active V4L2 formats on each pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @vip_format: format information corresponding to the active format
> > + * @pads: media pads
> > + * @streaming: Flag for storing streaming state
> > + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> > + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xsdirxss_state {
> > +     struct xsdirxss_core core;
> > +     struct v4l2_subdev subdev;
> > +     struct v4l2_ctrl_handler ctrl_handler;
> > +     struct v4l2_event event;
> > +     struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> > +     struct v4l2_mbus_framefmt default_format;
> > +     struct v4l2_fract frame_interval;
> > +     const struct xvip_video_format *vip_format;
> > +     struct media_pad pads[XSDIRX_MEDIA_PADS];
> > +     bool streaming;
> > +     bool vidlocked;
> > +     bool ts_is_interlaced;
> > +};
> > +
> > +static inline struct xsdirxss_state *
> > +to_xsdirxssstate(struct v4l2_subdev *subdev)
> > +{
> > +     return container_of(subdev, struct xsdirxss_state, subdev);
> > +}
> > +
> > +/*
> > + * Register related operations
> > + */
> > +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> > +{
> > +     return ioread32(xsdirxss->iomem + addr);
> > +}
> > +
> > +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> > +                               u32 value)
> > +{
> > +     iowrite32(value, xsdirxss->iomem + addr);
> > +}
> > +
> > +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> > +                             u32 clr)
> > +{
> > +     xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> > +}
> > +
> > +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> > +                             u32 set)
> > +{
> > +     xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> > +}
> > +
> > +static void xsdirx_core_disable(struct xsdirxss_core *core)
> > +{
> > +     xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static void xsdirx_core_enable(struct xsdirxss_core *core)
> > +{
> > +     xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> > +{
> > +     u32 i, val;
> > +
> > +     mask &= XSDIRX_DETECT_ALL_MODES;
> > +     if (!mask) {
> > +             dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> > +             return -EINVAL;
> > +     }
> > +
> > +     dev_dbg(core->dev, "mask = 0x%x\n", mask);
> > +
> > +     val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> > +     val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> > +     val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> > +     val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> > +
> > +     if (hweight16(mask) > 1) {
> > +             /* Multi mode detection as more than 1 bit set in mask */
> > +             dev_dbg(core->dev, "Detect multiple modes\n");
> > +             for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> > +                     switch (mask & (1 << i)) {
> > +                     case BIT(XSDIRX_MODE_SD_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_HD_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_3G_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_6G_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_12GI_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_12GF_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > +                             break;
> > +                     }
> > +             }
> > +             val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > +     } else {
> > +             /* Fixed Mode */
> > +             u32 forced_mode_mask = 0;
> > +
> > +             dev_dbg(core->dev, "Detect fixed mode\n");
> > +
> > +             /* Find offset of first bit set */
> > +             switch (__ffs(mask)) {
> > +             case XSDIRX_MODE_SD_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_SD_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_HD_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_HD_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_3G_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_3G_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_6G_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_6G_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_12GI_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_12GF_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> > +                     break;
> > +             }
> > +             dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> > +                     forced_mode_mask);
> > +             val |= forced_mode_mask <<
> XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > +     }
> > +
> > +     dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> > +             val);
> > +     xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> > +{
> > +     if (flag)
> > +             xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> > +                          XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > +     else
> > +             xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> > +                          XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > +}
> > +
> > +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32
> enable)
> > +{
> > +     u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> > +
> > +     val = enable & XSDIRX_EDH_ALLERR_MASK;
> > +
> > +     xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> > +}
> > +
> > +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> > +{
> > +     /*
> > +      * The video lock window is the amount of time for which the
> > +      * the mode and transport stream should be locked to get the
> > +      * video lock interrupt.
> > +      */
> > +     xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> > +}
> > +
> > +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +     xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +     xsdirxss_set(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> > +{
> > +     if (flag)
> > +             xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> > +                          XSDIRX_GLBL_INTR_EN_MASK);
> > +     else
> > +             xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> > +                          XSDIRX_GLBL_INTR_EN_MASK);
> > +}
> > +
> > +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +     xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > +}
> > +
> > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +     if (enable)
> > +             xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> > +     else
> > +             xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> > +}
> > +
> > +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +     if (enable)
> > +             xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +     else
> > +             xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +}
> > +
> > +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +     /* The sdi to native bridge is followed by native to axis4 bridge */
> > +     if (enable) {
> > +             xsdirx_axis4_bridge_control(core, enable);
> > +             xsdirx_vid_bridge_control(core, enable);
> > +     } else {
> > +             xsdirx_vid_bridge_control(core, enable);
> > +             xsdirx_axis4_bridge_control(core, enable);
> > +     }
> > +}
> > +
> > +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> > +{
> > +     xsdirx_streamflow_control(core, false);
> > +}
> > +
> > +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> > +                                u32 framerate)
> > +{
> > +     switch (framerate) {
> > +     case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > +             frame_interval->numerator = 1001;
> > +             frame_interval->denominator = 24000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 24000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 25000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > +             frame_interval->numerator = 1001;
> > +             frame_interval->denominator = 30000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 30000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> > +             frame_interval->numerator = 1001;
> > +             frame_interval->denominator = 48000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_48HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 48000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 50000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > +             frame_interval->numerator = 1001;
> > +             frame_interval->denominator = 60000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 60000;
> > +             break;
> > +     default:
> > +             frame_interval->numerator = 1;
> > +             frame_interval->denominator = 1;
> > +     }
> > +}
> > +
> > +/**
> > + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> > + * @state: pointer to driver state
> > + *
> > + * This function decodes the stream's ST352 payload (if available) to get
> > + * stream properties like width, height, picture type (interlaced/progressive),
> > + * etc.
> > + *
> > + * Return: 0 for success else errors
> > + */
> > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > +{
> > +     struct xsdirxss_core *core = &state->core;
> > +     u32 mode, payload = 0, val, family, valid, tscan;
> > +     u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> > +     u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> > +     struct v4l2_mbus_framefmt *format = &state->formats[0];
> > +
> > +     mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +     mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > +     valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > +
> > +     if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> > +             dev_err(core->dev, "No valid ST352 payload present even for 3G
> mode and above\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +     if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > +             payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +             byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > +                             XST352_PAYLOAD_BYTE_MASK;
> > +             active_luma = (payload &
> XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > +                             XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > +             pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > +                             XST352_BYTE2_PIC_TYPE_OFFSET;
> > +             framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > +                             XST352_BYTE2_FPS_MASK;
> > +             tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > +                             XST352_BYTE2_TS_TYPE_OFFSET;
> > +             sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> > +                        XST352_BYTE3_COLOR_FORMAT_OFFSET;
> > +     } else {
> > +             dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> > +                     mode);
> > +             framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> > +                             XSDIRX_TS_DET_STAT_RATE_OFFSET;
> > +             tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> > +                             XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> > +     }
> > +
> > +     family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> > +               XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> > +     state->ts_is_interlaced = tscan ? false : true;
> > +
> > +     dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> > +             state->ts_is_interlaced, family);
> > +
> > +     switch (mode) {
> > +     case XSDIRX_MODE_HD_MASK:
> > +             if (!valid) {
> > +                     /* No payload obtained */
> > +                     dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> > +                             framerate, tscan);
> > +                     /*
> > +                      * NOTE : A progressive segmented frame pSF will be
> > +                      * reported incorrectly as Interlaced as we rely on IP's
> > +                      * transport scan locked bit.
> > +                      */
> > +                     dev_warn(core->dev, "pSF will be incorrectly reported as
> Interlaced\n");
> > +
> > +                     switch (framerate) {
> > +                     case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > +                             if (family == XSDIRX_SMPTE_ST_296) {
> > +                                     format->width = 1280;
> > +                                     format->height = 720;
> > +                                     format->field = V4L2_FIELD_NONE;
> > +                             } else if (family == XSDIRX_SMPTE_ST_2048_2) {
> > +                                     format->width = 2048;
> > +                                     format->height = 1080;
> > +                                     if (tscan)
> > +                                             format->field = V4L2_FIELD_NONE;
> > +                                     else
> > +                                             format->field =
> > +                                                     V4L2_FIELD_ALTERNATE;
> > +                             } else {
> > +                                     format->width = 1920;
> > +                                     format->height = 1080;
> > +                                     if (tscan)
> > +                                             format->field = V4L2_FIELD_NONE;
> > +                                     else
> > +                                             format->field =
> > +                                                     V4L2_FIELD_ALTERNATE;
> > +                             }
> > +                             break;
> > +                     case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > +                             if (family == XSDIRX_SMPTE_ST_274) {
> > +                                     format->width = 1920;
> > +                                     format->height = 1080;
> > +                             } else {
> > +                                     format->width = 1280;
> > +                                     format->height = 720;
> > +                             }
> > +                             format->field = V4L2_FIELD_NONE;
> > +                             break;
> > +                     default:
> > +                             format->width = 1920;
> > +                             format->height = 1080;
> > +                             format->field = V4L2_FIELD_NONE;
> > +                     }
> > +             } else {
> > +                     dev_dbg(core->dev, "Got the payload\n");
> > +                     switch (byte1) {
> > +                     case XST352_BYTE1_ST292_1x720L_1_5G:
> > +                             /* SMPTE ST 292-1 for 720 line payloads */
> > +                             format->width = 1280;
> > +                             format->height = 720;
> > +                             break;
> > +                     case XST352_BYTE1_ST292_1x1080L_1_5G:
> > +                             /* SMPTE ST 292-1 for 1080 line payloads */
> > +                             format->height = 1080;
> > +                             if (active_luma)
> > +                                     format->width = 2048;
> > +                             else
> > +                                     format->width = 1920;
> > +                             break;
> > +                     default:
> > +                             dev_dbg(core->dev, "Unknown HD Mode SMPTE
> standard\n");
> > +                             return -EINVAL;
> > +                     }
> > +             }
> > +             break;
> > +     case XSDIRX_MODE_SD_MASK:
> > +             format->field = V4L2_FIELD_ALTERNATE;
> > +
> > +             switch (family) {
> > +             case XSDIRX_NTSC:
> > +                     format->width = 720;
> > +                     format->height = 486;
> > +                     break;
> > +             case XSDIRX_PAL:
> > +                     format->width = 720;
> > +                     format->height = 576;
> > +                     break;
> > +             default:
> > +                     dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case XSDIRX_MODE_3G_MASK:
> > +             switch (byte1) {
> > +             case XST352_BYTE1_ST425_2008_750L_3GB:
> > +                     /* Sec 4.1.6.1 SMPTE 425-2008 */
> > +             case XST352_BYTE1_ST372_2x720L_3GB:
> > +                     /* Table 13 SMPTE 425-2008 */
> > +                     format->width = 1280;
> > +                     format->height = 720;
> > +                     break;
> > +             case XST352_BYTE1_ST425_2008_1125L_3GA:
> > +                     /* ST352 Table SMPTE 425-1 */
> > +             case XST352_BYTE1_ST372_DL_3GB:
> > +                     /* Table 13 SMPTE 425-2008 */
> > +             case XST352_BYTE1_ST372_2x1080L_3GB:
> > +                     /* Table 13 SMPTE 425-2008 */
> > +                     format->height = 1080;
> > +                     if (active_luma)
> > +                             format->width = 2048;
> > +                     else
> > +                             format->width = 1920;
> > +                     break;
> > +             default:
> > +                     dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case XSDIRX_MODE_6G_MASK:
> > +             switch (byte1) {
> > +             case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> > +                     /* Dual link 6G */
> > +             case XST352_BYTE1_ST2081_10_2160L_6G:
> > +                     /* Table 3 SMPTE ST 2081-10 */
> > +                     format->height = 2160;
> > +                     if (active_luma)
> > +                             format->width = 4096;
> > +                     else
> > +                             format->width = 3840;
> > +                     break;
> > +             default:
> > +                     dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case XSDIRX_MODE_12GI_MASK:
> > +     case XSDIRX_MODE_12GF_MASK:
> > +             switch (byte1) {
> > +             case XST352_BYTE1_ST2082_10_2160L_12G:
> > +                     /* Section 4.3.1 SMPTE ST 2082-10 */
> > +                     format->height = 2160;
> > +                     if (active_luma)
> > +                             format->width = 4096;
> > +                     else
> > +                             format->width = 3840;
> > +                     break;
> > +             default:
> > +                     dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     default:
> > +             dev_err(core->dev, "Invalid Mode\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (valid) {
> > +             if (pic_type)
> > +                     format->field = V4L2_FIELD_NONE;
> > +             else
> > +                     format->field = V4L2_FIELD_ALTERNATE;
> > +     }
> > +
> > +     if (format->field == V4L2_FIELD_ALTERNATE)
> > +             format->height = format->height / 2;
> > +
> > +     switch (sampling) {
> > +     case XST352_BYTE3_COLOR_FORMAT_422:
> > +             format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > +             break;
> > +     default:
> > +             dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> > +             return -EINVAL;
> > +     }
> > +
> > +     xsdirxss_get_framerate(&state->frame_interval, framerate);
> > +
> > +     dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload =
> 0x%08x ts = 0x%08x\n",
> > +             format->width, format->height, format->field, payload, val);
> > +     dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> > +             state->frame_interval.numerator,
> > +             state->frame_interval.denominator);
> > +     dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> > + * @irq: IRQ number
> > + * @dev_id: Pointer to device state
> > + *
> > + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> > + * in the interrupt clear register. The interrupt status register is read only.
> > + *
> > + * Return: IRQ_HANDLED after handling interrupts
> > + */
> > +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> > +{
> > +     struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> > +     struct xsdirxss_core *core = &state->core;
> > +     u32 status;
> > +
> > +     status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > +     dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> > +
> > +     if (!status)
> > +             return IRQ_NONE;
> > +
> > +     if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> > +             u32 val1, val2;
> > +
> > +             dev_dbg(core->dev, "video lock interrupt\n");
> > +             xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> > +
> > +             val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +
> > +             if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> > +                 (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> > +                     u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> > +                                XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> > +
> > +                     dev_dbg(core->dev, "mode & ts lock occurred\n");
> > +
> > +                     xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > +                     xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > +
> > +                     val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > +                     val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +
> > +                     dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> > +                     dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> > +
> > +                     if (!xsdirx_get_stream_properties(state)) {
> > +                             memset(&state->event, 0, sizeof(state->event));
> > +                             state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> > +                             state->event.u.src_change.changes =
> > +                                     V4L2_EVENT_SRC_CH_RESOLUTION;
> > +                             v4l2_subdev_notify_event(&state->subdev,
> > +                                                      &state->event);
> > +
> > +                             state->vidlocked = true;
> > +                     } else {
> > +                             dev_err(core->dev, "Unable to get stream properties!\n");
> > +                             state->vidlocked = false;
> > +                     }
> > +             } else {
> > +                     dev_dbg(core->dev, "video unlock before video lock!\n");
> > +                     state->vidlocked = false;
> > +             }
> > +     }
> > +
> > +     if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > +             dev_dbg(core->dev, "video unlock interrupt\n");
> > +             xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> > +             xsdirx_streamdowncb(core);
> > +
> > +             memset(&state->event, 0, sizeof(state->event));
> > +             state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> > +             v4l2_subdev_notify_event(&state->subdev, &state->event);
> > +
> > +             state->vidlocked = false;
> > +     }
> > +
> > +     if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > +             dev_dbg(core->dev, "Video in to AXI4 Stream core underflow
> interrupt\n");
> > +             xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> > +
> > +             memset(&state->event, 0, sizeof(state->event));
> > +             state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> > +             v4l2_subdev_notify_event(&state->subdev, &state->event);
> > +     }
> > +
> > +     if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > +             dev_dbg(core->dev, "Video in to AXI4 Stream core overflow
> interrupt\n");
> > +             xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> > +
> > +             memset(&state->event, 0, sizeof(state->event));
> > +             state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> > +             v4l2_subdev_notify_event(&state->subdev, &state->event);
> > +     }
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +/**
> > + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 File Handle
> > + * @sub: Subcribe event structure
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> > +                                 struct v4l2_fh *fh,
> > +                                 struct v4l2_event_subscription *sub)
> > +{
> > +     int ret;
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     switch (sub->type) {
> > +     case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> > +     case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> > +     case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> > +             ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> > +             break;
> > +     case V4L2_EVENT_SOURCE_CHANGE:
> > +             ret = v4l2_src_change_event_subscribe(fh, sub);
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +     dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> > +     return ret;
> > +}
> > +
> > +/**
> > + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> > +                                   struct v4l2_fh *fh,
> > +                                   struct v4l2_event_subscription *sub)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> > +     return v4l2_event_unsubscribe(fh, sub);
> > +}
> > +
> > +/**
> > + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> > + * @ctrl: V4L2 control to be set
> > + *
> > + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> > + * Subsystem.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +     int ret = 0;
> > +     struct xsdirxss_state *xsdirxss =
> > +             container_of(ctrl->handler,
> > +                          struct xsdirxss_state, ctrl_handler);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > +             ctrl->id, ctrl->val);
> > +
> > +     if (xsdirxss->streaming) {
> > +             dev_err(core->dev, "Cannot set controls while streaming\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     xsdirx_core_disable(core);
> > +     switch (ctrl->id) {
> > +     case V4L2_CID_XILINX_SDIRX_FRAMER:
> > +             xsdirx_framer(core, ctrl->val);
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > +             xsdirx_setvidlockwindow(core, ctrl->val);
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > +             xsdirx_setedherrcnttrigger(core, ctrl->val);
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > +             if (ctrl->val) {
> > +                     if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > +                             dev_dbg(core->dev, "Upto 3G supported\n");
> > +                             ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> > +                                            BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +                                            BIT(XSDIRX_MODE_12GF_OFFSET));
> > +                     }
> > +
> > +                     if (core->mode == XSDIRXSS_SDI_STD_6G) {
> > +                             dev_dbg(core->dev, "Upto 6G supported\n");
> > +                             ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +                                            BIT(XSDIRX_MODE_12GF_OFFSET));
> > +                     }
> > +
> > +                     ret = xsdirx_set_modedetect(core, ctrl->val);
> > +             } else {
> > +                     dev_err(core->dev, "Select at least one mode!\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     default:
> > +             xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_SS_EN_MASK);
> > +             return -EINVAL;
> > +     }
> > +     xsdirx_core_enable(core);
> > +     return ret;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> > + * @ctrl: Pointer to V4L2 control
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +     u32 val;
> > +     struct xsdirxss_state *xsdirxss =
> > +             container_of(ctrl->handler,
> > +                          struct xsdirxss_state, ctrl_handler);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     switch (ctrl->id) {
> > +     case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_err(core->dev, "Can't get values when video not
> locked!\n");
> > +                     return -EINVAL;
> > +             }
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > +             switch (val) {
> > +             case XSDIRX_MODE_SD_MASK:
> > +                     ctrl->val = XSDIRX_MODE_SD_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_HD_MASK:
> > +                     ctrl->val = XSDIRX_MODE_HD_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_3G_MASK:
> > +                     ctrl->val = XSDIRX_MODE_3G_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_6G_MASK:
> > +                     ctrl->val = XSDIRX_MODE_6G_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_12GI_MASK:
> > +                     ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_12GF_MASK:
> > +                     ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> > +                     break;
> > +             }
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_CRC:
> > +             ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> > +             xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +             if (val == XSDIRX_MODE_SD_MASK) {
> > +                     ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> > +             } else {
> > +                     dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +             if (val == XSDIRX_MODE_SD_MASK) {
> > +                     ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> > +             } else {
> > +                     dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_err(core->dev, "Can't get values when video not
> locked!\n");
> > +                     return -EINVAL;
> > +             }
> > +             ctrl->val = xsdirxss->ts_is_interlaced;
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_err(core->dev, "Can't get values when video not
> locked!\n");
> > +                     return -EINVAL;
> > +             }
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> > +             val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> > +             ctrl->val = 1 << val;
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_IS_3GB:
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_err(core->dev, "Can't get values when video not
> locked!\n");
> > +                     return -EINVAL;
> > +             }
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> > +             ctrl->val = val ? true : false;
> > +             break;
> > +     default:
> > +             dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > +             return -EINVAL;
> > +     }
> > +     dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> > +             ctrl->id, ctrl->val);
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> > + * @sd: Pointer to V4L2 subdevice structure
> > + *
> > + * This function prints the current status of Xilinx SDI Rx Subsystem
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +     u32 data, i;
> > +
> > +     v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > +     for (i = 0; i < 0x28; i++) {
> > +             data = xsdirxss_read(core, i * 4);
> > +             v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> > +                       i * 4, data);
> > +     }
> > +     v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> > +     return 0;
> > +}
> > +
> > +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +     xsdirx_streamflow_control(&xsdirxss->core, true);
> > +}
> > +
> > +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +     xsdirx_streamflow_control(&xsdirxss->core, false);
> > +}
> > +
> > +/**
> > + * xsdirxss_g_frame_interval - Get the frame interval
> > + * @sd: V4L2 Sub device
> > + * @fi: Pointer to V4l2 Sub device frame interval structure
> > + *
> > + * This function is used to get the frame interval.
> > + * The frame rate can be integral or fractional.
> > + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24
> fps
> > + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 =>
> 23.97 fps
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> > +                                  struct v4l2_subdev_frame_interval *fi)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     if (!xsdirxss->vidlocked) {
> > +             dev_err(core->dev, "Video not locked!\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     fi->interval = xsdirxss->frame_interval;
> > +
> > +     dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> > +             xsdirxss->frame_interval.numerator,
> > +             xsdirxss->frame_interval.denominator);
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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 SDI Rx Subsystem.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     if (enable) {
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_dbg(core->dev, "Video is not locked\n");
> > +                     return -EINVAL;
> > +             }
> > +             if (xsdirxss->streaming) {
> > +                     dev_dbg(core->dev, "Already streaming\n");
> > +                     return -EINVAL;
> > +             }
> > +
> > +             xsdirxss_start_stream(xsdirxss);
> > +             xsdirxss->streaming = true;
> > +             dev_dbg(core->dev, "Streaming started\n");
> > +     } else {
> > +             if (!xsdirxss->streaming) {
> > +                     dev_dbg(core->dev, "Stopped streaming already\n");
> > +                     return -EINVAL;
> > +             }
> > +
> > +             xsdirxss_stop_stream(xsdirxss);
> > +             xsdirxss->streaming = false;
> > +             dev_dbg(core->dev, "Streaming stopped\n");
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static struct v4l2_mbus_framefmt *
> > +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> > +                       struct v4l2_subdev_pad_config *cfg,
> > +                             unsigned int pad, u32 which)
> > +{
> > +     switch (which) {
> > +     case V4L2_SUBDEV_FORMAT_TRY:
> > +             return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> > +     case V4L2_SUBDEV_FORMAT_ACTIVE:
> > +             return &xsdirxss->formats[pad];
> > +     default:
> > +             return NULL;
> > +     }
> > +}
> > +
> > +/**
> > + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> > +                            struct v4l2_subdev_pad_config *cfg,
> > +                                     struct v4l2_subdev_format *fmt)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     if (!xsdirxss->vidlocked) {
> > +             dev_err(core->dev, "Video not locked!\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> > +                                              fmt->pad, fmt->which);
> > +
> > +     dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> > +             fmt->format.width, fmt->format.height, fmt->format.field);
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> > +                            struct v4l2_subdev_pad_config *cfg,
> > +                             struct v4l2_subdev_format *fmt)
> > +{
> > +     struct v4l2_mbus_framefmt *__format;
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > +     dev_dbg(xsdirxss->core.dev,
> > +             "set width %d height %d code %d field %d colorspace %d\n",
> > +             fmt->format.width, fmt->format.height,
> > +             fmt->format.code, fmt->format.field,
> > +             fmt->format.colorspace);
> > +
> > +     __format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> > +                                          fmt->pad, fmt->which);
> > +
> > +     /* Currently reset the code to one fixed in hardware */
> > +     /* TODO : Add checks for width height */
> > +     fmt->format.code = __format->code;
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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 pad.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_open(struct v4l2_subdev *sd,
> > +                      struct v4l2_subdev_fh *fh)
> > +{
> > +     struct v4l2_mbus_framefmt *format;
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > +     format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> > +     *format = xsdirxss->default_format;
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_close - Called on v4l2_close()
> > + * @sd: Pointer to V4L2 sub device structure
> > + * @fh: Pointer to V4L2 File handle
> > + *
> > + * This function is called on v4l2_close().
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_close(struct v4l2_subdev *sd,
> > +                       struct v4l2_subdev_fh *fh)
> > +{
> > +     return 0;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Media Operations
> > + */
> > +
> > +static const struct media_entity_operations xsdirxss_media_ops = {
> > +     .link_validate = v4l2_subdev_link_validate
> > +};
> > +
> > +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> > +     .g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> > +     .s_ctrl = xsdirxss_s_ctrl
> > +};
> > +
> > +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> > +     {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> > +             .name   = "SDI Rx : EDH Error Count Enable",
> > +             .type   = V4L2_CTRL_TYPE_BITMASK,
> > +             .min    = 0,
> > +             .max    = XSDIRX_EDH_ALLERR_MASK,
> > +             .def    = 0,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> > +             .name   = "SDI Rx : EDH Error Count",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 0,
> > +             .max    = 0xFFFF,
> > +             .step   = 1,
> > +             .def    = 0,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> > +             .name   = "SDI Rx : EDH Status",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 0,
> > +             .max    = 0xFFFFFFFF,
> > +             .step   = 1,
> > +             .def    = 0,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }
> > +};
> > +
> > +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> > +     {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_FRAMER,
> > +             .name   = "SDI Rx : Enable Framer",
> > +             .type   = V4L2_CTRL_TYPE_BOOLEAN,
> > +             .min    = false,
> > +             .max    = true,
> > +             .step   = 1,
> > +             .def    = true,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> > +             .name   = "SDI Rx : Video Lock Window",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 0,
> > +             .max    = 0xFFFFFFFF,
> > +             .step   = 1,
> > +             .def    = XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> > +             .name   = "SDI Rx : Modes search Mask",
> > +             .type   = V4L2_CTRL_TYPE_BITMASK,
> > +             .min    = 0,
> > +             .max    = XSDIRX_DETECT_ALL_MODES,
> > +             .def    = XSDIRX_DETECT_ALL_MODES,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> > +             .name   = "SDI Rx : Mode Detect Status",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = XSDIRX_MODE_SD_OFFSET,
> > +             .max    = XSDIRX_MODE_12GF_OFFSET,
> > +             .step   = 1,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_CRC,
> > +             .name   = "SDI Rx : CRC Error status",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 0,
> > +             .max    = 0xFFFFFFFF,
> > +             .step   = 1,
> > +             .def    = 0,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> > +             .name   = "SDI Rx : TS is Interlaced",
> > +             .type   = V4L2_CTRL_TYPE_BOOLEAN,
> > +             .min    = false,
> > +             .max    = true,
> > +             .def    = false,
> > +             .step   = 1,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> > +             .name   = "SDI Rx : Active Streams",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 1,
> > +             .max    = 16,
> > +             .def    = 1,
> > +             .step   = 1,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_IS_3GB,
> > +             .name   = "SDI Rx : Is 3GB",
> > +             .type   = V4L2_CTRL_TYPE_BOOLEAN,
> > +             .min    = false,
> > +             .max    = true,
> > +             .def    = false,
> > +             .step   = 1,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> > +     .log_status = xsdirxss_log_status,
> > +     .subscribe_event = xsdirxss_subscribe_event,
> > +     .unsubscribe_event = xsdirxss_unsubscribe_event
> > +};
> > +
> > +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > +     .g_frame_interval = xsdirxss_g_frame_interval,
> > +     .s_stream = xsdirxss_s_stream
> > +};
> > +
> > +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > +     .get_fmt = xsdirxss_get_format,
> > +     .set_fmt = xsdirxss_set_format,
> > +};
> > +
> > +static struct v4l2_subdev_ops xsdirxss_ops = {
> > +     .core = &xsdirxss_core_ops,
> > +     .video = &xsdirxss_video_ops,
> > +     .pad = &xsdirxss_pad_ops
> > +};
> > +
> > +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> > +     .open = xsdirxss_open,
> > +     .close = xsdirxss_close
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > +{
> > +     struct device_node *node = xsdirxss->core.dev->of_node;
> > +     struct device_node *ports = NULL;
> > +     struct device_node *port = NULL;
> > +     unsigned int nports = 0;
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +     int ret;
> > +     const char *sdi_std;
> > +
> > +     core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > +     dev_dbg(core->dev, "EDH property = %s\n",
> > +             core->include_edh ? "Present" : "Absent");
> > +
> > +     ret = of_property_read_string(node, "xlnx,line-rate",
> > +                                   &sdi_std);
> > +     if (ret < 0) {
> > +             dev_err(core->dev, "xlnx,line-rate property not found\n");
> > +             return ret;
> > +     }
> > +
> > +     if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> > +             core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> > +     } else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +             core->mode = XSDIRXSS_SDI_STD_6G;
> > +     } else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +             core->mode = XSDIRXSS_SDI_STD_3G;
> > +     } else {
> > +             dev_err(core->dev, "Invalid Line Rate\n");
> > +             return -EINVAL;
> > +     }
> > +     dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > +             core->mode);
> > +
> > +     ports = of_get_child_by_name(node, "ports");
> > +     if (!ports)
> > +             ports = node;
> > +
> > +     for_each_child_of_node(ports, port) {
> > +             const struct xvip_video_format *format;
> > +             struct device_node *endpoint;
> > +
> > +             if (!port->name || of_node_cmp(port->name, "port"))
> > +                     continue;
> > +
> > +             format = xvip_of_get_format(port);
> > +             if (IS_ERR(format)) {
> > +                     dev_err(core->dev, "invalid format in DT");
> > +                     return PTR_ERR(format);
> > +             }
> > +
> > +             dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> > +                     format->vf_code, format->width, format->bpp);
> > +
> > +             if (format->vf_code != XVIP_VF_YUV_422 &&
> > +                 format->vf_code != XVIP_VF_YUV_420) {
> > +                     dev_err(core->dev, "Incorrect UG934 video format set.\n");
> > +                     return -EINVAL;
> > +             }
> > +             xsdirxss->vip_format = format;
> > +
> > +             endpoint = of_get_next_child(port, NULL);
> > +             if (!endpoint) {
> > +                     dev_err(core->dev, "No port at\n");
> > +                     return -EINVAL;
> > +             }
> > +
> > +             /* Count the number of ports. */
> > +             nports++;
> > +     }
> > +
> > +     if (nports != 1) {
> > +             dev_err(core->dev, "invalid number of ports %u\n", nports);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Register interrupt handler */
> > +     core->irq = irq_of_parse_and_map(node, 0);
> > +
> > +     ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> > +                            IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> > +     if (ret) {
> > +             dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> > +                     ret);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int xsdirxss_probe(struct platform_device *pdev)
> > +{
> > +     struct v4l2_subdev *subdev;
> > +     struct xsdirxss_state *xsdirxss;
> > +     struct xsdirxss_core *core;
> > +     struct resource *res;
> > +     int ret;
> > +     unsigned int num_ctrls, num_edh_ctrls = 0, i;
> > +
> > +     xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> > +     if (!xsdirxss)
> > +             return -ENOMEM;
> > +
> > +     xsdirxss->core.dev = &pdev->dev;
> > +     core = &xsdirxss->core;
> > +
> > +     core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> > +     if (IS_ERR(core->axi_clk)) {
> > +             ret = PTR_ERR(core->axi_clk);
> > +             dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> > +     if (IS_ERR(core->sdirx_clk)) {
> > +             ret = PTR_ERR(core->sdirx_clk);
> > +             dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> > +     if (IS_ERR(core->vidout_clk)) {
> > +             ret = PTR_ERR(core->vidout_clk);
> > +             dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = clk_prepare_enable(core->axi_clk);
> > +     if (ret) {
> > +             dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = clk_prepare_enable(core->sdirx_clk);
> > +     if (ret) {
> > +             dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> > +             goto rx_clk_err;
> > +     }
> > +
> > +     ret = clk_prepare_enable(core->vidout_clk);
> > +     if (ret) {
> > +             dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> > +             goto vidout_clk_err;
> > +     }
> > +
> > +     ret = xsdirxss_parse_of(xsdirxss);
> > +     if (ret < 0)
> > +             goto clk_err;
> > +
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +     xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> > +     if (IS_ERR(xsdirxss->core.iomem)) {
> > +             ret = PTR_ERR(xsdirxss->core.iomem);
> > +             goto clk_err;
> > +     }
> > +
> > +     /* Reset the core */
> > +     xsdirx_streamflow_control(core, false);
> > +     xsdirx_core_disable(core);
> > +     xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> > +     xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +     xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > +     xsdirx_globalintr(core, true);
> > +     xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > +
> > +     /* Initialize V4L2 subdevice and media entity */
> > +     xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> > +
> > +     /* Initialize the default format */
> > +     xsdirxss->default_format.code = xsdirxss->vip_format->code;
> > +     xsdirxss->default_format.field = V4L2_FIELD_NONE;
> > +     xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> > +     xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> > +     xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> > +
> > +     xsdirxss->formats[0] = xsdirxss->default_format;
> > +
> > +     /* Initialize V4L2 subdevice and media entity */
> > +     subdev = &xsdirxss->subdev;
> > +     v4l2_subdev_init(subdev, &xsdirxss_ops);
> > +
> > +     subdev->dev = &pdev->dev;
> > +     subdev->internal_ops = &xsdirxss_internal_ops;
> > +     strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
> 
> Use strscpy.
> 

Ok. I will fix this in v2.

> > +
> > +     subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS |
> V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +
> > +     subdev->entity.ops = &xsdirxss_media_ops;
> > +
> > +     v4l2_set_subdevdata(subdev, xsdirxss);
> > +
> > +     ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> > +     if (ret < 0)
> > +             goto error;
> > +
> > +     /* Initialise and register the controls */
> > +     num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > +
> > +     if (xsdirxss->core.include_edh)
> > +             num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> > +
> > +     v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> > +                            (num_ctrls + num_edh_ctrls));
> > +
> > +     for (i = 0; i < num_ctrls; i++) {
> > +             struct v4l2_ctrl *ctrl;
> > +
> > +             dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> > +                     i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> > +
> > +             ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > +                                         &xsdirxss_ctrls[i], NULL);
> > +             if (!ctrl) {
> > +                     dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> > +                             xsdirxss_ctrls[i].name);
> > +                     goto error;
> > +             }
> > +     }
> > +
> > +     if (xsdirxss->core.include_edh) {
> > +             for (i = 0; i < num_edh_ctrls; i++) {
> > +                     struct v4l2_ctrl *ctrl;
> > +
> > +                     dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> > +                             i, xsdirxss_edh_ctrls[i].name,
> > +                             xsdirxss_edh_ctrls[i].id);
> > +
> > +                     ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > +                                                 &xsdirxss_edh_ctrls[i],
> > +                                                 NULL);
> > +                     if (!ctrl) {
> > +                             dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> > +                                     xsdirxss_edh_ctrls[i].name);
> > +                             goto error;
> > +                     }
> > +             }
> > +     } else {
> > +             dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as
> EDH is disabled in IP\n");
> > +     }
> > +
> > +     if (xsdirxss->ctrl_handler.error) {
> > +             dev_err(&pdev->dev, "failed to add controls\n");
> > +             ret = xsdirxss->ctrl_handler.error;
> > +             goto error;
> > +     }
> > +
> > +     subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> > +
> > +     ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> > +     if (ret < 0) {
> > +             dev_err(&pdev->dev, "failed to set controls\n");
> > +             goto error;
> > +     }
> > +
> > +     platform_set_drvdata(pdev, xsdirxss);
> > +
> > +     ret = v4l2_async_register_subdev(subdev);
> > +     if (ret < 0) {
> > +             dev_err(&pdev->dev, "failed to register subdev\n");
> > +             goto error;
> > +     }
> > +
> > +     xsdirxss->streaming = false;
> > +
> > +     dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> > +
> > +     xsdirx_core_enable(core);
> > +
> > +     return 0;
> > +error:
> > +     v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +     media_entity_cleanup(&subdev->entity);
> > +
> > +clk_err:
> > +     clk_disable_unprepare(core->vidout_clk);
> > +vidout_clk_err:
> > +     clk_disable_unprepare(core->sdirx_clk);
> > +rx_clk_err:
> > +     clk_disable_unprepare(core->axi_clk);
> > +     return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > +     struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > +     struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > +     v4l2_async_unregister_subdev(subdev);
> > +     v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +     media_entity_cleanup(&subdev->entity);
> > +     clk_disable_unprepare(xsdirxss->core.vidout_clk);
> > +     clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> > +     clk_disable_unprepare(xsdirxss->core.axi_clk);
> > +     return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > +     { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> > +
> > +static struct platform_driver xsdirxss_driver = {
> > +     .driver = {
> > +             .name           = "xilinx-sdirxss",
> > +             .of_match_table = xsdirxss_of_id_table,
> > +     },
> > +     .probe                  = xsdirxss_probe,
> > +     .remove                 = xsdirxss_remove,
> > +};
> > +
> > +module_platform_driver(xsdirxss_driver);
> > +
> > +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> > +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-
> sdirxss.h
> > new file mode 100644
> > index 0000000..983a43b
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,63 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode and flag definitions.
> > + *
> > + * Copyright (C) 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * This enum is used to prepare the bitmask of modes to be detected
> > + */
> > +enum {
> > +     XSDIRX_MODE_SD_OFFSET = 0,
> > +     XSDIRX_MODE_HD_OFFSET,
> > +     XSDIRX_MODE_3G_OFFSET,
> > +     XSDIRX_MODE_6G_OFFSET,
> > +     XSDIRX_MODE_12GI_OFFSET,
> > +     XSDIRX_MODE_12GF_OFFSET,
> > +     XSDIRX_MODE_NUM_SUPPORTED,
> > +};
> > +
> > +#define XSDIRX_DETECT_ALL_MODES
> (BIT(XSDIRX_MODE_SD_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_HD_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_3G_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_6G_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_12GI_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_12GF_OFFSET))
> > +
> > +/*
> > + * EDH Error Types
> > + * ANC - Ancillary Data Packet Errors
> > + * FF - Full Field Errors
> > + * AP - Active Portion Errors
> > + */
> > +
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR                BIT(0)
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR                BIT(1)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR                BIT(2)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR                BIT(3)
> > +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR                BIT(4)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR         BIT(5)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR         BIT(6)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR         BIT(7)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR         BIT(8)
> > +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR         BIT(9)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR         BIT(10)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR         BIT(11)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR         BIT(12)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR         BIT(13)
> > +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR         BIT(14)
> > +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR     BIT(15)
> > +
> > +#define XSDIRX_EDH_ALLERR_MASK               0xFFFF
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index f023623..4c68a10 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -83,4 +83,34 @@
> >  /* Reset all event counters */
> >  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS
> (V4L2_CID_XILINX_MIPICSISS + 2)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* Base ID */
> > +#define V4L2_CID_XILINX_SDIRX                        (V4L2_CID_USER_BASE +
> 0xc100)
> > +
> > +/* Framer Control */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER         (V4L2_CID_XILINX_SDIRX + 1)
> > +/* Video Lock Window Control */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> (V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> (V4L2_CID_XILINX_SDIRX + 3)
> > +/* Mode search Control */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> (V4L2_CID_XILINX_SDIRX + 4)
> > +/* Get Detected Mode control */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT    (V4L2_CID_XILINX_SDIRX
> + 5)
> > +/* Get CRC error status */
> > +#define V4L2_CID_XILINX_SDIRX_CRC            (V4L2_CID_XILINX_SDIRX + 6)
> > +/* Get EDH error count control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT     (V4L2_CID_XILINX_SDIRX +
> 7)
> > +/* Get EDH status control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS     (V4L2_CID_XILINX_SDIRX +
> 8)
> > +/* Get Transport Interlaced status */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> (V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get Active Streams count */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> (V4L2_CID_XILINX_SDIRX + 10)
> > +/* Is Mode 3GB */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB         (V4L2_CID_XILINX_SDIRX + 11)
> > +
> >  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-
> v4l2-events.h
> > index 2aa357c..7b47595 100644
> > --- a/include/uapi/linux/xilinx-v4l2-events.h
> > +++ b/include/uapi/linux/xilinx-v4l2-events.h
> > @@ -18,4 +18,13 @@
> >  /* Stream Line Buffer full */
> >  #define V4L2_EVENT_XLNXCSIRX_SLBF    (V4L2_EVENT_XLNXCSIRX_CLASS |
> 0x1)
> >
> > +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> > +#define V4L2_EVENT_XLNXSDIRX_CLASS   (V4L2_EVENT_PRIVATE_START |
> 0x200)
> > +/* Video Unlock event */
> > +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK
> (V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> > +/* Video in to AXI4 Stream core underflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW
> (V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> > +/* Video in to AXI4 Stream core overflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW
> (V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> > +
> >  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> >
> 
> I am concerned about this driver: I see that none of the *_dv_timings callbacks
> are implemented. I would expect to see that for a video receiver. There is also
> no g_input_status implemented.
> 
> Take a look at another SDI driver: drivers/media/spi/gs1662.c
> 

I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662 driver
requires the timings because it is a SDI Transmitter. 

Here the timings are not required as the IP block generates a AXI4 Stream.
I think it may be required only in case of native / parallel video being outputted
as the output stream needs timing information to be decoded.

Please feel free to correct my understanding if wrong.

In the current driver, the input stream properties like width, height, frame rate,
progressive/interlaced  are determined from the ST352 packet payload or from the
properties detected by the core.

See the xsdirx_get_stream_properties() for details.

> Some of the controls you add in this driver can likely be dropped. Especially
> those controls that are not specific to the Xilinx implementation but are
> generic for any SDI receiver, should be looked at closely: those are
> candidates for becoming standard controls.

I don't know how other SDI Receiver devices function.
So I am assuming all these controls are Xilinx specific implementations.

> 
> But the documentation above is simply insufficient for me to tell what is
> SDI specific and what is implementation specific.
> 

I will add more documentation for these controls.

> Also, I'm no SDI expert, certainly not for the UHD-SDI.
> 
> Regards,
> 
>         Hans

Regards
Vishal Sagar

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-14 11:44       ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-14 11:44 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus

Hi Hans,

Thanks for reviewing this patch.

> -----Original Message-----
> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> Sent: Wednesday, June 05, 2019 6:28 PM
> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>;
> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> EXTERNAL EMAIL
> 
> On 6/4/19 3:55 PM, Vishal Sagar wrote:
> > The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> > streams from SDI sources like SDI broadcast equipment like cameras and
> > mixers. This block outputs either native SDI, native video or
> > AXI4-Stream compliant data stream for further processing. Please refer
> > to PG290 for details.
> >
> > The driver is used to configure the IP to add framer, search for
> > specific modes, get the detected mode, stream parameters, errors, etc.
> > It also generates events for video lock/unlock, bridge over/under flow.
> >
> > The driver supports only 10 bpc YUV 422 media bus format. It also
> > decodes the stream parameters based on the ST352 packet embedded in the
> > stream. In case the ST352 packet isn't present in the stream, the core's
> > detected properties are used to set stream properties.
> >
> > The driver currently supports only the AXI4-Stream configuration.
> >
> > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > ---
> >  drivers/media/platform/xilinx/Kconfig          |   11 +
> >  drivers/media/platform/xilinx/Makefile         |    1 +
> >  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> ++++++++++++++++++++++++
> >  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> 
> My comments for the MIPI CSI-2 Rx driver apply here as well with respect
> to the naming of controls/events and documenting them.
> 
> Also, the contents of xilinx-v4l2-*.h can just be moved to xilinx-sdirxss.h.
> 

Ok I will update the patch series based on this and resend v2.

> >  6 files changed, 1960 insertions(+)
> >  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> >  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index cd1a0fd..0c68caa 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
> >         Bridge. The driver is used to set the number of active lanes and
> >         get short packet data.
> >
> > +config VIDEO_XILINX_SDIRXSS
> > +     tristate "Xilinx UHD SDI Rx Subsystem"
> > +     help
> > +       Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> > +       based driver that takes input from a SDI source like SDI camera and
> > +       converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> > +       UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> > +       AXI4-Stream bridge. The driver is used to set different stream
> > +       detection modes and identify stream properties to properly configure
> > +       downstream.
> > +
> >  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 6119a34..223f2ea 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > new file mode 100644
> > index 0000000..ba2d9d0
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,1846 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#include <dt-bindings/media/xilinx-vip.h>
> > +#include <linux/bitops.h>
> > +#include <linux/compiler.h>
> > +#include <linux/clk.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/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-sdirxss.h>
> > +#include <linux/xilinx-v4l2-events.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"
> > +
> > +/*
> > + * SDI Rx register map, bitmask and offsets
> > + */
> > +#define XSDIRX_RST_CTRL_REG          0x00
> > +#define XSDIRX_MDL_CTRL_REG          0x04
> > +#define XSDIRX_GLBL_IER_REG          0x0C
> > +#define XSDIRX_ISR_REG                       0x10
> > +#define XSDIRX_IER_REG                       0x14
> > +#define XSDIRX_ST352_VALID_REG               0x18
> > +#define XSDIRX_ST352_DS1_REG         0x1C
> > +#define XSDIRX_ST352_DS3_REG         0x20
> > +#define XSDIRX_ST352_DS5_REG         0x24
> > +#define XSDIRX_ST352_DS7_REG         0x28
> > +#define XSDIRX_ST352_DS9_REG         0x2C
> > +#define XSDIRX_ST352_DS11_REG                0x30
> > +#define XSDIRX_ST352_DS13_REG                0x34
> > +#define XSDIRX_ST352_DS15_REG                0x38
> > +#define XSDIRX_VERSION_REG           0x3C
> > +#define XSDIRX_SS_CONFIG_REG         0x40
> > +#define XSDIRX_MODE_DET_STAT_REG     0x44
> > +#define XSDIRX_TS_DET_STAT_REG               0x48
> > +#define XSDIRX_EDH_STAT_REG          0x4C
> > +#define XSDIRX_EDH_ERRCNT_EN_REG     0x50
> > +#define XSDIRX_EDH_ERRCNT_REG                0x54
> > +#define XSDIRX_CRC_ERRCNT_REG                0x58
> > +#define XSDIRX_VID_LOCK_WINDOW_REG   0x5C
> > +#define XSDIRX_SB_RX_STS_REG         0x60
> > +
> > +#define XSDIRX_RST_CTRL_SS_EN_MASK                   BIT(0)
> > +#define XSDIRX_RST_CTRL_SRST_MASK                    BIT(1)
> > +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK          BIT(2)
> > +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK          BIT(3)
> > +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK                BIT(8)
> > +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK     BIT(9)
> > +
> > +#define XSDIRX_MDL_CTRL_FRM_EN_MASK          BIT(4)
> > +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK     BIT(5)
> > +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK              BIT(8)
> > +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK              BIT(9)
> > +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK              BIT(10)
> > +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK              BIT(11)
> > +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK    BIT(12)
> > +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK    BIT(13)
> > +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK   GENMASK(13, 8)
> > +
> > +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET   16
> > +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK     GENMASK(18, 16)
> > +
> > +#define XSDIRX_GLBL_INTR_EN_MASK     BIT(0)
> > +
> > +#define XSDIRX_INTR_VIDLOCK_MASK     BIT(0)
> > +#define XSDIRX_INTR_VIDUNLOCK_MASK   BIT(1)
> > +#define XSDIRX_INTR_OVERFLOW_MASK    BIT(9)
> > +#define XSDIRX_INTR_UNDERFLOW_MASK   BIT(10)
> > +
> > +#define XSDIRX_INTR_ALL_MASK (XSDIRX_INTR_VIDLOCK_MASK |\
> > +                             XSDIRX_INTR_VIDUNLOCK_MASK |\
> > +                             XSDIRX_INTR_OVERFLOW_MASK |\
> > +                             XSDIRX_INTR_UNDERFLOW_MASK)
> > +
> > +#define XSDIRX_ST352_VALID_DS1_MASK  BIT(0)
> > +#define XSDIRX_ST352_VALID_DS3_MASK  BIT(1)
> > +#define XSDIRX_ST352_VALID_DS5_MASK  BIT(2)
> > +#define XSDIRX_ST352_VALID_DS7_MASK  BIT(3)
> > +#define XSDIRX_ST352_VALID_DS9_MASK  BIT(4)
> > +#define XSDIRX_ST352_VALID_DS11_MASK BIT(5)
> > +#define XSDIRX_ST352_VALID_DS13_MASK BIT(6)
> > +#define XSDIRX_ST352_VALID_DS15_MASK BIT(7)
> > +
> > +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK    GENMASK(2, 0)
> > +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK  BIT(3)
> > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK GENMASK(6, 4)
> > +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET       4
> > +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK    BIT(7)
> > +
> > +#define XSDIRX_ACTIVE_STREAMS_1              0x0
> > +#define XSDIRX_ACTIVE_STREAMS_2              0x1
> > +#define XSDIRX_ACTIVE_STREAMS_4              0x2
> > +#define XSDIRX_ACTIVE_STREAMS_8              0x3
> > +#define XSDIRX_ACTIVE_STREAMS_16     0x4
> > +
> > +#define XSDIRX_TS_DET_STAT_LOCKED_MASK               BIT(0)
> > +#define XSDIRX_TS_DET_STAT_SCAN_MASK         BIT(1)
> > +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET               (1)
> > +#define XSDIRX_TS_DET_STAT_FAMILY_MASK               GENMASK(7, 4)
> > +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET     (4)
> > +#define XSDIRX_TS_DET_STAT_RATE_MASK         GENMASK(11, 8)
> > +#define XSDIRX_TS_DET_STAT_RATE_OFFSET               (8)
> > +
> > +#define XSDIRX_TS_DET_STAT_RATE_NONE         0x0
> > +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ              0x2
> > +#define XSDIRX_TS_DET_STAT_RATE_24HZ         0x3
> > +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ              0x4
> > +#define XSDIRX_TS_DET_STAT_RATE_25HZ         0x5
> > +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ              0x6
> > +#define XSDIRX_TS_DET_STAT_RATE_30HZ         0x7
> > +#define XSDIRX_TS_DET_STAT_RATE_48HZ         0x8
> > +#define XSDIRX_TS_DET_STAT_RATE_50HZ         0x9
> > +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ              0xA
> > +#define XSDIRX_TS_DET_STAT_RATE_60HZ         0xB
> > +
> > +#define XSDIRX_EDH_STAT_EDH_AP_MASK  BIT(0)
> > +#define XSDIRX_EDH_STAT_EDH_FF_MASK  BIT(1)
> > +#define XSDIRX_EDH_STAT_EDH_ANC_MASK BIT(2)
> > +#define XSDIRX_EDH_STAT_AP_FLAG_MASK GENMASK(8, 4)
> > +#define XSDIRX_EDH_STAT_FF_FLAG_MASK GENMASK(13, 9)
> > +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK        GENMASK(18, 14)
> > +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK        GENMASK(22, 19)
> > +
> > +#define XSDIRX_EDH_ERRCNT_COUNT_MASK GENMASK(15, 0)
> > +
> > +#define XSDIRX_CRC_ERRCNT_COUNT_MASK GENMASK(31, 16)
> > +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK        GENMASK(15, 0)
> > +
> > +#define XSDIRX_VERSION_REV_MASK              GENMASK(7, 0)
> > +#define XSDIRX_VERSION_PATCHID_MASK  GENMASK(11, 8)
> > +#define XSDIRX_VERSION_VER_REV_MASK  GENMASK(15, 12)
> > +#define XSDIRX_VERSION_VER_MIN_MASK  GENMASK(23, 16)
> > +#define XSDIRX_VERSION_VER_MAJ_MASK  GENMASK(31, 24)
> > +
> > +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK           BIT(1)
> > +
> > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK     BIT(0)
> > +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK     BIT(1)
> > +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK    BIT(2)
> > +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK              BIT(3)
> > +
> > +/* Number of media pads */
> > +#define XSDIRX_MEDIA_PADS    (1)
> > +
> > +#define XSDIRX_DEFAULT_WIDTH (1920)
> > +#define XSDIRX_DEFAULT_HEIGHT        (1080)
> > +
> > +#define XSDIRX_MAX_STR_LENGTH        16
> > +
> > +#define XSDIRXSS_SDI_STD_3G          0
> > +#define XSDIRXSS_SDI_STD_6G          1
> > +#define XSDIRXSS_SDI_STD_12G_8DS     2
> > +
> > +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW     0x3000
> > +
> > +#define XSDIRX_MODE_HD_MASK  0x0
> > +#define XSDIRX_MODE_SD_MASK  0x1
> > +#define XSDIRX_MODE_3G_MASK  0x2
> > +#define XSDIRX_MODE_6G_MASK  0x4
> > +#define XSDIRX_MODE_12GI_MASK        0x5
> > +#define XSDIRX_MODE_12GF_MASK        0x6
> > +
> > +/*
> > + * Maximum number of events per file handle.
> > + */
> > +#define XSDIRX_MAX_EVENTS    (128)
> > +
> > +/* ST352 related macros */
> > +#define XST352_PAYLOAD_BYTE_MASK     0xFF
> > +#define XST352_PAYLOAD_BYTE1_SHIFT   0
> > +#define XST352_PAYLOAD_BYTE2_SHIFT   8
> > +#define XST352_PAYLOAD_BYTE3_SHIFT   16
> > +#define XST352_PAYLOAD_BYTE4_SHIFT   24
> > +
> > +#define XST352_BYTE1_ST292_1x720L_1_5G               0x84
> > +#define XST352_BYTE1_ST292_1x1080L_1_5G              0x85
> > +#define XST352_BYTE1_ST425_2008_750L_3GB     0x88
> > +#define XST352_BYTE1_ST425_2008_1125L_3GA    0x89
> > +#define XST352_BYTE1_ST372_DL_3GB            0x8A
> > +#define XST352_BYTE1_ST372_2x720L_3GB                0x8B
> > +#define XST352_BYTE1_ST372_2x1080L_3GB               0x8C
> > +#define XST352_BYTE1_ST2081_10_2160L_6G              0xC0
> > +#define XST352_BYTE1_ST2081_10_DL_2160L_6G   0xC2
> > +#define XST352_BYTE1_ST2082_10_2160L_12G     0xCE
> > +
> > +#define XST352_BYTE2_TS_TYPE_MASK            BIT(15)
> > +#define XST352_BYTE2_TS_TYPE_OFFSET          15
> > +#define XST352_BYTE2_PIC_TYPE_MASK           BIT(14)
> > +#define XST352_BYTE2_PIC_TYPE_OFFSET         14
> > +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED  0
> > +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE 1
> > +
> > +#define XST352_BYTE2_FPS_MASK                        0xF
> > +#define XST352_BYTE2_FPS_SHIFT                       8
> > +#define XST352_BYTE2_FPS_24F                 0x2
> > +#define XST352_BYTE2_FPS_24                  0x3
> > +#define XST352_BYTE2_FPS_48F                 0x4
> > +#define XST352_BYTE2_FPS_25                  0x5
> > +#define XST352_BYTE2_FPS_30F                 0x6
> > +#define XST352_BYTE2_FPS_30                  0x7
> > +#define XST352_BYTE2_FPS_48                  0x8
> > +#define XST352_BYTE2_FPS_50                  0x9
> > +#define XST352_BYTE2_FPS_60F                 0xA
> > +#define XST352_BYTE2_FPS_60                  0xB
> > +/* Table 4 ST 2081-10:2015 */
> > +#define XST352_BYTE2_FPS_96                  0xC
> > +#define XST352_BYTE2_FPS_100                 0xD
> > +#define XST352_BYTE2_FPS_120                 0xE
> > +#define XST352_BYTE2_FPS_120F                        0xF
> > +
> > +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK     BIT(22)
> > +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET   22
> > +
> > +#define XST352_BYTE3_COLOR_FORMAT_MASK               GENMASK(19, 16)
> > +#define XST352_BYTE3_COLOR_FORMAT_OFFSET     16
> > +#define XST352_BYTE3_COLOR_FORMAT_422                0x0
> > +#define XST352_BYTE3_COLOR_FORMAT_420                0x3
> > +
> > +/**
> > + * enum sdi_family_enc - SDI Transport Video Format Detected with Active
> Pixels
> > + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> > + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> > + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP
> 2048x1080
> > + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> > + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> > + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> > + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> > + */
> > +enum sdi_family_enc {
> > +     XSDIRX_SMPTE_ST_274     = 0,
> > +     XSDIRX_SMPTE_ST_296     = 1,
> > +     XSDIRX_SMPTE_ST_2048_2  = 2,
> > +     XSDIRX_SMPTE_ST_295     = 3,
> > +     XSDIRX_NTSC             = 8,
> > +     XSDIRX_PAL              = 9,
> > +     XSDIRX_TS_UNKNOWN       = 15
> > +};
> > +
> > +/**
> > + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device
> structure
> > + * @dev: Platform structure
> > + * @iomem: Base address of subsystem
> > + * @irq: requested irq number
> > + * @include_edh: EDH processor presence
> > + * @mode: 3G/6G/12G mode
> > + * @axi_clk: Axi lite interface clock
> > + * @sdirx_clk: SDI Rx GT clock
> > + * @vidout_clk: Video clock
> > + */
> > +struct xsdirxss_core {
> > +     struct device *dev;
> > +     void __iomem *iomem;
> > +     int irq;
> > +     bool include_edh;
> > +     int mode;
> > +     struct clk *axi_clk;
> > +     struct clk *sdirx_clk;
> > +     struct clk *vidout_clk;
> > +};
> > +
> > +/**
> > + * struct xsdirxss_state - SDI Rx Subsystem device structure
> > + * @core: Core structure for MIPI SDI Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @event: Holds the video unlock event
> > + * @formats: Active V4L2 formats on each pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @vip_format: format information corresponding to the active format
> > + * @pads: media pads
> > + * @streaming: Flag for storing streaming state
> > + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> > + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xsdirxss_state {
> > +     struct xsdirxss_core core;
> > +     struct v4l2_subdev subdev;
> > +     struct v4l2_ctrl_handler ctrl_handler;
> > +     struct v4l2_event event;
> > +     struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> > +     struct v4l2_mbus_framefmt default_format;
> > +     struct v4l2_fract frame_interval;
> > +     const struct xvip_video_format *vip_format;
> > +     struct media_pad pads[XSDIRX_MEDIA_PADS];
> > +     bool streaming;
> > +     bool vidlocked;
> > +     bool ts_is_interlaced;
> > +};
> > +
> > +static inline struct xsdirxss_state *
> > +to_xsdirxssstate(struct v4l2_subdev *subdev)
> > +{
> > +     return container_of(subdev, struct xsdirxss_state, subdev);
> > +}
> > +
> > +/*
> > + * Register related operations
> > + */
> > +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> > +{
> > +     return ioread32(xsdirxss->iomem + addr);
> > +}
> > +
> > +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> > +                               u32 value)
> > +{
> > +     iowrite32(value, xsdirxss->iomem + addr);
> > +}
> > +
> > +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> > +                             u32 clr)
> > +{
> > +     xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> > +}
> > +
> > +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> > +                             u32 set)
> > +{
> > +     xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> > +}
> > +
> > +static void xsdirx_core_disable(struct xsdirxss_core *core)
> > +{
> > +     xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static void xsdirx_core_enable(struct xsdirxss_core *core)
> > +{
> > +     xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> > +{
> > +     u32 i, val;
> > +
> > +     mask &= XSDIRX_DETECT_ALL_MODES;
> > +     if (!mask) {
> > +             dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> > +             return -EINVAL;
> > +     }
> > +
> > +     dev_dbg(core->dev, "mask = 0x%x\n", mask);
> > +
> > +     val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> > +     val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> > +     val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> > +     val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> > +
> > +     if (hweight16(mask) > 1) {
> > +             /* Multi mode detection as more than 1 bit set in mask */
> > +             dev_dbg(core->dev, "Detect multiple modes\n");
> > +             for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> > +                     switch (mask & (1 << i)) {
> > +                     case BIT(XSDIRX_MODE_SD_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_HD_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_3G_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_6G_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_12GI_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > +                             break;
> > +                     case BIT(XSDIRX_MODE_12GF_OFFSET):
> > +                             val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > +                             break;
> > +                     }
> > +             }
> > +             val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > +     } else {
> > +             /* Fixed Mode */
> > +             u32 forced_mode_mask = 0;
> > +
> > +             dev_dbg(core->dev, "Detect fixed mode\n");
> > +
> > +             /* Find offset of first bit set */
> > +             switch (__ffs(mask)) {
> > +             case XSDIRX_MODE_SD_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_SD_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_HD_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_HD_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_3G_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_3G_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_6G_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_6G_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_12GI_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> > +                     break;
> > +             case XSDIRX_MODE_12GF_OFFSET:
> > +                     forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> > +                     break;
> > +             }
> > +             dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> > +                     forced_mode_mask);
> > +             val |= forced_mode_mask <<
> XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > +     }
> > +
> > +     dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> > +             val);
> > +     xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> > +
> > +     return 0;
> > +}
> > +
> > +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> > +{
> > +     if (flag)
> > +             xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> > +                          XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > +     else
> > +             xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> > +                          XSDIRX_MDL_CTRL_FRM_EN_MASK);
> > +}
> > +
> > +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32
> enable)
> > +{
> > +     u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> > +
> > +     val = enable & XSDIRX_EDH_ALLERR_MASK;
> > +
> > +     xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> > +}
> > +
> > +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> > +{
> > +     /*
> > +      * The video lock window is the amount of time for which the
> > +      * the mode and transport stream should be locked to get the
> > +      * video lock interrupt.
> > +      */
> > +     xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> > +}
> > +
> > +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +     xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +     xsdirxss_set(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> > +{
> > +     if (flag)
> > +             xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> > +                          XSDIRX_GLBL_INTR_EN_MASK);
> > +     else
> > +             xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> > +                          XSDIRX_GLBL_INTR_EN_MASK);
> > +}
> > +
> > +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +     xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > +}
> > +
> > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +     if (enable)
> > +             xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> > +     else
> > +             xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> > +}
> > +
> > +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +     if (enable)
> > +             xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +     else
> > +             xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +}
> > +
> > +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +     /* The sdi to native bridge is followed by native to axis4 bridge */
> > +     if (enable) {
> > +             xsdirx_axis4_bridge_control(core, enable);
> > +             xsdirx_vid_bridge_control(core, enable);
> > +     } else {
> > +             xsdirx_vid_bridge_control(core, enable);
> > +             xsdirx_axis4_bridge_control(core, enable);
> > +     }
> > +}
> > +
> > +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> > +{
> > +     xsdirx_streamflow_control(core, false);
> > +}
> > +
> > +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> > +                                u32 framerate)
> > +{
> > +     switch (framerate) {
> > +     case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > +             frame_interval->numerator = 1001;
> > +             frame_interval->denominator = 24000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 24000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 25000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > +             frame_interval->numerator = 1001;
> > +             frame_interval->denominator = 30000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 30000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> > +             frame_interval->numerator = 1001;
> > +             frame_interval->denominator = 48000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_48HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 48000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 50000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > +             frame_interval->numerator = 1001;
> > +             frame_interval->denominator = 60000;
> > +             break;
> > +     case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > +             frame_interval->numerator = 1000;
> > +             frame_interval->denominator = 60000;
> > +             break;
> > +     default:
> > +             frame_interval->numerator = 1;
> > +             frame_interval->denominator = 1;
> > +     }
> > +}
> > +
> > +/**
> > + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> > + * @state: pointer to driver state
> > + *
> > + * This function decodes the stream's ST352 payload (if available) to get
> > + * stream properties like width, height, picture type (interlaced/progressive),
> > + * etc.
> > + *
> > + * Return: 0 for success else errors
> > + */
> > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > +{
> > +     struct xsdirxss_core *core = &state->core;
> > +     u32 mode, payload = 0, val, family, valid, tscan;
> > +     u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> > +     u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> > +     struct v4l2_mbus_framefmt *format = &state->formats[0];
> > +
> > +     mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +     mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > +     valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > +
> > +     if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> > +             dev_err(core->dev, "No valid ST352 payload present even for 3G
> mode and above\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +     if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > +             payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +             byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > +                             XST352_PAYLOAD_BYTE_MASK;
> > +             active_luma = (payload &
> XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > +                             XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > +             pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > +                             XST352_BYTE2_PIC_TYPE_OFFSET;
> > +             framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > +                             XST352_BYTE2_FPS_MASK;
> > +             tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > +                             XST352_BYTE2_TS_TYPE_OFFSET;
> > +             sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> > +                        XST352_BYTE3_COLOR_FORMAT_OFFSET;
> > +     } else {
> > +             dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> > +                     mode);
> > +             framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> > +                             XSDIRX_TS_DET_STAT_RATE_OFFSET;
> > +             tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> > +                             XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> > +     }
> > +
> > +     family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> > +               XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> > +     state->ts_is_interlaced = tscan ? false : true;
> > +
> > +     dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> > +             state->ts_is_interlaced, family);
> > +
> > +     switch (mode) {
> > +     case XSDIRX_MODE_HD_MASK:
> > +             if (!valid) {
> > +                     /* No payload obtained */
> > +                     dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> > +                             framerate, tscan);
> > +                     /*
> > +                      * NOTE : A progressive segmented frame pSF will be
> > +                      * reported incorrectly as Interlaced as we rely on IP's
> > +                      * transport scan locked bit.
> > +                      */
> > +                     dev_warn(core->dev, "pSF will be incorrectly reported as
> Interlaced\n");
> > +
> > +                     switch (framerate) {
> > +                     case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > +                             if (family == XSDIRX_SMPTE_ST_296) {
> > +                                     format->width = 1280;
> > +                                     format->height = 720;
> > +                                     format->field = V4L2_FIELD_NONE;
> > +                             } else if (family == XSDIRX_SMPTE_ST_2048_2) {
> > +                                     format->width = 2048;
> > +                                     format->height = 1080;
> > +                                     if (tscan)
> > +                                             format->field = V4L2_FIELD_NONE;
> > +                                     else
> > +                                             format->field =
> > +                                                     V4L2_FIELD_ALTERNATE;
> > +                             } else {
> > +                                     format->width = 1920;
> > +                                     format->height = 1080;
> > +                                     if (tscan)
> > +                                             format->field = V4L2_FIELD_NONE;
> > +                                     else
> > +                                             format->field =
> > +                                                     V4L2_FIELD_ALTERNATE;
> > +                             }
> > +                             break;
> > +                     case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > +                     case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > +                             if (family == XSDIRX_SMPTE_ST_274) {
> > +                                     format->width = 1920;
> > +                                     format->height = 1080;
> > +                             } else {
> > +                                     format->width = 1280;
> > +                                     format->height = 720;
> > +                             }
> > +                             format->field = V4L2_FIELD_NONE;
> > +                             break;
> > +                     default:
> > +                             format->width = 1920;
> > +                             format->height = 1080;
> > +                             format->field = V4L2_FIELD_NONE;
> > +                     }
> > +             } else {
> > +                     dev_dbg(core->dev, "Got the payload\n");
> > +                     switch (byte1) {
> > +                     case XST352_BYTE1_ST292_1x720L_1_5G:
> > +                             /* SMPTE ST 292-1 for 720 line payloads */
> > +                             format->width = 1280;
> > +                             format->height = 720;
> > +                             break;
> > +                     case XST352_BYTE1_ST292_1x1080L_1_5G:
> > +                             /* SMPTE ST 292-1 for 1080 line payloads */
> > +                             format->height = 1080;
> > +                             if (active_luma)
> > +                                     format->width = 2048;
> > +                             else
> > +                                     format->width = 1920;
> > +                             break;
> > +                     default:
> > +                             dev_dbg(core->dev, "Unknown HD Mode SMPTE
> standard\n");
> > +                             return -EINVAL;
> > +                     }
> > +             }
> > +             break;
> > +     case XSDIRX_MODE_SD_MASK:
> > +             format->field = V4L2_FIELD_ALTERNATE;
> > +
> > +             switch (family) {
> > +             case XSDIRX_NTSC:
> > +                     format->width = 720;
> > +                     format->height = 486;
> > +                     break;
> > +             case XSDIRX_PAL:
> > +                     format->width = 720;
> > +                     format->height = 576;
> > +                     break;
> > +             default:
> > +                     dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case XSDIRX_MODE_3G_MASK:
> > +             switch (byte1) {
> > +             case XST352_BYTE1_ST425_2008_750L_3GB:
> > +                     /* Sec 4.1.6.1 SMPTE 425-2008 */
> > +             case XST352_BYTE1_ST372_2x720L_3GB:
> > +                     /* Table 13 SMPTE 425-2008 */
> > +                     format->width = 1280;
> > +                     format->height = 720;
> > +                     break;
> > +             case XST352_BYTE1_ST425_2008_1125L_3GA:
> > +                     /* ST352 Table SMPTE 425-1 */
> > +             case XST352_BYTE1_ST372_DL_3GB:
> > +                     /* Table 13 SMPTE 425-2008 */
> > +             case XST352_BYTE1_ST372_2x1080L_3GB:
> > +                     /* Table 13 SMPTE 425-2008 */
> > +                     format->height = 1080;
> > +                     if (active_luma)
> > +                             format->width = 2048;
> > +                     else
> > +                             format->width = 1920;
> > +                     break;
> > +             default:
> > +                     dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case XSDIRX_MODE_6G_MASK:
> > +             switch (byte1) {
> > +             case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> > +                     /* Dual link 6G */
> > +             case XST352_BYTE1_ST2081_10_2160L_6G:
> > +                     /* Table 3 SMPTE ST 2081-10 */
> > +                     format->height = 2160;
> > +                     if (active_luma)
> > +                             format->width = 4096;
> > +                     else
> > +                             format->width = 3840;
> > +                     break;
> > +             default:
> > +                     dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case XSDIRX_MODE_12GI_MASK:
> > +     case XSDIRX_MODE_12GF_MASK:
> > +             switch (byte1) {
> > +             case XST352_BYTE1_ST2082_10_2160L_12G:
> > +                     /* Section 4.3.1 SMPTE ST 2082-10 */
> > +                     format->height = 2160;
> > +                     if (active_luma)
> > +                             format->width = 4096;
> > +                     else
> > +                             format->width = 3840;
> > +                     break;
> > +             default:
> > +                     dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     default:
> > +             dev_err(core->dev, "Invalid Mode\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     if (valid) {
> > +             if (pic_type)
> > +                     format->field = V4L2_FIELD_NONE;
> > +             else
> > +                     format->field = V4L2_FIELD_ALTERNATE;
> > +     }
> > +
> > +     if (format->field == V4L2_FIELD_ALTERNATE)
> > +             format->height = format->height / 2;
> > +
> > +     switch (sampling) {
> > +     case XST352_BYTE3_COLOR_FORMAT_422:
> > +             format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > +             break;
> > +     default:
> > +             dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> > +             return -EINVAL;
> > +     }
> > +
> > +     xsdirxss_get_framerate(&state->frame_interval, framerate);
> > +
> > +     dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload =
> 0x%08x ts = 0x%08x\n",
> > +             format->width, format->height, format->field, payload, val);
> > +     dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> > +             state->frame_interval.numerator,
> > +             state->frame_interval.denominator);
> > +     dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> > + * @irq: IRQ number
> > + * @dev_id: Pointer to device state
> > + *
> > + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> > + * in the interrupt clear register. The interrupt status register is read only.
> > + *
> > + * Return: IRQ_HANDLED after handling interrupts
> > + */
> > +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> > +{
> > +     struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> > +     struct xsdirxss_core *core = &state->core;
> > +     u32 status;
> > +
> > +     status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > +     dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> > +
> > +     if (!status)
> > +             return IRQ_NONE;
> > +
> > +     if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> > +             u32 val1, val2;
> > +
> > +             dev_dbg(core->dev, "video lock interrupt\n");
> > +             xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> > +
> > +             val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +
> > +             if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> > +                 (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> > +                     u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> > +                                XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> > +
> > +                     dev_dbg(core->dev, "mode & ts lock occurred\n");
> > +
> > +                     xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > +                     xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > +
> > +                     val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> > +                     val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +
> > +                     dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> > +                     dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> > +
> > +                     if (!xsdirx_get_stream_properties(state)) {
> > +                             memset(&state->event, 0, sizeof(state->event));
> > +                             state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> > +                             state->event.u.src_change.changes =
> > +                                     V4L2_EVENT_SRC_CH_RESOLUTION;
> > +                             v4l2_subdev_notify_event(&state->subdev,
> > +                                                      &state->event);
> > +
> > +                             state->vidlocked = true;
> > +                     } else {
> > +                             dev_err(core->dev, "Unable to get stream properties!\n");
> > +                             state->vidlocked = false;
> > +                     }
> > +             } else {
> > +                     dev_dbg(core->dev, "video unlock before video lock!\n");
> > +                     state->vidlocked = false;
> > +             }
> > +     }
> > +
> > +     if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > +             dev_dbg(core->dev, "video unlock interrupt\n");
> > +             xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> > +             xsdirx_streamdowncb(core);
> > +
> > +             memset(&state->event, 0, sizeof(state->event));
> > +             state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> > +             v4l2_subdev_notify_event(&state->subdev, &state->event);
> > +
> > +             state->vidlocked = false;
> > +     }
> > +
> > +     if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > +             dev_dbg(core->dev, "Video in to AXI4 Stream core underflow
> interrupt\n");
> > +             xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> > +
> > +             memset(&state->event, 0, sizeof(state->event));
> > +             state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> > +             v4l2_subdev_notify_event(&state->subdev, &state->event);
> > +     }
> > +
> > +     if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > +             dev_dbg(core->dev, "Video in to AXI4 Stream core overflow
> interrupt\n");
> > +             xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> > +
> > +             memset(&state->event, 0, sizeof(state->event));
> > +             state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> > +             v4l2_subdev_notify_event(&state->subdev, &state->event);
> > +     }
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +/**
> > + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> > + * @sd: V4L2 Sub device
> > + * @fh: V4L2 File Handle
> > + * @sub: Subcribe event structure
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> > +                                 struct v4l2_fh *fh,
> > +                                 struct v4l2_event_subscription *sub)
> > +{
> > +     int ret;
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     switch (sub->type) {
> > +     case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> > +     case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> > +     case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> > +             ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> > +             break;
> > +     case V4L2_EVENT_SOURCE_CHANGE:
> > +             ret = v4l2_src_change_event_subscribe(fh, sub);
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +     dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> > +     return ret;
> > +}
> > +
> > +/**
> > + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> > +                                   struct v4l2_fh *fh,
> > +                                   struct v4l2_event_subscription *sub)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> > +     return v4l2_event_unsubscribe(fh, sub);
> > +}
> > +
> > +/**
> > + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> > + * @ctrl: V4L2 control to be set
> > + *
> > + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> > + * Subsystem.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +     int ret = 0;
> > +     struct xsdirxss_state *xsdirxss =
> > +             container_of(ctrl->handler,
> > +                          struct xsdirxss_state, ctrl_handler);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > +             ctrl->id, ctrl->val);
> > +
> > +     if (xsdirxss->streaming) {
> > +             dev_err(core->dev, "Cannot set controls while streaming\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     xsdirx_core_disable(core);
> > +     switch (ctrl->id) {
> > +     case V4L2_CID_XILINX_SDIRX_FRAMER:
> > +             xsdirx_framer(core, ctrl->val);
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > +             xsdirx_setvidlockwindow(core, ctrl->val);
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > +             xsdirx_setedherrcnttrigger(core, ctrl->val);
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > +             if (ctrl->val) {
> > +                     if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > +                             dev_dbg(core->dev, "Upto 3G supported\n");
> > +                             ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> > +                                            BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +                                            BIT(XSDIRX_MODE_12GF_OFFSET));
> > +                     }
> > +
> > +                     if (core->mode == XSDIRXSS_SDI_STD_6G) {
> > +                             dev_dbg(core->dev, "Upto 6G supported\n");
> > +                             ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +                                            BIT(XSDIRX_MODE_12GF_OFFSET));
> > +                     }
> > +
> > +                     ret = xsdirx_set_modedetect(core, ctrl->val);
> > +             } else {
> > +                     dev_err(core->dev, "Select at least one mode!\n");
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     default:
> > +             xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +                          XSDIRX_RST_CTRL_SS_EN_MASK);
> > +             return -EINVAL;
> > +     }
> > +     xsdirx_core_enable(core);
> > +     return ret;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> > + * @ctrl: Pointer to V4L2 control
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +     u32 val;
> > +     struct xsdirxss_state *xsdirxss =
> > +             container_of(ctrl->handler,
> > +                          struct xsdirxss_state, ctrl_handler);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     switch (ctrl->id) {
> > +     case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_err(core->dev, "Can't get values when video not
> locked!\n");
> > +                     return -EINVAL;
> > +             }
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > +             switch (val) {
> > +             case XSDIRX_MODE_SD_MASK:
> > +                     ctrl->val = XSDIRX_MODE_SD_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_HD_MASK:
> > +                     ctrl->val = XSDIRX_MODE_HD_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_3G_MASK:
> > +                     ctrl->val = XSDIRX_MODE_3G_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_6G_MASK:
> > +                     ctrl->val = XSDIRX_MODE_6G_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_12GI_MASK:
> > +                     ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> > +                     break;
> > +             case XSDIRX_MODE_12GF_MASK:
> > +                     ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> > +                     break;
> > +             }
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_CRC:
> > +             ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> > +             xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +             if (val == XSDIRX_MODE_SD_MASK) {
> > +                     ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> > +             } else {
> > +                     dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +             if (val == XSDIRX_MODE_SD_MASK) {
> > +                     ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> > +             } else {
> > +                     dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> > +                     return -EINVAL;
> > +             }
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_err(core->dev, "Can't get values when video not
> locked!\n");
> > +                     return -EINVAL;
> > +             }
> > +             ctrl->val = xsdirxss->ts_is_interlaced;
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_err(core->dev, "Can't get values when video not
> locked!\n");
> > +                     return -EINVAL;
> > +             }
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> > +             val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> > +             ctrl->val = 1 << val;
> > +             break;
> > +     case V4L2_CID_XILINX_SDIRX_IS_3GB:
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_err(core->dev, "Can't get values when video not
> locked!\n");
> > +                     return -EINVAL;
> > +             }
> > +             val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > +             val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> > +             ctrl->val = val ? true : false;
> > +             break;
> > +     default:
> > +             dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > +             return -EINVAL;
> > +     }
> > +     dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> > +             ctrl->id, ctrl->val);
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> > + * @sd: Pointer to V4L2 subdevice structure
> > + *
> > + * This function prints the current status of Xilinx SDI Rx Subsystem
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +     u32 data, i;
> > +
> > +     v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > +     for (i = 0; i < 0x28; i++) {
> > +             data = xsdirxss_read(core, i * 4);
> > +             v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> > +                       i * 4, data);
> > +     }
> > +     v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> > +     return 0;
> > +}
> > +
> > +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +     xsdirx_streamflow_control(&xsdirxss->core, true);
> > +}
> > +
> > +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +     xsdirx_streamflow_control(&xsdirxss->core, false);
> > +}
> > +
> > +/**
> > + * xsdirxss_g_frame_interval - Get the frame interval
> > + * @sd: V4L2 Sub device
> > + * @fi: Pointer to V4l2 Sub device frame interval structure
> > + *
> > + * This function is used to get the frame interval.
> > + * The frame rate can be integral or fractional.
> > + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24
> fps
> > + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 =>
> 23.97 fps
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> > +                                  struct v4l2_subdev_frame_interval *fi)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     if (!xsdirxss->vidlocked) {
> > +             dev_err(core->dev, "Video not locked!\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     fi->interval = xsdirxss->frame_interval;
> > +
> > +     dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> > +             xsdirxss->frame_interval.numerator,
> > +             xsdirxss->frame_interval.denominator);
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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 SDI Rx Subsystem.
> > + *
> > + * Return: 0 on success, errors otherwise
> > + */
> > +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     if (enable) {
> > +             if (!xsdirxss->vidlocked) {
> > +                     dev_dbg(core->dev, "Video is not locked\n");
> > +                     return -EINVAL;
> > +             }
> > +             if (xsdirxss->streaming) {
> > +                     dev_dbg(core->dev, "Already streaming\n");
> > +                     return -EINVAL;
> > +             }
> > +
> > +             xsdirxss_start_stream(xsdirxss);
> > +             xsdirxss->streaming = true;
> > +             dev_dbg(core->dev, "Streaming started\n");
> > +     } else {
> > +             if (!xsdirxss->streaming) {
> > +                     dev_dbg(core->dev, "Stopped streaming already\n");
> > +                     return -EINVAL;
> > +             }
> > +
> > +             xsdirxss_stop_stream(xsdirxss);
> > +             xsdirxss->streaming = false;
> > +             dev_dbg(core->dev, "Streaming stopped\n");
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static struct v4l2_mbus_framefmt *
> > +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> > +                       struct v4l2_subdev_pad_config *cfg,
> > +                             unsigned int pad, u32 which)
> > +{
> > +     switch (which) {
> > +     case V4L2_SUBDEV_FORMAT_TRY:
> > +             return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> > +     case V4L2_SUBDEV_FORMAT_ACTIVE:
> > +             return &xsdirxss->formats[pad];
> > +     default:
> > +             return NULL;
> > +     }
> > +}
> > +
> > +/**
> > + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> > +                            struct v4l2_subdev_pad_config *cfg,
> > +                                     struct v4l2_subdev_format *fmt)
> > +{
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +     if (!xsdirxss->vidlocked) {
> > +             dev_err(core->dev, "Video not locked!\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> > +                                              fmt->pad, fmt->which);
> > +
> > +     dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> > +             fmt->format.width, fmt->format.height, fmt->format.field);
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> > +                            struct v4l2_subdev_pad_config *cfg,
> > +                             struct v4l2_subdev_format *fmt)
> > +{
> > +     struct v4l2_mbus_framefmt *__format;
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > +     dev_dbg(xsdirxss->core.dev,
> > +             "set width %d height %d code %d field %d colorspace %d\n",
> > +             fmt->format.width, fmt->format.height,
> > +             fmt->format.code, fmt->format.field,
> > +             fmt->format.colorspace);
> > +
> > +     __format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> > +                                          fmt->pad, fmt->which);
> > +
> > +     /* Currently reset the code to one fixed in hardware */
> > +     /* TODO : Add checks for width height */
> > +     fmt->format.code = __format->code;
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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 pad.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_open(struct v4l2_subdev *sd,
> > +                      struct v4l2_subdev_fh *fh)
> > +{
> > +     struct v4l2_mbus_framefmt *format;
> > +     struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > +     format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> > +     *format = xsdirxss->default_format;
> > +
> > +     return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_close - Called on v4l2_close()
> > + * @sd: Pointer to V4L2 sub device structure
> > + * @fh: Pointer to V4L2 File handle
> > + *
> > + * This function is called on v4l2_close().
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_close(struct v4l2_subdev *sd,
> > +                       struct v4l2_subdev_fh *fh)
> > +{
> > +     return 0;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Media Operations
> > + */
> > +
> > +static const struct media_entity_operations xsdirxss_media_ops = {
> > +     .link_validate = v4l2_subdev_link_validate
> > +};
> > +
> > +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> > +     .g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> > +     .s_ctrl = xsdirxss_s_ctrl
> > +};
> > +
> > +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> > +     {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> > +             .name   = "SDI Rx : EDH Error Count Enable",
> > +             .type   = V4L2_CTRL_TYPE_BITMASK,
> > +             .min    = 0,
> > +             .max    = XSDIRX_EDH_ALLERR_MASK,
> > +             .def    = 0,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> > +             .name   = "SDI Rx : EDH Error Count",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 0,
> > +             .max    = 0xFFFF,
> > +             .step   = 1,
> > +             .def    = 0,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> > +             .name   = "SDI Rx : EDH Status",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 0,
> > +             .max    = 0xFFFFFFFF,
> > +             .step   = 1,
> > +             .def    = 0,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }
> > +};
> > +
> > +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> > +     {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_FRAMER,
> > +             .name   = "SDI Rx : Enable Framer",
> > +             .type   = V4L2_CTRL_TYPE_BOOLEAN,
> > +             .min    = false,
> > +             .max    = true,
> > +             .step   = 1,
> > +             .def    = true,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> > +             .name   = "SDI Rx : Video Lock Window",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 0,
> > +             .max    = 0xFFFFFFFF,
> > +             .step   = 1,
> > +             .def    = XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> > +             .name   = "SDI Rx : Modes search Mask",
> > +             .type   = V4L2_CTRL_TYPE_BITMASK,
> > +             .min    = 0,
> > +             .max    = XSDIRX_DETECT_ALL_MODES,
> > +             .def    = XSDIRX_DETECT_ALL_MODES,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> > +             .name   = "SDI Rx : Mode Detect Status",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = XSDIRX_MODE_SD_OFFSET,
> > +             .max    = XSDIRX_MODE_12GF_OFFSET,
> > +             .step   = 1,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_CRC,
> > +             .name   = "SDI Rx : CRC Error status",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 0,
> > +             .max    = 0xFFFFFFFF,
> > +             .step   = 1,
> > +             .def    = 0,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> > +             .name   = "SDI Rx : TS is Interlaced",
> > +             .type   = V4L2_CTRL_TYPE_BOOLEAN,
> > +             .min    = false,
> > +             .max    = true,
> > +             .def    = false,
> > +             .step   = 1,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> > +             .name   = "SDI Rx : Active Streams",
> > +             .type   = V4L2_CTRL_TYPE_INTEGER,
> > +             .min    = 1,
> > +             .max    = 16,
> > +             .def    = 1,
> > +             .step   = 1,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }, {
> > +             .ops    = &xsdirxss_ctrl_ops,
> > +             .id     = V4L2_CID_XILINX_SDIRX_IS_3GB,
> > +             .name   = "SDI Rx : Is 3GB",
> > +             .type   = V4L2_CTRL_TYPE_BOOLEAN,
> > +             .min    = false,
> > +             .max    = true,
> > +             .def    = false,
> > +             .step   = 1,
> > +             .flags  = V4L2_CTRL_FLAG_VOLATILE |
> V4L2_CTRL_FLAG_READ_ONLY,
> > +     }
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> > +     .log_status = xsdirxss_log_status,
> > +     .subscribe_event = xsdirxss_subscribe_event,
> > +     .unsubscribe_event = xsdirxss_unsubscribe_event
> > +};
> > +
> > +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > +     .g_frame_interval = xsdirxss_g_frame_interval,
> > +     .s_stream = xsdirxss_s_stream
> > +};
> > +
> > +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > +     .get_fmt = xsdirxss_get_format,
> > +     .set_fmt = xsdirxss_set_format,
> > +};
> > +
> > +static struct v4l2_subdev_ops xsdirxss_ops = {
> > +     .core = &xsdirxss_core_ops,
> > +     .video = &xsdirxss_video_ops,
> > +     .pad = &xsdirxss_pad_ops
> > +};
> > +
> > +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> > +     .open = xsdirxss_open,
> > +     .close = xsdirxss_close
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > +{
> > +     struct device_node *node = xsdirxss->core.dev->of_node;
> > +     struct device_node *ports = NULL;
> > +     struct device_node *port = NULL;
> > +     unsigned int nports = 0;
> > +     struct xsdirxss_core *core = &xsdirxss->core;
> > +     int ret;
> > +     const char *sdi_std;
> > +
> > +     core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > +     dev_dbg(core->dev, "EDH property = %s\n",
> > +             core->include_edh ? "Present" : "Absent");
> > +
> > +     ret = of_property_read_string(node, "xlnx,line-rate",
> > +                                   &sdi_std);
> > +     if (ret < 0) {
> > +             dev_err(core->dev, "xlnx,line-rate property not found\n");
> > +             return ret;
> > +     }
> > +
> > +     if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> > +             core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> > +     } else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +             core->mode = XSDIRXSS_SDI_STD_6G;
> > +     } else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +             core->mode = XSDIRXSS_SDI_STD_3G;
> > +     } else {
> > +             dev_err(core->dev, "Invalid Line Rate\n");
> > +             return -EINVAL;
> > +     }
> > +     dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > +             core->mode);
> > +
> > +     ports = of_get_child_by_name(node, "ports");
> > +     if (!ports)
> > +             ports = node;
> > +
> > +     for_each_child_of_node(ports, port) {
> > +             const struct xvip_video_format *format;
> > +             struct device_node *endpoint;
> > +
> > +             if (!port->name || of_node_cmp(port->name, "port"))
> > +                     continue;
> > +
> > +             format = xvip_of_get_format(port);
> > +             if (IS_ERR(format)) {
> > +                     dev_err(core->dev, "invalid format in DT");
> > +                     return PTR_ERR(format);
> > +             }
> > +
> > +             dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> > +                     format->vf_code, format->width, format->bpp);
> > +
> > +             if (format->vf_code != XVIP_VF_YUV_422 &&
> > +                 format->vf_code != XVIP_VF_YUV_420) {
> > +                     dev_err(core->dev, "Incorrect UG934 video format set.\n");
> > +                     return -EINVAL;
> > +             }
> > +             xsdirxss->vip_format = format;
> > +
> > +             endpoint = of_get_next_child(port, NULL);
> > +             if (!endpoint) {
> > +                     dev_err(core->dev, "No port at\n");
> > +                     return -EINVAL;
> > +             }
> > +
> > +             /* Count the number of ports. */
> > +             nports++;
> > +     }
> > +
> > +     if (nports != 1) {
> > +             dev_err(core->dev, "invalid number of ports %u\n", nports);
> > +             return -EINVAL;
> > +     }
> > +
> > +     /* Register interrupt handler */
> > +     core->irq = irq_of_parse_and_map(node, 0);
> > +
> > +     ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> > +                            IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> > +     if (ret) {
> > +             dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> > +                     ret);
> > +             return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int xsdirxss_probe(struct platform_device *pdev)
> > +{
> > +     struct v4l2_subdev *subdev;
> > +     struct xsdirxss_state *xsdirxss;
> > +     struct xsdirxss_core *core;
> > +     struct resource *res;
> > +     int ret;
> > +     unsigned int num_ctrls, num_edh_ctrls = 0, i;
> > +
> > +     xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> > +     if (!xsdirxss)
> > +             return -ENOMEM;
> > +
> > +     xsdirxss->core.dev = &pdev->dev;
> > +     core = &xsdirxss->core;
> > +
> > +     core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> > +     if (IS_ERR(core->axi_clk)) {
> > +             ret = PTR_ERR(core->axi_clk);
> > +             dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> > +     if (IS_ERR(core->sdirx_clk)) {
> > +             ret = PTR_ERR(core->sdirx_clk);
> > +             dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> > +     if (IS_ERR(core->vidout_clk)) {
> > +             ret = PTR_ERR(core->vidout_clk);
> > +             dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = clk_prepare_enable(core->axi_clk);
> > +     if (ret) {
> > +             dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = clk_prepare_enable(core->sdirx_clk);
> > +     if (ret) {
> > +             dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> > +             goto rx_clk_err;
> > +     }
> > +
> > +     ret = clk_prepare_enable(core->vidout_clk);
> > +     if (ret) {
> > +             dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> > +             goto vidout_clk_err;
> > +     }
> > +
> > +     ret = xsdirxss_parse_of(xsdirxss);
> > +     if (ret < 0)
> > +             goto clk_err;
> > +
> > +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +     xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> > +     if (IS_ERR(xsdirxss->core.iomem)) {
> > +             ret = PTR_ERR(xsdirxss->core.iomem);
> > +             goto clk_err;
> > +     }
> > +
> > +     /* Reset the core */
> > +     xsdirx_streamflow_control(core, false);
> > +     xsdirx_core_disable(core);
> > +     xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> > +     xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +     xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > +     xsdirx_globalintr(core, true);
> > +     xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > +
> > +     /* Initialize V4L2 subdevice and media entity */
> > +     xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> > +
> > +     /* Initialize the default format */
> > +     xsdirxss->default_format.code = xsdirxss->vip_format->code;
> > +     xsdirxss->default_format.field = V4L2_FIELD_NONE;
> > +     xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> > +     xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> > +     xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> > +
> > +     xsdirxss->formats[0] = xsdirxss->default_format;
> > +
> > +     /* Initialize V4L2 subdevice and media entity */
> > +     subdev = &xsdirxss->subdev;
> > +     v4l2_subdev_init(subdev, &xsdirxss_ops);
> > +
> > +     subdev->dev = &pdev->dev;
> > +     subdev->internal_ops = &xsdirxss_internal_ops;
> > +     strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
> 
> Use strscpy.
> 

Ok. I will fix this in v2.

> > +
> > +     subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS |
> V4L2_SUBDEV_FL_HAS_DEVNODE;
> > +
> > +     subdev->entity.ops = &xsdirxss_media_ops;
> > +
> > +     v4l2_set_subdevdata(subdev, xsdirxss);
> > +
> > +     ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> > +     if (ret < 0)
> > +             goto error;
> > +
> > +     /* Initialise and register the controls */
> > +     num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > +
> > +     if (xsdirxss->core.include_edh)
> > +             num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> > +
> > +     v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> > +                            (num_ctrls + num_edh_ctrls));
> > +
> > +     for (i = 0; i < num_ctrls; i++) {
> > +             struct v4l2_ctrl *ctrl;
> > +
> > +             dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> > +                     i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> > +
> > +             ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > +                                         &xsdirxss_ctrls[i], NULL);
> > +             if (!ctrl) {
> > +                     dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> > +                             xsdirxss_ctrls[i].name);
> > +                     goto error;
> > +             }
> > +     }
> > +
> > +     if (xsdirxss->core.include_edh) {
> > +             for (i = 0; i < num_edh_ctrls; i++) {
> > +                     struct v4l2_ctrl *ctrl;
> > +
> > +                     dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> > +                             i, xsdirxss_edh_ctrls[i].name,
> > +                             xsdirxss_edh_ctrls[i].id);
> > +
> > +                     ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> > +                                                 &xsdirxss_edh_ctrls[i],
> > +                                                 NULL);
> > +                     if (!ctrl) {
> > +                             dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> > +                                     xsdirxss_edh_ctrls[i].name);
> > +                             goto error;
> > +                     }
> > +             }
> > +     } else {
> > +             dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as
> EDH is disabled in IP\n");
> > +     }
> > +
> > +     if (xsdirxss->ctrl_handler.error) {
> > +             dev_err(&pdev->dev, "failed to add controls\n");
> > +             ret = xsdirxss->ctrl_handler.error;
> > +             goto error;
> > +     }
> > +
> > +     subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> > +
> > +     ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> > +     if (ret < 0) {
> > +             dev_err(&pdev->dev, "failed to set controls\n");
> > +             goto error;
> > +     }
> > +
> > +     platform_set_drvdata(pdev, xsdirxss);
> > +
> > +     ret = v4l2_async_register_subdev(subdev);
> > +     if (ret < 0) {
> > +             dev_err(&pdev->dev, "failed to register subdev\n");
> > +             goto error;
> > +     }
> > +
> > +     xsdirxss->streaming = false;
> > +
> > +     dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> > +
> > +     xsdirx_core_enable(core);
> > +
> > +     return 0;
> > +error:
> > +     v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +     media_entity_cleanup(&subdev->entity);
> > +
> > +clk_err:
> > +     clk_disable_unprepare(core->vidout_clk);
> > +vidout_clk_err:
> > +     clk_disable_unprepare(core->sdirx_clk);
> > +rx_clk_err:
> > +     clk_disable_unprepare(core->axi_clk);
> > +     return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > +     struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > +     struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > +     v4l2_async_unregister_subdev(subdev);
> > +     v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +     media_entity_cleanup(&subdev->entity);
> > +     clk_disable_unprepare(xsdirxss->core.vidout_clk);
> > +     clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> > +     clk_disable_unprepare(xsdirxss->core.axi_clk);
> > +     return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > +     { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> > +     { }
> > +};
> > +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> > +
> > +static struct platform_driver xsdirxss_driver = {
> > +     .driver = {
> > +             .name           = "xilinx-sdirxss",
> > +             .of_match_table = xsdirxss_of_id_table,
> > +     },
> > +     .probe                  = xsdirxss_probe,
> > +     .remove                 = xsdirxss_remove,
> > +};
> > +
> > +module_platform_driver(xsdirxss_driver);
> > +
> > +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> > +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-
> sdirxss.h
> > new file mode 100644
> > index 0000000..983a43b
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,63 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode and flag definitions.
> > + *
> > + * Copyright (C) 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * This enum is used to prepare the bitmask of modes to be detected
> > + */
> > +enum {
> > +     XSDIRX_MODE_SD_OFFSET = 0,
> > +     XSDIRX_MODE_HD_OFFSET,
> > +     XSDIRX_MODE_3G_OFFSET,
> > +     XSDIRX_MODE_6G_OFFSET,
> > +     XSDIRX_MODE_12GI_OFFSET,
> > +     XSDIRX_MODE_12GF_OFFSET,
> > +     XSDIRX_MODE_NUM_SUPPORTED,
> > +};
> > +
> > +#define XSDIRX_DETECT_ALL_MODES
> (BIT(XSDIRX_MODE_SD_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_HD_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_3G_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_6G_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_12GI_OFFSET) | \
> > +                                     BIT(XSDIRX_MODE_12GF_OFFSET))
> > +
> > +/*
> > + * EDH Error Types
> > + * ANC - Ancillary Data Packet Errors
> > + * FF - Full Field Errors
> > + * AP - Active Portion Errors
> > + */
> > +
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR                BIT(0)
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR                BIT(1)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR                BIT(2)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR                BIT(3)
> > +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR                BIT(4)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR         BIT(5)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR         BIT(6)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR         BIT(7)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR         BIT(8)
> > +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR         BIT(9)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR         BIT(10)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR         BIT(11)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR         BIT(12)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR         BIT(13)
> > +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR         BIT(14)
> > +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR     BIT(15)
> > +
> > +#define XSDIRX_EDH_ALLERR_MASK               0xFFFF
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index f023623..4c68a10 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -83,4 +83,34 @@
> >  /* Reset all event counters */
> >  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS
> (V4L2_CID_XILINX_MIPICSISS + 2)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* Base ID */
> > +#define V4L2_CID_XILINX_SDIRX                        (V4L2_CID_USER_BASE +
> 0xc100)
> > +
> > +/* Framer Control */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER         (V4L2_CID_XILINX_SDIRX + 1)
> > +/* Video Lock Window Control */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> (V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> (V4L2_CID_XILINX_SDIRX + 3)
> > +/* Mode search Control */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> (V4L2_CID_XILINX_SDIRX + 4)
> > +/* Get Detected Mode control */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT    (V4L2_CID_XILINX_SDIRX
> + 5)
> > +/* Get CRC error status */
> > +#define V4L2_CID_XILINX_SDIRX_CRC            (V4L2_CID_XILINX_SDIRX + 6)
> > +/* Get EDH error count control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT     (V4L2_CID_XILINX_SDIRX +
> 7)
> > +/* Get EDH status control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS     (V4L2_CID_XILINX_SDIRX +
> 8)
> > +/* Get Transport Interlaced status */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> (V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get Active Streams count */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> (V4L2_CID_XILINX_SDIRX + 10)
> > +/* Is Mode 3GB */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB         (V4L2_CID_XILINX_SDIRX + 11)
> > +
> >  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-
> v4l2-events.h
> > index 2aa357c..7b47595 100644
> > --- a/include/uapi/linux/xilinx-v4l2-events.h
> > +++ b/include/uapi/linux/xilinx-v4l2-events.h
> > @@ -18,4 +18,13 @@
> >  /* Stream Line Buffer full */
> >  #define V4L2_EVENT_XLNXCSIRX_SLBF    (V4L2_EVENT_XLNXCSIRX_CLASS |
> 0x1)
> >
> > +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> > +#define V4L2_EVENT_XLNXSDIRX_CLASS   (V4L2_EVENT_PRIVATE_START |
> 0x200)
> > +/* Video Unlock event */
> > +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK
> (V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> > +/* Video in to AXI4 Stream core underflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW
> (V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> > +/* Video in to AXI4 Stream core overflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW
> (V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> > +
> >  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> >
> 
> I am concerned about this driver: I see that none of the *_dv_timings callbacks
> are implemented. I would expect to see that for a video receiver. There is also
> no g_input_status implemented.
> 
> Take a look at another SDI driver: drivers/media/spi/gs1662.c
> 

I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662 driver
requires the timings because it is a SDI Transmitter. 

Here the timings are not required as the IP block generates a AXI4 Stream.
I think it may be required only in case of native / parallel video being outputted
as the output stream needs timing information to be decoded.

Please feel free to correct my understanding if wrong.

In the current driver, the input stream properties like width, height, frame rate,
progressive/interlaced  are determined from the ST352 packet payload or from the
properties detected by the core.

See the xsdirx_get_stream_properties() for details.

> Some of the controls you add in this driver can likely be dropped. Especially
> those controls that are not specific to the Xilinx implementation but are
> generic for any SDI receiver, should be looked at closely: those are
> candidates for becoming standard controls.

I don't know how other SDI Receiver devices function.
So I am assuming all these controls are Xilinx specific implementations.

> 
> But the documentation above is simply insufficient for me to tell what is
> SDI specific and what is implementation specific.
> 

I will add more documentation for these controls.

> Also, I'm no SDI expert, certainly not for the UHD-SDI.
> 
> Regards,
> 
>         Hans

Regards
Vishal Sagar

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-13 22:05     ` Hyun Kwon
  (?)
@ 2019-06-14 12:12       ` Vishal Sagar
  -1 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-14 12:12 UTC (permalink / raw)
  To: Hyun Kwon
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari,
	Vishal Sagar

Hi Hyun,

Thanks for reviewing the code. 

> -----Original Message-----
> From: Hyun Kwon [mailto:hyun.kwon@xilinx.com]
> Sent: Friday, June 14, 2019 3:35 AM
> To: Vishal Sagar <vishal.sagar@xilinx.com>
> Cc: Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; linux-
> kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> Hi Vishal,
> 
> Thanks for the patch.
> 
> On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> > The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> > streams from SDI sources like SDI broadcast equipment like cameras and
> > mixers. This block outputs either native SDI, native video or
> > AXI4-Stream compliant data stream for further processing. Please refer
> > to PG290 for details.
> >
> > The driver is used to configure the IP to add framer, search for
> > specific modes, get the detected mode, stream parameters, errors, etc.
> > It also generates events for video lock/unlock, bridge over/under flow.
> >
> > The driver supports only 10 bpc YUV 422 media bus format. It also
> > decodes the stream parameters based on the ST352 packet embedded in the
> > stream. In case the ST352 packet isn't present in the stream, the core's
> > detected properties are used to set stream properties.
> >
> > The driver currently supports only the AXI4-Stream configuration.
> >
> > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > ---
> >  drivers/media/platform/xilinx/Kconfig          |   11 +
> >  drivers/media/platform/xilinx/Makefile         |    1 +
> >  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> ++++++++++++++++++++++++
> >  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> >  6 files changed, 1960 insertions(+)
> >  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> >  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index cd1a0fd..0c68caa 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
> >  	  Bridge. The driver is used to set the number of active lanes and
> >  	  get short packet data.
> >
> > +config VIDEO_XILINX_SDIRXSS
> > +	tristate "Xilinx UHD SDI Rx Subsystem"
> > +	help
> > +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> > +	  based driver that takes input from a SDI source like SDI camera and
> > +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> > +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> > +	  AXI4-Stream bridge. The driver is used to set different stream
> > +	  detection modes and identify stream properties to properly configure
> > +	  downstream.
> > +
> >  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 6119a34..223f2ea 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > new file mode 100644
> > index 0000000..ba2d9d0
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,1846 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>

<snip>

> > +/*
> > + * SDI Rx register map, bitmask and offsets
> > + */
> > +#define XSDIRX_RST_CTRL_REG		0x00
> > +#define XSDIRX_MDL_CTRL_REG		0x04
> > +#define XSDIRX_GLBL_IER_REG		0x0C
> > +#define XSDIRX_ISR_REG			0x10
> > +#define XSDIRX_IER_REG			0x14
> > +#define XSDIRX_ST352_VALID_REG		0x18
> > +#define XSDIRX_ST352_DS1_REG		0x1C
> > +#define XSDIRX_ST352_DS3_REG		0x20
> > +#define XSDIRX_ST352_DS5_REG		0x24
> > +#define XSDIRX_ST352_DS7_REG		0x28
> > +#define XSDIRX_ST352_DS9_REG		0x2C
> > +#define XSDIRX_ST352_DS11_REG		0x30
> > +#define XSDIRX_ST352_DS13_REG		0x34
> > +#define XSDIRX_ST352_DS15_REG		0x38
> > +#define XSDIRX_VERSION_REG		0x3C
> > +#define XSDIRX_SS_CONFIG_REG		0x40
> > +#define XSDIRX_MODE_DET_STAT_REG	0x44
> > +#define XSDIRX_TS_DET_STAT_REG		0x48
> > +#define XSDIRX_EDH_STAT_REG		0x4C
> > +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> > +#define XSDIRX_EDH_ERRCNT_REG		0x54
> > +#define XSDIRX_CRC_ERRCNT_REG		0x58
> > +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> > +#define XSDIRX_SB_RX_STS_REG		0x60
> > +
> > +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> > +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> > +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> > +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> > +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> > +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
> 
> I'd rather put these under corresponding offsets. In that way, it'd be easier
> to follow. Up to you.
> 

I would like to keep this as is.

> > +
> > +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> > +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> > +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> > +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> > +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> > +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> > +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> > +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
> 
> These are not actually mask, so I'd remove _MASK. But it maybe just me.
> 

I would like to keep this as is.

> > +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> > +

<snip>

> > +#define XSDIRX_MODE_12GI_MASK	0x5
> > +#define XSDIRX_MODE_12GF_MASK	0x6
> > +
> > +/*
> > + * Maximum number of events per file handle.
> > + */
> 
> Nit, This doesn't have to be multieline comment.
> 

I will make this a single line comment in v2.

> > +#define XSDIRX_MAX_EVENTS	(128)
> > +
> > +/* ST352 related macros */

<snip>

> > +struct xsdirxss_core {
> > +	struct device *dev;
> > +	void __iomem *iomem;
> > +	int irq;
> > +	bool include_edh;
> 
> I wonder if this is required. I guess it's not critical anyway, so up to you.
> 

I would keep this as it is as it’s a IP configuration information.

> > +	int mode;
> > +	struct clk *axi_clk;
> > +	struct clk *sdirx_clk;
> > +	struct clk *vidout_clk;
> > +};
> > +
> > +/**
> > + * struct xsdirxss_state - SDI Rx Subsystem device structure
> > + * @core: Core structure for MIPI SDI Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @event: Holds the video unlock event
> > + * @formats: Active V4L2 formats on each pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @vip_format: format information corresponding to the active format
> > + * @pads: media pads
> > + * @streaming: Flag for storing streaming state
> > + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> > + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xsdirxss_state {
> > +	struct xsdirxss_core core;
> > +	struct v4l2_subdev subdev;
> > +	struct v4l2_ctrl_handler ctrl_handler;
> > +	struct v4l2_event event;
> > +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> 
> It's always single port, so this doesn't have to be an array.
> 

I will fix this in v2.

> > +	struct v4l2_mbus_framefmt default_format;
> > +	struct v4l2_fract frame_interval;
> > +	const struct xvip_video_format *vip_format;
> > +	struct media_pad pads[XSDIRX_MEDIA_PADS];
> 
> Ditto.
> 

I will fix this in v2.

> > +	bool streaming;
> > +	bool vidlocked;
> > +	bool ts_is_interlaced;
> > +};
> > +

<snip>

> > +
> > +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> > +
> > +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> > +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> > +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> > +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> 
> () is not needed here. I'd avoid unneeded ().
> 

I will fix this in v2.

> > +
> > +	if (hweight16(mask) > 1) {

<snip>

> > +	} else {
> > +		/* Fixed Mode */
> > +		u32 forced_mode_mask = 0;
> 
> Setting this under default below would make it clearer. But up to you.
> 

I will fix this in v2.

> > +
> > +		dev_dbg(core->dev, "Detect fixed mode\n");
> > +

<snip>

> > +
> > +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> > +{
> > +	if (flag)
> 
> The flag can be determined at build time, so this 'if' isn't needed. Then
> the disabling call can be added to xsdirxss_remove().
> 

I will try to take care of this in v2.

> > +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> > +			     XSDIRX_GLBL_INTR_EN_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> > +			     XSDIRX_GLBL_INTR_EN_MASK);
> > +}
> > +
> > +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > +}
> > +
> > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	if (enable)
> > +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> 
> Even these can be inlined. I don't see need for having dynamic check.
> 
> > +}
> > +
> > +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	if (enable)
> > +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> 
> Ditto.
> 
> > +}
> > +
> > +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	/* The sdi to native bridge is followed by native to axis4 bridge */
> > +	if (enable) {
> > +		xsdirx_axis4_bridge_control(core, enable);
> > +		xsdirx_vid_bridge_control(core, enable);
> > +	} else {
> > +		xsdirx_vid_bridge_control(core, enable);
> > +		xsdirx_axis4_bridge_control(core, enable);
> > +	}
> > +}
> > +
> > +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> > +{
> > +	xsdirx_streamflow_control(core, false);
> > +}
> 
> These thin wrapers don't seem useful. Wouldn't it be better to inline most of
> these?
> 

I will inline these functions in v2.

> > +
> > +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> > +				   u32 framerate)

<snip>

> > +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > +				XST352_PAYLOAD_BYTE_MASK;
> > +		active_luma = (payload &
> XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > +				XST352_BYTE2_PIC_TYPE_OFFSET;
> > +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > +				XST352_BYTE2_FPS_MASK;
> > +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > +				XST352_BYTE2_TS_TYPE_OFFSET;
> 
> Please align consistently throughout the patch. I believe the checkpatch
> --strict warns on these.
> 

Checkpatch didn't warn about these.

> > +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK)
> >>
> > +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> > +	} else {
> > +		dev_dbg(core->dev, "No ST352 payload available : Mode =
> %d\n",
> > +			mode);
> > +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> > +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> > +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> > +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> > +	}
> > +
> > +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> > +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> 
> Just nit. Alignment.

Ok I will fix this in v2.

> 
> > +	state->ts_is_interlaced = tscan ? false : true;
> > +
> > +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> > +		state->ts_is_interlaced, family);
> > +
> > +	switch (mode) {
> > +	case XSDIRX_MODE_HD_MASK:
> > +		if (!valid) {
> > +			/* No payload obtained */
> > +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> > +				framerate, tscan);
> > +			/*
> > +			 * NOTE : A progressive segmented frame pSF will be
> > +			 * reported incorrectly as Interlaced as we rely on IP's
> > +			 * transport scan locked bit.
> > +			 */
> > +			dev_warn(core->dev, "pSF will be incorrectly reported as
> Interlaced\n");
> > +
> > +			switch (framerate) {
> > +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > +				if (family == XSDIRX_SMPTE_ST_296) {
> > +					format->width = 1280;
> > +					format->height = 720;
> > +					format->field = V4L2_FIELD_NONE;
> > +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> > +					format->width = 2048;
> > +					format->height = 1080;
> > +					if (tscan)
> > +						format->field =
> V4L2_FIELD_NONE;
> > +					else
> > +						format->field =
> > +							V4L2_FIELD_ALTERNATE;
> > +				} else {
> > +					format->width = 1920;
> > +					format->height = 1080;
> > +					if (tscan)
> > +						format->field =
> V4L2_FIELD_NONE;
> > +					else
> > +						format->field =
> > +							V4L2_FIELD_ALTERNATE;
> > +				}
> > +				break;
> > +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > +				if (family == XSDIRX_SMPTE_ST_274) {
> > +					format->width = 1920;
> > +					format->height = 1080;
> > +				} else {
> > +					format->width = 1280;
> > +					format->height = 720;
> > +				}
> > +				format->field = V4L2_FIELD_NONE;
> > +				break;
> > +			default:
> > +				format->width = 1920;
> > +				format->height = 1080;
> > +				format->field = V4L2_FIELD_NONE;
> > +			}
> > +		} else {
> > +			dev_dbg(core->dev, "Got the payload\n");
> > +			switch (byte1) {
> > +			case XST352_BYTE1_ST292_1x720L_1_5G:
> > +				/* SMPTE ST 292-1 for 720 line payloads */
> > +				format->width = 1280;
> > +				format->height = 720;
> > +				break;
> > +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> > +				/* SMPTE ST 292-1 for 1080 line payloads */
> > +				format->height = 1080;
> > +				if (active_luma)
> > +					format->width = 2048;
> > +				else
> > +					format->width = 1920;
> > +				break;
> > +			default:
> > +				dev_dbg(core->dev, "Unknown HD Mode SMPTE
> standard\n");
> > +				return -EINVAL;
> > +			}
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_SD_MASK:
> > +		format->field = V4L2_FIELD_ALTERNATE;
> > +
> > +		switch (family) {
> > +		case XSDIRX_NTSC:
> > +			format->width = 720;
> > +			format->height = 486;
> > +			break;
> > +		case XSDIRX_PAL:
> > +			format->width = 720;
> > +			format->height = 576;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown SD Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_3G_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST425_2008_750L_3GB:
> > +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> > +		case XST352_BYTE1_ST372_2x720L_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +			format->width = 1280;
> > +			format->height = 720;
> > +			break;
> > +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> > +			/* ST352 Table SMPTE 425-1 */
> > +		case XST352_BYTE1_ST372_DL_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +		case XST352_BYTE1_ST372_2x1080L_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +			format->height = 1080;
> > +			if (active_luma)
> > +				format->width = 2048;
> > +			else
> > +				format->width = 1920;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_6G_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> > +			/* Dual link 6G */
> > +		case XST352_BYTE1_ST2081_10_2160L_6G:
> > +			/* Table 3 SMPTE ST 2081-10 */
> > +			format->height = 2160;
> > +			if (active_luma)
> > +				format->width = 4096;
> > +			else
> > +				format->width = 3840;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_12GI_MASK:
> > +	case XSDIRX_MODE_12GF_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST2082_10_2160L_12G:
> > +			/* Section 4.3.1 SMPTE ST 2082-10 */
> > +			format->height = 2160;
> > +			if (active_luma)
> > +				format->width = 4096;
> > +			else
> > +				format->width = 3840;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	default:
> > +		dev_err(core->dev, "Invalid Mode\n");
> > +		return -EINVAL;
> > +	}
> 
> Could you please take another look if this can be organized simpler? This is
> a little too many switch - case and if statements.
> 

I am unable to come up with a better way. :(
I would appreciate if anyone can help regarding this or if this needs more documentation
to understand easily.

> > +
> > +	if (valid) {
> > +		if (pic_type)
> > +			format->field = V4L2_FIELD_NONE;
> > +		else
> > +			format->field = V4L2_FIELD_ALTERNATE;
> > +	}
> > +

<snip>

> > +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +	int ret = 0;
> > +	struct xsdirxss_state *xsdirxss =
> > +		container_of(ctrl->handler,
> 
> This doesn't have to be a new line.
> 

Ok but keeping container_of on same line makes the below line go beyond 80 char limit.
So keeping it as it is.

> > +			     struct xsdirxss_state, ctrl_handler);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > +		ctrl->id, ctrl->val);
> > +
> > +	if (xsdirxss->streaming) {
> > +		dev_err(core->dev, "Cannot set controls while streaming\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	xsdirx_core_disable(core);
> 
> Not sure why this is needed here. Can this be part of s_stream?
> 

The core is enabled so that the SDI Rx can at least decode the stream type into
Width,height, frame interval and progressive/interlace type. The subsystem starts
To send out data only when the SDI to Video and Video to AXI4 Stream bridges are enabled.

The bridges are enabled in s_stream().

This is getting disabled and enabled in the s_ctrl so that the core properties can
Be modified.

> > +	switch (ctrl->id) {
> > +	case V4L2_CID_XILINX_SDIRX_FRAMER:

<snip>

> > +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> > +{
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +	u32 data, i;
> 
> 'data' can be declared inside the for loop below.
> 

Ok I will fix this in v2.

> > +
> > +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > +	for (i = 0; i < 0x28; i++) {
> > +		data = xsdirxss_read(core, i * 4);
> > +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> > +			  i * 4, data);
> > +	}
> > +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> > +	return 0;
> > +}
> > +
> > +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +	xsdirx_streamflow_control(&xsdirxss->core, true);
> > +}
> > +
> > +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +	xsdirx_streamflow_control(&xsdirxss->core, false);
> > +}
> 
> Ditto. Not sure if these single liner wrappers are useful.
> 

I had kept them for readability purpose. But ok I will remove these in v2.

> > +
> > +/**
> > + * xsdirxss_g_frame_interval - Get the frame interval

<snip>

> > + */
> > +static int xsdirxss_get_format(struct v4l2_subdev *sd,
> > +			       struct v4l2_subdev_pad_config *cfg,
> > +					struct v4l2_subdev_format *fmt)
> 
> Please check the alignment.
> 

I will fix this in v2.

> > +{
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +	if (!xsdirxss->vidlocked) {
> > +		dev_err(core->dev, "Video not locked!\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> > +						 fmt->pad, fmt->which);
> > +
> > +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> > +		fmt->format.width, fmt->format.height, fmt->format.field);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> > +			       struct v4l2_subdev_pad_config *cfg,
> > +				struct v4l2_subdev_format *fmt)
> 
> Please check the alignment.
> 

I will fix this in v2.

> > +{
> > +	struct v4l2_mbus_framefmt *__format;
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > +	dev_dbg(xsdirxss->core.dev,
> > +		"set width %d height %d code %d field %d colorspace %d\n",
> > +		fmt->format.width, fmt->format.height,
> > +		fmt->format.code, fmt->format.field,
> > +		fmt->format.colorspace);
> > +
> > +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,

<snip>

> > +
> > +	ret = of_property_read_string(node, "xlnx,line-rate",
> > +				      &sdi_std);
> 
> Single line is enough.
> 

Ok I will get it to a single line in v2.

> > +	if (ret < 0) {
> > +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> > +		return ret;
> > +	}
> > +
> > +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> > +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_6G;
> > +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_3G;
> > +	} else {
> > +		dev_err(core->dev, "Invalid Line Rate\n");
> > +		return -EINVAL;
> > +	}
> > +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > +		core->mode);
> > +
> > +	ports = of_get_child_by_name(node, "ports");
> > +	if (!ports)
> > +		ports = node;
> > +
> > +	for_each_child_of_node(ports, port) {
> > +		const struct xvip_video_format *format;
> > +		struct device_node *endpoint;
> > +
> > +		if (!port->name || of_node_cmp(port->name, "port"))
> > +			continue;
> > +
> > +		format = xvip_of_get_format(port);
> > +		if (IS_ERR(format)) {
> > +			dev_err(core->dev, "invalid format in DT");
> > +			return PTR_ERR(format);
> > +		}
> > +
> > +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> > +			format->vf_code, format->width, format->bpp);
> 
> The commit mentions this driver supports only 10bpc. Wouldn't it be better to
> check it here?
> 

Yes I will add a check for width to be 10 here. 

> > +
> > +		if (format->vf_code != XVIP_VF_YUV_422 &&
> > +		    format->vf_code != XVIP_VF_YUV_420) {
> > +			dev_err(core->dev, "Incorrect UG934 video format
> set.\n");
> 
> You can make this single line as below,
> 
> 	dev_err(core->dev,
> 		"Incorrect UG934 video format set.\n");
> 

Ok will fix this in v2.

> > +			return -EINVAL;
> > +		}
> > +		xsdirxss->vip_format = format;
> > +
> > +		endpoint = of_get_next_child(port, NULL);
> > +		if (!endpoint) {
> > +			dev_err(core->dev, "No port at\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		/* Count the number of ports. */
> > +		nports++;
> > +	}
> > +
> > +	if (nports != 1) {
> > +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* Register interrupt handler */
> > +	core->irq = irq_of_parse_and_map(node, 0);
> > +
> > +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> > +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> > +	if (ret) {
> > +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> > +			ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +

<snip>

> > +			}
> > +		}
> > +	} else {
> > +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls
> as EDH is disabled in IP\n");
> 
> Is this worth being here? There's already an equivalent print for dt is parsed.
> 

Agree. I will remove this in v2.

> > +	}
> > +
> > +	if (xsdirxss->ctrl_handler.error) {
> > +		dev_err(&pdev->dev, "failed to add controls\n");
> > +		ret = xsdirxss->ctrl_handler.error;
> > +		goto error;
> > +	}
> > +
> > +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> > +
> > +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to set controls\n");
> > +		goto error;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, xsdirxss);
> > +
> > +	ret = v4l2_async_register_subdev(subdev);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to register subdev\n");
> > +		goto error;
> > +	}
> > +
> > +	xsdirxss->streaming = false;
> > +
> > +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> > +
> > +	xsdirx_core_enable(core);
> > +
> > +	return 0;
> > +error:
> > +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +	media_entity_cleanup(&subdev->entity);
> > +
> > +clk_err:
> > +	clk_disable_unprepare(core->vidout_clk);
> > +vidout_clk_err:
> > +	clk_disable_unprepare(core->sdirx_clk);
> > +rx_clk_err:
> > +	clk_disable_unprepare(core->axi_clk);
> > +	return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > +	v4l2_async_unregister_subdev(subdev);
> > +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +	media_entity_cleanup(&subdev->entity);
> > +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> > +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> > +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> > +
> > +static struct platform_driver xsdirxss_driver = {
> > +	.driver = {
> > +		.name		= "xilinx-sdirxss",
> > +		.of_match_table	= xsdirxss_of_id_table,
> > +	},
> > +	.probe			= xsdirxss_probe,
> > +	.remove			= xsdirxss_remove,
> > +};
> > +
> > +module_platform_driver(xsdirxss_driver);
> > +
> > +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> > +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-
> sdirxss.h
> > new file mode 100644
> > index 0000000..983a43b
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,63 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode and flag definitions.
> > + *
> > + * Copyright (C) 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * This enum is used to prepare the bitmask of modes to be detected
> > + */
> > +enum {
> > +	XSDIRX_MODE_SD_OFFSET = 0,
> > +	XSDIRX_MODE_HD_OFFSET,
> > +	XSDIRX_MODE_3G_OFFSET,
> > +	XSDIRX_MODE_6G_OFFSET,
> > +	XSDIRX_MODE_12GI_OFFSET,
> > +	XSDIRX_MODE_12GF_OFFSET,
> > +	XSDIRX_MODE_NUM_SUPPORTED,
> > +};
> > +
> > +#define XSDIRX_DETECT_ALL_MODES
> 	(BIT(XSDIRX_MODE_SD_OFFSET) | \
> > +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> > +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> > +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> > +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> > +					BIT(XSDIRX_MODE_12GF_OFFSET))
> > +
> > +/*
> > + * EDH Error Types
> > + * ANC - Ancillary Data Packet Errors
> > + * FF - Full Field Errors
> > + * AP - Active Portion Errors
> > + */
> > +
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> > +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> > +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> > +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> > +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> > +
> > +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index f023623..4c68a10 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -83,4 +83,34 @@
> >  /* Reset all event counters */
> >  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS
> (V4L2_CID_XILINX_MIPICSISS + 2)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* Base ID */
> > +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE +
> 0xc100)
> > +
> > +/* Framer Control */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX
> + 1)
> > +/* Video Lock Window Control */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> 	(V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> 	(V4L2_CID_XILINX_SDIRX + 3)
> > +/* Mode search Control */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX
> + 4)
> > +/* Get Detected Mode control */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX
> + 5)
> > +/* Get CRC error status */
> > +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX
> + 6)
> > +/* Get EDH error count control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX
> + 7)
> > +/* Get EDH status control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX
> + 8)
> > +/* Get Transport Interlaced status */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> 	(V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get Active Streams count */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> 	(V4L2_CID_XILINX_SDIRX + 10)
> > +/* Is Mode 3GB */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX
> + 11)
> > +
> >  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-
> v4l2-events.h
> > index 2aa357c..7b47595 100644
> > --- a/include/uapi/linux/xilinx-v4l2-events.h
> > +++ b/include/uapi/linux/xilinx-v4l2-events.h
> > @@ -18,4 +18,13 @@
> >  /* Stream Line Buffer full */
> >  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS
> | 0x1)
> >
> > +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> > +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START |
> 0x200)
> > +/* Video Unlock event */
> > +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> > +/* Video in to AXI4 Stream core underflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> > +/* Video in to AXI4 Stream core overflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> 
> Could you please double-check if all events / controls are hard-required? More
> of these will make generic v4l applications harder to run on this driver. It'd
> be nicer if some of these (ex, from sdi spec) can be shared for any sdi if
> possible?
> 

I will document these controls and events in detail in v2.
I think these are needed as they are specific to Xilinx implementation.

Regards
Vishal Sagar

> Thanks,
> -hyun
> 
> > +
> >  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> > --
> > 1.8.3.1
> >
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-14 12:12       ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-14 12:12 UTC (permalink / raw)
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari,
	Vishal Sagar

Hi Hyun,

Thanks for reviewing the code. 

> -----Original Message-----
> From: Hyun Kwon [mailto:hyun.kwon@xilinx.com]
> Sent: Friday, June 14, 2019 3:35 AM
> To: Vishal Sagar <vishal.sagar@xilinx.com>
> Cc: Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; linux-
> kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> Hi Vishal,
> 
> Thanks for the patch.
> 
> On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> > The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> > streams from SDI sources like SDI broadcast equipment like cameras and
> > mixers. This block outputs either native SDI, native video or
> > AXI4-Stream compliant data stream for further processing. Please refer
> > to PG290 for details.
> >
> > The driver is used to configure the IP to add framer, search for
> > specific modes, get the detected mode, stream parameters, errors, etc.
> > It also generates events for video lock/unlock, bridge over/under flow.
> >
> > The driver supports only 10 bpc YUV 422 media bus format. It also
> > decodes the stream parameters based on the ST352 packet embedded in the
> > stream. In case the ST352 packet isn't present in the stream, the core's
> > detected properties are used to set stream properties.
> >
> > The driver currently supports only the AXI4-Stream configuration.
> >
> > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > ---
> >  drivers/media/platform/xilinx/Kconfig          |   11 +
> >  drivers/media/platform/xilinx/Makefile         |    1 +
> >  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> ++++++++++++++++++++++++
> >  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> >  6 files changed, 1960 insertions(+)
> >  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> >  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index cd1a0fd..0c68caa 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
> >  	  Bridge. The driver is used to set the number of active lanes and
> >  	  get short packet data.
> >
> > +config VIDEO_XILINX_SDIRXSS
> > +	tristate "Xilinx UHD SDI Rx Subsystem"
> > +	help
> > +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> > +	  based driver that takes input from a SDI source like SDI camera and
> > +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> > +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> > +	  AXI4-Stream bridge. The driver is used to set different stream
> > +	  detection modes and identify stream properties to properly configure
> > +	  downstream.
> > +
> >  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 6119a34..223f2ea 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > new file mode 100644
> > index 0000000..ba2d9d0
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,1846 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>

<snip>

> > +/*
> > + * SDI Rx register map, bitmask and offsets
> > + */
> > +#define XSDIRX_RST_CTRL_REG		0x00
> > +#define XSDIRX_MDL_CTRL_REG		0x04
> > +#define XSDIRX_GLBL_IER_REG		0x0C
> > +#define XSDIRX_ISR_REG			0x10
> > +#define XSDIRX_IER_REG			0x14
> > +#define XSDIRX_ST352_VALID_REG		0x18
> > +#define XSDIRX_ST352_DS1_REG		0x1C
> > +#define XSDIRX_ST352_DS3_REG		0x20
> > +#define XSDIRX_ST352_DS5_REG		0x24
> > +#define XSDIRX_ST352_DS7_REG		0x28
> > +#define XSDIRX_ST352_DS9_REG		0x2C
> > +#define XSDIRX_ST352_DS11_REG		0x30
> > +#define XSDIRX_ST352_DS13_REG		0x34
> > +#define XSDIRX_ST352_DS15_REG		0x38
> > +#define XSDIRX_VERSION_REG		0x3C
> > +#define XSDIRX_SS_CONFIG_REG		0x40
> > +#define XSDIRX_MODE_DET_STAT_REG	0x44
> > +#define XSDIRX_TS_DET_STAT_REG		0x48
> > +#define XSDIRX_EDH_STAT_REG		0x4C
> > +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> > +#define XSDIRX_EDH_ERRCNT_REG		0x54
> > +#define XSDIRX_CRC_ERRCNT_REG		0x58
> > +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> > +#define XSDIRX_SB_RX_STS_REG		0x60
> > +
> > +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> > +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> > +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> > +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> > +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> > +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
> 
> I'd rather put these under corresponding offsets. In that way, it'd be easier
> to follow. Up to you.
> 

I would like to keep this as is.

> > +
> > +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> > +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> > +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> > +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> > +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> > +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> > +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> > +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
> 
> These are not actually mask, so I'd remove _MASK. But it maybe just me.
> 

I would like to keep this as is.

> > +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> > +

<snip>

> > +#define XSDIRX_MODE_12GI_MASK	0x5
> > +#define XSDIRX_MODE_12GF_MASK	0x6
> > +
> > +/*
> > + * Maximum number of events per file handle.
> > + */
> 
> Nit, This doesn't have to be multieline comment.
> 

I will make this a single line comment in v2.

> > +#define XSDIRX_MAX_EVENTS	(128)
> > +
> > +/* ST352 related macros */

<snip>

> > +struct xsdirxss_core {
> > +	struct device *dev;
> > +	void __iomem *iomem;
> > +	int irq;
> > +	bool include_edh;
> 
> I wonder if this is required. I guess it's not critical anyway, so up to you.
> 

I would keep this as it is as it’s a IP configuration information.

> > +	int mode;
> > +	struct clk *axi_clk;
> > +	struct clk *sdirx_clk;
> > +	struct clk *vidout_clk;
> > +};
> > +
> > +/**
> > + * struct xsdirxss_state - SDI Rx Subsystem device structure
> > + * @core: Core structure for MIPI SDI Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @event: Holds the video unlock event
> > + * @formats: Active V4L2 formats on each pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @vip_format: format information corresponding to the active format
> > + * @pads: media pads
> > + * @streaming: Flag for storing streaming state
> > + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> > + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xsdirxss_state {
> > +	struct xsdirxss_core core;
> > +	struct v4l2_subdev subdev;
> > +	struct v4l2_ctrl_handler ctrl_handler;
> > +	struct v4l2_event event;
> > +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> 
> It's always single port, so this doesn't have to be an array.
> 

I will fix this in v2.

> > +	struct v4l2_mbus_framefmt default_format;
> > +	struct v4l2_fract frame_interval;
> > +	const struct xvip_video_format *vip_format;
> > +	struct media_pad pads[XSDIRX_MEDIA_PADS];
> 
> Ditto.
> 

I will fix this in v2.

> > +	bool streaming;
> > +	bool vidlocked;
> > +	bool ts_is_interlaced;
> > +};
> > +

<snip>

> > +
> > +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> > +
> > +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> > +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> > +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> > +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> 
> () is not needed here. I'd avoid unneeded ().
> 

I will fix this in v2.

> > +
> > +	if (hweight16(mask) > 1) {

<snip>

> > +	} else {
> > +		/* Fixed Mode */
> > +		u32 forced_mode_mask = 0;
> 
> Setting this under default below would make it clearer. But up to you.
> 

I will fix this in v2.

> > +
> > +		dev_dbg(core->dev, "Detect fixed mode\n");
> > +

<snip>

> > +
> > +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> > +{
> > +	if (flag)
> 
> The flag can be determined at build time, so this 'if' isn't needed. Then
> the disabling call can be added to xsdirxss_remove().
> 

I will try to take care of this in v2.

> > +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> > +			     XSDIRX_GLBL_INTR_EN_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> > +			     XSDIRX_GLBL_INTR_EN_MASK);
> > +}
> > +
> > +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > +}
> > +
> > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	if (enable)
> > +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> 
> Even these can be inlined. I don't see need for having dynamic check.
> 
> > +}
> > +
> > +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	if (enable)
> > +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> 
> Ditto.
> 
> > +}
> > +
> > +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	/* The sdi to native bridge is followed by native to axis4 bridge */
> > +	if (enable) {
> > +		xsdirx_axis4_bridge_control(core, enable);
> > +		xsdirx_vid_bridge_control(core, enable);
> > +	} else {
> > +		xsdirx_vid_bridge_control(core, enable);
> > +		xsdirx_axis4_bridge_control(core, enable);
> > +	}
> > +}
> > +
> > +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> > +{
> > +	xsdirx_streamflow_control(core, false);
> > +}
> 
> These thin wrapers don't seem useful. Wouldn't it be better to inline most of
> these?
> 

I will inline these functions in v2.

> > +
> > +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> > +				   u32 framerate)

<snip>

> > +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > +				XST352_PAYLOAD_BYTE_MASK;
> > +		active_luma = (payload &
> XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > +				XST352_BYTE2_PIC_TYPE_OFFSET;
> > +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > +				XST352_BYTE2_FPS_MASK;
> > +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > +				XST352_BYTE2_TS_TYPE_OFFSET;
> 
> Please align consistently throughout the patch. I believe the checkpatch
> --strict warns on these.
> 

Checkpatch didn't warn about these.

> > +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK)
> >>
> > +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> > +	} else {
> > +		dev_dbg(core->dev, "No ST352 payload available : Mode =
> %d\n",
> > +			mode);
> > +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> > +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> > +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> > +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> > +	}
> > +
> > +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> > +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> 
> Just nit. Alignment.

Ok I will fix this in v2.

> 
> > +	state->ts_is_interlaced = tscan ? false : true;
> > +
> > +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> > +		state->ts_is_interlaced, family);
> > +
> > +	switch (mode) {
> > +	case XSDIRX_MODE_HD_MASK:
> > +		if (!valid) {
> > +			/* No payload obtained */
> > +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> > +				framerate, tscan);
> > +			/*
> > +			 * NOTE : A progressive segmented frame pSF will be
> > +			 * reported incorrectly as Interlaced as we rely on IP's
> > +			 * transport scan locked bit.
> > +			 */
> > +			dev_warn(core->dev, "pSF will be incorrectly reported as
> Interlaced\n");
> > +
> > +			switch (framerate) {
> > +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > +				if (family == XSDIRX_SMPTE_ST_296) {
> > +					format->width = 1280;
> > +					format->height = 720;
> > +					format->field = V4L2_FIELD_NONE;
> > +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> > +					format->width = 2048;
> > +					format->height = 1080;
> > +					if (tscan)
> > +						format->field =
> V4L2_FIELD_NONE;
> > +					else
> > +						format->field =
> > +							V4L2_FIELD_ALTERNATE;
> > +				} else {
> > +					format->width = 1920;
> > +					format->height = 1080;
> > +					if (tscan)
> > +						format->field =
> V4L2_FIELD_NONE;
> > +					else
> > +						format->field =
> > +							V4L2_FIELD_ALTERNATE;
> > +				}
> > +				break;
> > +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > +				if (family == XSDIRX_SMPTE_ST_274) {
> > +					format->width = 1920;
> > +					format->height = 1080;
> > +				} else {
> > +					format->width = 1280;
> > +					format->height = 720;
> > +				}
> > +				format->field = V4L2_FIELD_NONE;
> > +				break;
> > +			default:
> > +				format->width = 1920;
> > +				format->height = 1080;
> > +				format->field = V4L2_FIELD_NONE;
> > +			}
> > +		} else {
> > +			dev_dbg(core->dev, "Got the payload\n");
> > +			switch (byte1) {
> > +			case XST352_BYTE1_ST292_1x720L_1_5G:
> > +				/* SMPTE ST 292-1 for 720 line payloads */
> > +				format->width = 1280;
> > +				format->height = 720;
> > +				break;
> > +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> > +				/* SMPTE ST 292-1 for 1080 line payloads */
> > +				format->height = 1080;
> > +				if (active_luma)
> > +					format->width = 2048;
> > +				else
> > +					format->width = 1920;
> > +				break;
> > +			default:
> > +				dev_dbg(core->dev, "Unknown HD Mode SMPTE
> standard\n");
> > +				return -EINVAL;
> > +			}
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_SD_MASK:
> > +		format->field = V4L2_FIELD_ALTERNATE;
> > +
> > +		switch (family) {
> > +		case XSDIRX_NTSC:
> > +			format->width = 720;
> > +			format->height = 486;
> > +			break;
> > +		case XSDIRX_PAL:
> > +			format->width = 720;
> > +			format->height = 576;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown SD Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_3G_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST425_2008_750L_3GB:
> > +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> > +		case XST352_BYTE1_ST372_2x720L_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +			format->width = 1280;
> > +			format->height = 720;
> > +			break;
> > +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> > +			/* ST352 Table SMPTE 425-1 */
> > +		case XST352_BYTE1_ST372_DL_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +		case XST352_BYTE1_ST372_2x1080L_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +			format->height = 1080;
> > +			if (active_luma)
> > +				format->width = 2048;
> > +			else
> > +				format->width = 1920;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_6G_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> > +			/* Dual link 6G */
> > +		case XST352_BYTE1_ST2081_10_2160L_6G:
> > +			/* Table 3 SMPTE ST 2081-10 */
> > +			format->height = 2160;
> > +			if (active_luma)
> > +				format->width = 4096;
> > +			else
> > +				format->width = 3840;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_12GI_MASK:
> > +	case XSDIRX_MODE_12GF_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST2082_10_2160L_12G:
> > +			/* Section 4.3.1 SMPTE ST 2082-10 */
> > +			format->height = 2160;
> > +			if (active_luma)
> > +				format->width = 4096;
> > +			else
> > +				format->width = 3840;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	default:
> > +		dev_err(core->dev, "Invalid Mode\n");
> > +		return -EINVAL;
> > +	}
> 
> Could you please take another look if this can be organized simpler? This is
> a little too many switch - case and if statements.
> 

I am unable to come up with a better way. :(
I would appreciate if anyone can help regarding this or if this needs more documentation
to understand easily.

> > +
> > +	if (valid) {
> > +		if (pic_type)
> > +			format->field = V4L2_FIELD_NONE;
> > +		else
> > +			format->field = V4L2_FIELD_ALTERNATE;
> > +	}
> > +

<snip>

> > +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +	int ret = 0;
> > +	struct xsdirxss_state *xsdirxss =
> > +		container_of(ctrl->handler,
> 
> This doesn't have to be a new line.
> 

Ok but keeping container_of on same line makes the below line go beyond 80 char limit.
So keeping it as it is.

> > +			     struct xsdirxss_state, ctrl_handler);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > +		ctrl->id, ctrl->val);
> > +
> > +	if (xsdirxss->streaming) {
> > +		dev_err(core->dev, "Cannot set controls while streaming\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	xsdirx_core_disable(core);
> 
> Not sure why this is needed here. Can this be part of s_stream?
> 

The core is enabled so that the SDI Rx can at least decode the stream type into
Width,height, frame interval and progressive/interlace type. The subsystem starts
To send out data only when the SDI to Video and Video to AXI4 Stream bridges are enabled.

The bridges are enabled in s_stream().

This is getting disabled and enabled in the s_ctrl so that the core properties can
Be modified.

> > +	switch (ctrl->id) {
> > +	case V4L2_CID_XILINX_SDIRX_FRAMER:

<snip>

> > +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> > +{
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +	u32 data, i;
> 
> 'data' can be declared inside the for loop below.
> 

Ok I will fix this in v2.

> > +
> > +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > +	for (i = 0; i < 0x28; i++) {
> > +		data = xsdirxss_read(core, i * 4);
> > +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> > +			  i * 4, data);
> > +	}
> > +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> > +	return 0;
> > +}
> > +
> > +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +	xsdirx_streamflow_control(&xsdirxss->core, true);
> > +}
> > +
> > +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +	xsdirx_streamflow_control(&xsdirxss->core, false);
> > +}
> 
> Ditto. Not sure if these single liner wrappers are useful.
> 

I had kept them for readability purpose. But ok I will remove these in v2.

> > +
> > +/**
> > + * xsdirxss_g_frame_interval - Get the frame interval

<snip>

> > + */
> > +static int xsdirxss_get_format(struct v4l2_subdev *sd,
> > +			       struct v4l2_subdev_pad_config *cfg,
> > +					struct v4l2_subdev_format *fmt)
> 
> Please check the alignment.
> 

I will fix this in v2.

> > +{
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +	if (!xsdirxss->vidlocked) {
> > +		dev_err(core->dev, "Video not locked!\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> > +						 fmt->pad, fmt->which);
> > +
> > +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> > +		fmt->format.width, fmt->format.height, fmt->format.field);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> > +			       struct v4l2_subdev_pad_config *cfg,
> > +				struct v4l2_subdev_format *fmt)
> 
> Please check the alignment.
> 

I will fix this in v2.

> > +{
> > +	struct v4l2_mbus_framefmt *__format;
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > +	dev_dbg(xsdirxss->core.dev,
> > +		"set width %d height %d code %d field %d colorspace %d\n",
> > +		fmt->format.width, fmt->format.height,
> > +		fmt->format.code, fmt->format.field,
> > +		fmt->format.colorspace);
> > +
> > +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,

<snip>

> > +
> > +	ret = of_property_read_string(node, "xlnx,line-rate",
> > +				      &sdi_std);
> 
> Single line is enough.
> 

Ok I will get it to a single line in v2.

> > +	if (ret < 0) {
> > +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> > +		return ret;
> > +	}
> > +
> > +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> > +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_6G;
> > +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_3G;
> > +	} else {
> > +		dev_err(core->dev, "Invalid Line Rate\n");
> > +		return -EINVAL;
> > +	}
> > +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > +		core->mode);
> > +
> > +	ports = of_get_child_by_name(node, "ports");
> > +	if (!ports)
> > +		ports = node;
> > +
> > +	for_each_child_of_node(ports, port) {
> > +		const struct xvip_video_format *format;
> > +		struct device_node *endpoint;
> > +
> > +		if (!port->name || of_node_cmp(port->name, "port"))
> > +			continue;
> > +
> > +		format = xvip_of_get_format(port);
> > +		if (IS_ERR(format)) {
> > +			dev_err(core->dev, "invalid format in DT");
> > +			return PTR_ERR(format);
> > +		}
> > +
> > +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> > +			format->vf_code, format->width, format->bpp);
> 
> The commit mentions this driver supports only 10bpc. Wouldn't it be better to
> check it here?
> 

Yes I will add a check for width to be 10 here. 

> > +
> > +		if (format->vf_code != XVIP_VF_YUV_422 &&
> > +		    format->vf_code != XVIP_VF_YUV_420) {
> > +			dev_err(core->dev, "Incorrect UG934 video format
> set.\n");
> 
> You can make this single line as below,
> 
> 	dev_err(core->dev,
> 		"Incorrect UG934 video format set.\n");
> 

Ok will fix this in v2.

> > +			return -EINVAL;
> > +		}
> > +		xsdirxss->vip_format = format;
> > +
> > +		endpoint = of_get_next_child(port, NULL);
> > +		if (!endpoint) {
> > +			dev_err(core->dev, "No port at\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		/* Count the number of ports. */
> > +		nports++;
> > +	}
> > +
> > +	if (nports != 1) {
> > +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* Register interrupt handler */
> > +	core->irq = irq_of_parse_and_map(node, 0);
> > +
> > +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> > +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> > +	if (ret) {
> > +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> > +			ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +

<snip>

> > +			}
> > +		}
> > +	} else {
> > +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls
> as EDH is disabled in IP\n");
> 
> Is this worth being here? There's already an equivalent print for dt is parsed.
> 

Agree. I will remove this in v2.

> > +	}
> > +
> > +	if (xsdirxss->ctrl_handler.error) {
> > +		dev_err(&pdev->dev, "failed to add controls\n");
> > +		ret = xsdirxss->ctrl_handler.error;
> > +		goto error;
> > +	}
> > +
> > +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> > +
> > +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to set controls\n");
> > +		goto error;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, xsdirxss);
> > +
> > +	ret = v4l2_async_register_subdev(subdev);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to register subdev\n");
> > +		goto error;
> > +	}
> > +
> > +	xsdirxss->streaming = false;
> > +
> > +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> > +
> > +	xsdirx_core_enable(core);
> > +
> > +	return 0;
> > +error:
> > +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +	media_entity_cleanup(&subdev->entity);
> > +
> > +clk_err:
> > +	clk_disable_unprepare(core->vidout_clk);
> > +vidout_clk_err:
> > +	clk_disable_unprepare(core->sdirx_clk);
> > +rx_clk_err:
> > +	clk_disable_unprepare(core->axi_clk);
> > +	return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > +	v4l2_async_unregister_subdev(subdev);
> > +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +	media_entity_cleanup(&subdev->entity);
> > +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> > +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> > +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> > +
> > +static struct platform_driver xsdirxss_driver = {
> > +	.driver = {
> > +		.name		= "xilinx-sdirxss",
> > +		.of_match_table	= xsdirxss_of_id_table,
> > +	},
> > +	.probe			= xsdirxss_probe,
> > +	.remove			= xsdirxss_remove,
> > +};
> > +
> > +module_platform_driver(xsdirxss_driver);
> > +
> > +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> > +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-
> sdirxss.h
> > new file mode 100644
> > index 0000000..983a43b
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,63 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode and flag definitions.
> > + *
> > + * Copyright (C) 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * This enum is used to prepare the bitmask of modes to be detected
> > + */
> > +enum {
> > +	XSDIRX_MODE_SD_OFFSET = 0,
> > +	XSDIRX_MODE_HD_OFFSET,
> > +	XSDIRX_MODE_3G_OFFSET,
> > +	XSDIRX_MODE_6G_OFFSET,
> > +	XSDIRX_MODE_12GI_OFFSET,
> > +	XSDIRX_MODE_12GF_OFFSET,
> > +	XSDIRX_MODE_NUM_SUPPORTED,
> > +};
> > +
> > +#define XSDIRX_DETECT_ALL_MODES
> 	(BIT(XSDIRX_MODE_SD_OFFSET) | \
> > +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> > +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> > +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> > +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> > +					BIT(XSDIRX_MODE_12GF_OFFSET))
> > +
> > +/*
> > + * EDH Error Types
> > + * ANC - Ancillary Data Packet Errors
> > + * FF - Full Field Errors
> > + * AP - Active Portion Errors
> > + */
> > +
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> > +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> > +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> > +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> > +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> > +
> > +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index f023623..4c68a10 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -83,4 +83,34 @@
> >  /* Reset all event counters */
> >  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS
> (V4L2_CID_XILINX_MIPICSISS + 2)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* Base ID */
> > +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE +
> 0xc100)
> > +
> > +/* Framer Control */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX
> + 1)
> > +/* Video Lock Window Control */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> 	(V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> 	(V4L2_CID_XILINX_SDIRX + 3)
> > +/* Mode search Control */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX
> + 4)
> > +/* Get Detected Mode control */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX
> + 5)
> > +/* Get CRC error status */
> > +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX
> + 6)
> > +/* Get EDH error count control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX
> + 7)
> > +/* Get EDH status control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX
> + 8)
> > +/* Get Transport Interlaced status */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> 	(V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get Active Streams count */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> 	(V4L2_CID_XILINX_SDIRX + 10)
> > +/* Is Mode 3GB */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX
> + 11)
> > +
> >  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-
> v4l2-events.h
> > index 2aa357c..7b47595 100644
> > --- a/include/uapi/linux/xilinx-v4l2-events.h
> > +++ b/include/uapi/linux/xilinx-v4l2-events.h
> > @@ -18,4 +18,13 @@
> >  /* Stream Line Buffer full */
> >  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS
> | 0x1)
> >
> > +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> > +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START |
> 0x200)
> > +/* Video Unlock event */
> > +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> > +/* Video in to AXI4 Stream core underflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> > +/* Video in to AXI4 Stream core overflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> 
> Could you please double-check if all events / controls are hard-required? More
> of these will make generic v4l applications harder to run on this driver. It'd
> be nicer if some of these (ex, from sdi spec) can be shared for any sdi if
> possible?
> 

I will document these controls and events in detail in v2.
I think these are needed as they are specific to Xilinx implementation.

Regards
Vishal Sagar

> Thanks,
> -hyun
> 
> > +
> >  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> > --
> > 1.8.3.1
> >
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-14 12:12       ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-14 12:12 UTC (permalink / raw)
  To: Hyun Kwon
  Cc: Mark Rutland, devicetree, Dinesh Kumar, Hyun Kwon,
	Sandip Kothari, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Vishal Sagar, Mauro Carvalho Chehab,
	linux-arm-kernel, linux-media

Hi Hyun,

Thanks for reviewing the code. 

> -----Original Message-----
> From: Hyun Kwon [mailto:hyun.kwon@xilinx.com]
> Sent: Friday, June 14, 2019 3:35 AM
> To: Vishal Sagar <vishal.sagar@xilinx.com>
> Cc: Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; linux-
> kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> Hi Vishal,
> 
> Thanks for the patch.
> 
> On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> > The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> > streams from SDI sources like SDI broadcast equipment like cameras and
> > mixers. This block outputs either native SDI, native video or
> > AXI4-Stream compliant data stream for further processing. Please refer
> > to PG290 for details.
> >
> > The driver is used to configure the IP to add framer, search for
> > specific modes, get the detected mode, stream parameters, errors, etc.
> > It also generates events for video lock/unlock, bridge over/under flow.
> >
> > The driver supports only 10 bpc YUV 422 media bus format. It also
> > decodes the stream parameters based on the ST352 packet embedded in the
> > stream. In case the ST352 packet isn't present in the stream, the core's
> > detected properties are used to set stream properties.
> >
> > The driver currently supports only the AXI4-Stream configuration.
> >
> > Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> > ---
> >  drivers/media/platform/xilinx/Kconfig          |   11 +
> >  drivers/media/platform/xilinx/Makefile         |    1 +
> >  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> ++++++++++++++++++++++++
> >  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> >  6 files changed, 1960 insertions(+)
> >  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> >  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> >
> > diff --git a/drivers/media/platform/xilinx/Kconfig
> b/drivers/media/platform/xilinx/Kconfig
> > index cd1a0fd..0c68caa 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
> >  	  Bridge. The driver is used to set the number of active lanes and
> >  	  get short packet data.
> >
> > +config VIDEO_XILINX_SDIRXSS
> > +	tristate "Xilinx UHD SDI Rx Subsystem"
> > +	help
> > +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> > +	  based driver that takes input from a SDI source like SDI camera and
> > +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> > +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> > +	  AXI4-Stream bridge. The driver is used to set different stream
> > +	  detection modes and identify stream properties to properly configure
> > +	  downstream.
> > +
> >  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 6119a34..223f2ea 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > new file mode 100644
> > index 0000000..ba2d9d0
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,1846 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>

<snip>

> > +/*
> > + * SDI Rx register map, bitmask and offsets
> > + */
> > +#define XSDIRX_RST_CTRL_REG		0x00
> > +#define XSDIRX_MDL_CTRL_REG		0x04
> > +#define XSDIRX_GLBL_IER_REG		0x0C
> > +#define XSDIRX_ISR_REG			0x10
> > +#define XSDIRX_IER_REG			0x14
> > +#define XSDIRX_ST352_VALID_REG		0x18
> > +#define XSDIRX_ST352_DS1_REG		0x1C
> > +#define XSDIRX_ST352_DS3_REG		0x20
> > +#define XSDIRX_ST352_DS5_REG		0x24
> > +#define XSDIRX_ST352_DS7_REG		0x28
> > +#define XSDIRX_ST352_DS9_REG		0x2C
> > +#define XSDIRX_ST352_DS11_REG		0x30
> > +#define XSDIRX_ST352_DS13_REG		0x34
> > +#define XSDIRX_ST352_DS15_REG		0x38
> > +#define XSDIRX_VERSION_REG		0x3C
> > +#define XSDIRX_SS_CONFIG_REG		0x40
> > +#define XSDIRX_MODE_DET_STAT_REG	0x44
> > +#define XSDIRX_TS_DET_STAT_REG		0x48
> > +#define XSDIRX_EDH_STAT_REG		0x4C
> > +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> > +#define XSDIRX_EDH_ERRCNT_REG		0x54
> > +#define XSDIRX_CRC_ERRCNT_REG		0x58
> > +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> > +#define XSDIRX_SB_RX_STS_REG		0x60
> > +
> > +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> > +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> > +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> > +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> > +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> > +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
> 
> I'd rather put these under corresponding offsets. In that way, it'd be easier
> to follow. Up to you.
> 

I would like to keep this as is.

> > +
> > +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> > +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> > +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> > +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> > +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> > +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> > +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> > +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
> 
> These are not actually mask, so I'd remove _MASK. But it maybe just me.
> 

I would like to keep this as is.

> > +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> > +

<snip>

> > +#define XSDIRX_MODE_12GI_MASK	0x5
> > +#define XSDIRX_MODE_12GF_MASK	0x6
> > +
> > +/*
> > + * Maximum number of events per file handle.
> > + */
> 
> Nit, This doesn't have to be multieline comment.
> 

I will make this a single line comment in v2.

> > +#define XSDIRX_MAX_EVENTS	(128)
> > +
> > +/* ST352 related macros */

<snip>

> > +struct xsdirxss_core {
> > +	struct device *dev;
> > +	void __iomem *iomem;
> > +	int irq;
> > +	bool include_edh;
> 
> I wonder if this is required. I guess it's not critical anyway, so up to you.
> 

I would keep this as it is as it’s a IP configuration information.

> > +	int mode;
> > +	struct clk *axi_clk;
> > +	struct clk *sdirx_clk;
> > +	struct clk *vidout_clk;
> > +};
> > +
> > +/**
> > + * struct xsdirxss_state - SDI Rx Subsystem device structure
> > + * @core: Core structure for MIPI SDI Rx Subsystem
> > + * @subdev: The v4l2 subdev structure
> > + * @ctrl_handler: control handler
> > + * @event: Holds the video unlock event
> > + * @formats: Active V4L2 formats on each pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @vip_format: format information corresponding to the active format
> > + * @pads: media pads
> > + * @streaming: Flag for storing streaming state
> > + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> > + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> > + *
> > + * This structure contains the device driver related parameters
> > + */
> > +struct xsdirxss_state {
> > +	struct xsdirxss_core core;
> > +	struct v4l2_subdev subdev;
> > +	struct v4l2_ctrl_handler ctrl_handler;
> > +	struct v4l2_event event;
> > +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> 
> It's always single port, so this doesn't have to be an array.
> 

I will fix this in v2.

> > +	struct v4l2_mbus_framefmt default_format;
> > +	struct v4l2_fract frame_interval;
> > +	const struct xvip_video_format *vip_format;
> > +	struct media_pad pads[XSDIRX_MEDIA_PADS];
> 
> Ditto.
> 

I will fix this in v2.

> > +	bool streaming;
> > +	bool vidlocked;
> > +	bool ts_is_interlaced;
> > +};
> > +

<snip>

> > +
> > +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> > +
> > +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> > +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> > +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> > +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> 
> () is not needed here. I'd avoid unneeded ().
> 

I will fix this in v2.

> > +
> > +	if (hweight16(mask) > 1) {

<snip>

> > +	} else {
> > +		/* Fixed Mode */
> > +		u32 forced_mode_mask = 0;
> 
> Setting this under default below would make it clearer. But up to you.
> 

I will fix this in v2.

> > +
> > +		dev_dbg(core->dev, "Detect fixed mode\n");
> > +

<snip>

> > +
> > +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> > +{
> > +	if (flag)
> 
> The flag can be determined at build time, so this 'if' isn't needed. Then
> the disabling call can be added to xsdirxss_remove().
> 

I will try to take care of this in v2.

> > +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> > +			     XSDIRX_GLBL_INTR_EN_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> > +			     XSDIRX_GLBL_INTR_EN_MASK);
> > +}
> > +
> > +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > +}
> > +
> > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	if (enable)
> > +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> 
> Even these can be inlined. I don't see need for having dynamic check.
> 
> > +}
> > +
> > +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	if (enable)
> > +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> > +	else
> > +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> 
> Ditto.
> 
> > +}
> > +
> > +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool
> enable)
> > +{
> > +	/* The sdi to native bridge is followed by native to axis4 bridge */
> > +	if (enable) {
> > +		xsdirx_axis4_bridge_control(core, enable);
> > +		xsdirx_vid_bridge_control(core, enable);
> > +	} else {
> > +		xsdirx_vid_bridge_control(core, enable);
> > +		xsdirx_axis4_bridge_control(core, enable);
> > +	}
> > +}
> > +
> > +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> > +{
> > +	xsdirx_streamflow_control(core, false);
> > +}
> 
> These thin wrapers don't seem useful. Wouldn't it be better to inline most of
> these?
> 

I will inline these functions in v2.

> > +
> > +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> > +				   u32 framerate)

<snip>

> > +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> > +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > +				XST352_PAYLOAD_BYTE_MASK;
> > +		active_luma = (payload &
> XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > +				XST352_BYTE2_PIC_TYPE_OFFSET;
> > +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > +				XST352_BYTE2_FPS_MASK;
> > +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > +				XST352_BYTE2_TS_TYPE_OFFSET;
> 
> Please align consistently throughout the patch. I believe the checkpatch
> --strict warns on these.
> 

Checkpatch didn't warn about these.

> > +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK)
> >>
> > +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> > +	} else {
> > +		dev_dbg(core->dev, "No ST352 payload available : Mode =
> %d\n",
> > +			mode);
> > +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> > +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> > +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> > +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> > +	}
> > +
> > +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> > +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> 
> Just nit. Alignment.

Ok I will fix this in v2.

> 
> > +	state->ts_is_interlaced = tscan ? false : true;
> > +
> > +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> > +		state->ts_is_interlaced, family);
> > +
> > +	switch (mode) {
> > +	case XSDIRX_MODE_HD_MASK:
> > +		if (!valid) {
> > +			/* No payload obtained */
> > +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> > +				framerate, tscan);
> > +			/*
> > +			 * NOTE : A progressive segmented frame pSF will be
> > +			 * reported incorrectly as Interlaced as we rely on IP's
> > +			 * transport scan locked bit.
> > +			 */
> > +			dev_warn(core->dev, "pSF will be incorrectly reported as
> Interlaced\n");
> > +
> > +			switch (framerate) {
> > +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> > +				if (family == XSDIRX_SMPTE_ST_296) {
> > +					format->width = 1280;
> > +					format->height = 720;
> > +					format->field = V4L2_FIELD_NONE;
> > +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> > +					format->width = 2048;
> > +					format->height = 1080;
> > +					if (tscan)
> > +						format->field =
> V4L2_FIELD_NONE;
> > +					else
> > +						format->field =
> > +							V4L2_FIELD_ALTERNATE;
> > +				} else {
> > +					format->width = 1920;
> > +					format->height = 1080;
> > +					if (tscan)
> > +						format->field =
> V4L2_FIELD_NONE;
> > +					else
> > +						format->field =
> > +							V4L2_FIELD_ALTERNATE;
> > +				}
> > +				break;
> > +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> > +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> > +				if (family == XSDIRX_SMPTE_ST_274) {
> > +					format->width = 1920;
> > +					format->height = 1080;
> > +				} else {
> > +					format->width = 1280;
> > +					format->height = 720;
> > +				}
> > +				format->field = V4L2_FIELD_NONE;
> > +				break;
> > +			default:
> > +				format->width = 1920;
> > +				format->height = 1080;
> > +				format->field = V4L2_FIELD_NONE;
> > +			}
> > +		} else {
> > +			dev_dbg(core->dev, "Got the payload\n");
> > +			switch (byte1) {
> > +			case XST352_BYTE1_ST292_1x720L_1_5G:
> > +				/* SMPTE ST 292-1 for 720 line payloads */
> > +				format->width = 1280;
> > +				format->height = 720;
> > +				break;
> > +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> > +				/* SMPTE ST 292-1 for 1080 line payloads */
> > +				format->height = 1080;
> > +				if (active_luma)
> > +					format->width = 2048;
> > +				else
> > +					format->width = 1920;
> > +				break;
> > +			default:
> > +				dev_dbg(core->dev, "Unknown HD Mode SMPTE
> standard\n");
> > +				return -EINVAL;
> > +			}
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_SD_MASK:
> > +		format->field = V4L2_FIELD_ALTERNATE;
> > +
> > +		switch (family) {
> > +		case XSDIRX_NTSC:
> > +			format->width = 720;
> > +			format->height = 486;
> > +			break;
> > +		case XSDIRX_PAL:
> > +			format->width = 720;
> > +			format->height = 576;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown SD Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_3G_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST425_2008_750L_3GB:
> > +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> > +		case XST352_BYTE1_ST372_2x720L_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +			format->width = 1280;
> > +			format->height = 720;
> > +			break;
> > +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> > +			/* ST352 Table SMPTE 425-1 */
> > +		case XST352_BYTE1_ST372_DL_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +		case XST352_BYTE1_ST372_2x1080L_3GB:
> > +			/* Table 13 SMPTE 425-2008 */
> > +			format->height = 1080;
> > +			if (active_luma)
> > +				format->width = 2048;
> > +			else
> > +				format->width = 1920;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_6G_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> > +			/* Dual link 6G */
> > +		case XST352_BYTE1_ST2081_10_2160L_6G:
> > +			/* Table 3 SMPTE ST 2081-10 */
> > +			format->height = 2160;
> > +			if (active_luma)
> > +				format->width = 4096;
> > +			else
> > +				format->width = 3840;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	case XSDIRX_MODE_12GI_MASK:
> > +	case XSDIRX_MODE_12GF_MASK:
> > +		switch (byte1) {
> > +		case XST352_BYTE1_ST2082_10_2160L_12G:
> > +			/* Section 4.3.1 SMPTE ST 2082-10 */
> > +			format->height = 2160;
> > +			if (active_luma)
> > +				format->width = 4096;
> > +			else
> > +				format->width = 3840;
> > +			break;
> > +		default:
> > +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE
> standard\n");
> > +			return -EINVAL;
> > +		}
> > +		break;
> > +	default:
> > +		dev_err(core->dev, "Invalid Mode\n");
> > +		return -EINVAL;
> > +	}
> 
> Could you please take another look if this can be organized simpler? This is
> a little too many switch - case and if statements.
> 

I am unable to come up with a better way. :(
I would appreciate if anyone can help regarding this or if this needs more documentation
to understand easily.

> > +
> > +	if (valid) {
> > +		if (pic_type)
> > +			format->field = V4L2_FIELD_NONE;
> > +		else
> > +			format->field = V4L2_FIELD_ALTERNATE;
> > +	}
> > +

<snip>

> > +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > +	int ret = 0;
> > +	struct xsdirxss_state *xsdirxss =
> > +		container_of(ctrl->handler,
> 
> This doesn't have to be a new line.
> 

Ok but keeping container_of on same line makes the below line go beyond 80 char limit.
So keeping it as it is.

> > +			     struct xsdirxss_state, ctrl_handler);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > +		ctrl->id, ctrl->val);
> > +
> > +	if (xsdirxss->streaming) {
> > +		dev_err(core->dev, "Cannot set controls while streaming\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	xsdirx_core_disable(core);
> 
> Not sure why this is needed here. Can this be part of s_stream?
> 

The core is enabled so that the SDI Rx can at least decode the stream type into
Width,height, frame interval and progressive/interlace type. The subsystem starts
To send out data only when the SDI to Video and Video to AXI4 Stream bridges are enabled.

The bridges are enabled in s_stream().

This is getting disabled and enabled in the s_ctrl so that the core properties can
Be modified.

> > +	switch (ctrl->id) {
> > +	case V4L2_CID_XILINX_SDIRX_FRAMER:

<snip>

> > +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> > +{
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +	u32 data, i;
> 
> 'data' can be declared inside the for loop below.
> 

Ok I will fix this in v2.

> > +
> > +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > +	for (i = 0; i < 0x28; i++) {
> > +		data = xsdirxss_read(core, i * 4);
> > +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> > +			  i * 4, data);
> > +	}
> > +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> > +	return 0;
> > +}
> > +
> > +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +	xsdirx_streamflow_control(&xsdirxss->core, true);
> > +}
> > +
> > +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> > +{
> > +	xsdirx_streamflow_control(&xsdirxss->core, false);
> > +}
> 
> Ditto. Not sure if these single liner wrappers are useful.
> 

I had kept them for readability purpose. But ok I will remove these in v2.

> > +
> > +/**
> > + * xsdirxss_g_frame_interval - Get the frame interval

<snip>

> > + */
> > +static int xsdirxss_get_format(struct v4l2_subdev *sd,
> > +			       struct v4l2_subdev_pad_config *cfg,
> > +					struct v4l2_subdev_format *fmt)
> 
> Please check the alignment.
> 

I will fix this in v2.

> > +{
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +	struct xsdirxss_core *core = &xsdirxss->core;
> > +
> > +	if (!xsdirxss->vidlocked) {
> > +		dev_err(core->dev, "Video not locked!\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> > +						 fmt->pad, fmt->which);
> > +
> > +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> > +		fmt->format.width, fmt->format.height, fmt->format.field);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_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.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> > +			       struct v4l2_subdev_pad_config *cfg,
> > +				struct v4l2_subdev_format *fmt)
> 
> Please check the alignment.
> 

I will fix this in v2.

> > +{
> > +	struct v4l2_mbus_framefmt *__format;
> > +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > +	dev_dbg(xsdirxss->core.dev,
> > +		"set width %d height %d code %d field %d colorspace %d\n",
> > +		fmt->format.width, fmt->format.height,
> > +		fmt->format.code, fmt->format.field,
> > +		fmt->format.colorspace);
> > +
> > +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,

<snip>

> > +
> > +	ret = of_property_read_string(node, "xlnx,line-rate",
> > +				      &sdi_std);
> 
> Single line is enough.
> 

Ok I will get it to a single line in v2.

> > +	if (ret < 0) {
> > +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> > +		return ret;
> > +	}
> > +
> > +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> > +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_6G;
> > +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> > +		core->mode = XSDIRXSS_SDI_STD_3G;
> > +	} else {
> > +		dev_err(core->dev, "Invalid Line Rate\n");
> > +		return -EINVAL;
> > +	}
> > +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > +		core->mode);
> > +
> > +	ports = of_get_child_by_name(node, "ports");
> > +	if (!ports)
> > +		ports = node;
> > +
> > +	for_each_child_of_node(ports, port) {
> > +		const struct xvip_video_format *format;
> > +		struct device_node *endpoint;
> > +
> > +		if (!port->name || of_node_cmp(port->name, "port"))
> > +			continue;
> > +
> > +		format = xvip_of_get_format(port);
> > +		if (IS_ERR(format)) {
> > +			dev_err(core->dev, "invalid format in DT");
> > +			return PTR_ERR(format);
> > +		}
> > +
> > +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> > +			format->vf_code, format->width, format->bpp);
> 
> The commit mentions this driver supports only 10bpc. Wouldn't it be better to
> check it here?
> 

Yes I will add a check for width to be 10 here. 

> > +
> > +		if (format->vf_code != XVIP_VF_YUV_422 &&
> > +		    format->vf_code != XVIP_VF_YUV_420) {
> > +			dev_err(core->dev, "Incorrect UG934 video format
> set.\n");
> 
> You can make this single line as below,
> 
> 	dev_err(core->dev,
> 		"Incorrect UG934 video format set.\n");
> 

Ok will fix this in v2.

> > +			return -EINVAL;
> > +		}
> > +		xsdirxss->vip_format = format;
> > +
> > +		endpoint = of_get_next_child(port, NULL);
> > +		if (!endpoint) {
> > +			dev_err(core->dev, "No port at\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		/* Count the number of ports. */
> > +		nports++;
> > +	}
> > +
> > +	if (nports != 1) {
> > +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* Register interrupt handler */
> > +	core->irq = irq_of_parse_and_map(node, 0);
> > +
> > +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> > +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> > +	if (ret) {
> > +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> > +			ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +

<snip>

> > +			}
> > +		}
> > +	} else {
> > +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls
> as EDH is disabled in IP\n");
> 
> Is this worth being here? There's already an equivalent print for dt is parsed.
> 

Agree. I will remove this in v2.

> > +	}
> > +
> > +	if (xsdirxss->ctrl_handler.error) {
> > +		dev_err(&pdev->dev, "failed to add controls\n");
> > +		ret = xsdirxss->ctrl_handler.error;
> > +		goto error;
> > +	}
> > +
> > +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> > +
> > +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to set controls\n");
> > +		goto error;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, xsdirxss);
> > +
> > +	ret = v4l2_async_register_subdev(subdev);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to register subdev\n");
> > +		goto error;
> > +	}
> > +
> > +	xsdirxss->streaming = false;
> > +
> > +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> > +
> > +	xsdirx_core_enable(core);
> > +
> > +	return 0;
> > +error:
> > +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +	media_entity_cleanup(&subdev->entity);
> > +
> > +clk_err:
> > +	clk_disable_unprepare(core->vidout_clk);
> > +vidout_clk_err:
> > +	clk_disable_unprepare(core->sdirx_clk);
> > +rx_clk_err:
> > +	clk_disable_unprepare(core->axi_clk);
> > +	return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > +	v4l2_async_unregister_subdev(subdev);
> > +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > +	media_entity_cleanup(&subdev->entity);
> > +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> > +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> > +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> > +
> > +static struct platform_driver xsdirxss_driver = {
> > +	.driver = {
> > +		.name		= "xilinx-sdirxss",
> > +		.of_match_table	= xsdirxss_of_id_table,
> > +	},
> > +	.probe			= xsdirxss_probe,
> > +	.remove			= xsdirxss_remove,
> > +};
> > +
> > +module_platform_driver(xsdirxss_driver);
> > +
> > +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> > +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-
> sdirxss.h
> > new file mode 100644
> > index 0000000..983a43b
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,63 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode and flag definitions.
> > + *
> > + * Copyright (C) 2019 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> > + *
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * This enum is used to prepare the bitmask of modes to be detected
> > + */
> > +enum {
> > +	XSDIRX_MODE_SD_OFFSET = 0,
> > +	XSDIRX_MODE_HD_OFFSET,
> > +	XSDIRX_MODE_3G_OFFSET,
> > +	XSDIRX_MODE_6G_OFFSET,
> > +	XSDIRX_MODE_12GI_OFFSET,
> > +	XSDIRX_MODE_12GF_OFFSET,
> > +	XSDIRX_MODE_NUM_SUPPORTED,
> > +};
> > +
> > +#define XSDIRX_DETECT_ALL_MODES
> 	(BIT(XSDIRX_MODE_SD_OFFSET) | \
> > +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> > +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> > +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> > +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> > +					BIT(XSDIRX_MODE_12GF_OFFSET))
> > +
> > +/*
> > + * EDH Error Types
> > + * ANC - Ancillary Data Packet Errors
> > + * FF - Full Field Errors
> > + * AP - Active Portion Errors
> > + */
> > +
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> > +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> > +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> > +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> > +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> > +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> > +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> > +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> > +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> > +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> > +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> > +
> > +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index f023623..4c68a10 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -83,4 +83,34 @@
> >  /* Reset all event counters */
> >  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS
> (V4L2_CID_XILINX_MIPICSISS + 2)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* Base ID */
> > +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE +
> 0xc100)
> > +
> > +/* Framer Control */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX
> + 1)
> > +/* Video Lock Window Control */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> 	(V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> 	(V4L2_CID_XILINX_SDIRX + 3)
> > +/* Mode search Control */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX
> + 4)
> > +/* Get Detected Mode control */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX
> + 5)
> > +/* Get CRC error status */
> > +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX
> + 6)
> > +/* Get EDH error count control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX
> + 7)
> > +/* Get EDH status control */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX
> + 8)
> > +/* Get Transport Interlaced status */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> 	(V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get Active Streams count */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> 	(V4L2_CID_XILINX_SDIRX + 10)
> > +/* Is Mode 3GB */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX
> + 11)
> > +
> >  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-
> v4l2-events.h
> > index 2aa357c..7b47595 100644
> > --- a/include/uapi/linux/xilinx-v4l2-events.h
> > +++ b/include/uapi/linux/xilinx-v4l2-events.h
> > @@ -18,4 +18,13 @@
> >  /* Stream Line Buffer full */
> >  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS
> | 0x1)
> >
> > +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> > +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START |
> 0x200)
> > +/* Video Unlock event */
> > +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> > +/* Video in to AXI4 Stream core underflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> > +/* Video in to AXI4 Stream core overflowed */
> > +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW
> 	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> 
> Could you please double-check if all events / controls are hard-required? More
> of these will make generic v4l applications harder to run on this driver. It'd
> be nicer if some of these (ex, from sdi spec) can be shared for any sdi if
> possible?
> 

I will document these controls and events in detail in v2.
I think these are needed as they are specific to Xilinx implementation.

Regards
Vishal Sagar

> Thanks,
> -hyun
> 
> > +
> >  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> > --
> > 1.8.3.1
> >
> >
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-13 22:31       ` Joe Perches
  (?)
@ 2019-06-14 12:15         ` Vishal Sagar
  -1 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-14 12:15 UTC (permalink / raw)
  To: Joe Perches, Hyun Kwon, Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari

Hi Joe,

Thanks for reviewing. 

> -----Original Message-----
> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
> owner@vger.kernel.org] On Behalf Of Joe Perches
> Sent: Friday, June 14, 2019 4:02 AM
> To: Hyun Kwon <hyunk@xilinx.com>; Vishal Sagar <vishal.sagar@xilinx.com>
> Cc: Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; linux-
> kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> EXTERNAL EMAIL
> 
> On Thu, 2019-06-13 at 15:05 -0700, Hyun Kwon wrote:
> > On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> 
> trivia:
> 
> > > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> []
> > > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > > +{
> []
> > > +   if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > > +           payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > > +           byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > > +                           XST352_PAYLOAD_BYTE_MASK;
> 
> Is XST352_PAYLOAD_BYTE_MASK correct ?
> Should it be XST352_PAYLOAD_BYTE1_MASK ?
> 

I had thought of it to be a generic mask to extract a byte out of 4 bytes in a ST352 packet.
Hence named it as XST352_PAYLOAD_BYTE_MASK

> > > +           active_luma = (payload &
> XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > > +                           XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > > +           pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > > +                           XST352_BYTE2_PIC_TYPE_OFFSET;
> > > +           framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > > +                           XST352_BYTE2_FPS_MASK;
> > > +           tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > > +                           XST352_BYTE2_TS_TYPE_OFFSET;
> >
> > Please align consistently throughout the patch. I believe the checkpatch
> > --strict warns on these.
> 
> I believe not.
> 
> Another possibility would be to use a macro like:
> 
> #define mask_and_shift(val, type)       \
>         ((val) & (XST352_ ## type ## _MASK)) >> (XST352_ ## type ## _OFFSET))
> 
> > > +           sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> > > +                      XST352_BYTE3_COLOR_FORMAT_OFFSET;
> 
> So these could be something like:
> 
>                 sampling = mask_and_shift(payload, BYTE3_COLOR_FORMAT);
> 

This looks like a good way. I will modify this in v2. 
I will also modify the XST352_PAYLOAD_BYTE_MASK to 
XST352_PAYLOAD_BYTE1_MASK so that this aligns with the macro.

Regards
Vishal Sagar


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-14 12:15         ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-14 12:15 UTC (permalink / raw)
  To: Joe Perches, Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari

Hi Joe,

Thanks for reviewing. 

> -----Original Message-----
> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
> owner@vger.kernel.org] On Behalf Of Joe Perches
> Sent: Friday, June 14, 2019 4:02 AM
> To: Hyun Kwon <hyunk@xilinx.com>; Vishal Sagar <vishal.sagar@xilinx.com>
> Cc: Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; linux-
> kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> EXTERNAL EMAIL
> 
> On Thu, 2019-06-13 at 15:05 -0700, Hyun Kwon wrote:
> > On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> 
> trivia:
> 
> > > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> []
> > > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > > +{
> []
> > > +   if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > > +           payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > > +           byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > > +                           XST352_PAYLOAD_BYTE_MASK;
> 
> Is XST352_PAYLOAD_BYTE_MASK correct ?
> Should it be XST352_PAYLOAD_BYTE1_MASK ?
> 

I had thought of it to be a generic mask to extract a byte out of 4 bytes in a ST352 packet.
Hence named it as XST352_PAYLOAD_BYTE_MASK

> > > +           active_luma = (payload &
> XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > > +                           XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > > +           pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > > +                           XST352_BYTE2_PIC_TYPE_OFFSET;
> > > +           framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > > +                           XST352_BYTE2_FPS_MASK;
> > > +           tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > > +                           XST352_BYTE2_TS_TYPE_OFFSET;
> >
> > Please align consistently throughout the patch. I believe the checkpatch
> > --strict warns on these.
> 
> I believe not.
> 
> Another possibility would be to use a macro like:
> 
> #define mask_and_shift(val, type)       \
>         ((val) & (XST352_ ## type ## _MASK)) >> (XST352_ ## type ## _OFFSET))
> 
> > > +           sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> > > +                      XST352_BYTE3_COLOR_FORMAT_OFFSET;
> 
> So these could be something like:
> 
>                 sampling = mask_and_shift(payload, BYTE3_COLOR_FORMAT);
> 

This looks like a good way. I will modify this in v2. 
I will also modify the XST352_PAYLOAD_BYTE_MASK to 
XST352_PAYLOAD_BYTE1_MASK so that this aligns with the macro.

Regards
Vishal Sagar

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-14 12:15         ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-14 12:15 UTC (permalink / raw)
  To: Joe Perches, Hyun Kwon, Vishal Sagar
  Cc: Mark Rutland, devicetree, Dinesh Kumar, Hyun Kwon,
	Sandip Kothari, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Mauro Carvalho Chehab, linux-arm-kernel,
	linux-media

Hi Joe,

Thanks for reviewing. 

> -----Original Message-----
> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
> owner@vger.kernel.org] On Behalf Of Joe Perches
> Sent: Friday, June 14, 2019 4:02 AM
> To: Hyun Kwon <hyunk@xilinx.com>; Vishal Sagar <vishal.sagar@xilinx.com>
> Cc: Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; linux-
> kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> EXTERNAL EMAIL
> 
> On Thu, 2019-06-13 at 15:05 -0700, Hyun Kwon wrote:
> > On Tue, 2019-06-04 at 06:55:56 -0700, Vishal Sagar wrote:
> 
> trivia:
> 
> > > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c
> b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> []
> > > +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> > > +{
> []
> > > +   if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> > > +           payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> > > +           byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> > > +                           XST352_PAYLOAD_BYTE_MASK;
> 
> Is XST352_PAYLOAD_BYTE_MASK correct ?
> Should it be XST352_PAYLOAD_BYTE1_MASK ?
> 

I had thought of it to be a generic mask to extract a byte out of 4 bytes in a ST352 packet.
Hence named it as XST352_PAYLOAD_BYTE_MASK

> > > +           active_luma = (payload &
> XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> > > +                           XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> > > +           pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> > > +                           XST352_BYTE2_PIC_TYPE_OFFSET;
> > > +           framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> > > +                           XST352_BYTE2_FPS_MASK;
> > > +           tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> > > +                           XST352_BYTE2_TS_TYPE_OFFSET;
> >
> > Please align consistently throughout the patch. I believe the checkpatch
> > --strict warns on these.
> 
> I believe not.
> 
> Another possibility would be to use a macro like:
> 
> #define mask_and_shift(val, type)       \
>         ((val) & (XST352_ ## type ## _MASK)) >> (XST352_ ## type ## _OFFSET))
> 
> > > +           sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> > > +                      XST352_BYTE3_COLOR_FORMAT_OFFSET;
> 
> So these could be something like:
> 
>                 sampling = mask_and_shift(payload, BYTE3_COLOR_FORMAT);
> 

This looks like a good way. I will modify this in v2. 
I will also modify the XST352_PAYLOAD_BYTE_MASK to 
XST352_PAYLOAD_BYTE1_MASK so that this aligns with the macro.

Regards
Vishal Sagar


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-14 11:44       ` Vishal Sagar
  (?)
@ 2019-06-15  7:55         ` Hans Verkuil
  -1 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2019-06-15  7:55 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus

On 6/14/19 1:44 PM, Vishal Sagar wrote:
> Hi Hans,
> 
> Thanks for reviewing this patch.
> 
>> -----Original Message-----
>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>> Sent: Wednesday, June 05, 2019 6:28 PM
>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>;
>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>> driver
>>
>> EXTERNAL EMAIL
>>
>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
>>> streams from SDI sources like SDI broadcast equipment like cameras and
>>> mixers. This block outputs either native SDI, native video or
>>> AXI4-Stream compliant data stream for further processing. Please refer
>>> to PG290 for details.
>>>
>>> The driver is used to configure the IP to add framer, search for
>>> specific modes, get the detected mode, stream parameters, errors, etc.
>>> It also generates events for video lock/unlock, bridge over/under flow.
>>>
>>> The driver supports only 10 bpc YUV 422 media bus format. It also
>>> decodes the stream parameters based on the ST352 packet embedded in the
>>> stream. In case the ST352 packet isn't present in the stream, the core's
>>> detected properties are used to set stream properties.
>>>
>>> The driver currently supports only the AXI4-Stream configuration.
>>>
>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
>>> ---
>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
>>>  drivers/media/platform/xilinx/Makefile         |    1 +
>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
>> ++++++++++++++++++++++++
>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +

<snip>

>> I am concerned about this driver: I see that none of the *_dv_timings callbacks
>> are implemented. I would expect to see that for a video receiver. There is also
>> no g_input_status implemented.
>>
>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
>>
> 
> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662 driver
> requires the timings because it is a SDI Transmitter. 
> 
> Here the timings are not required as the IP block generates a AXI4 Stream.
> I think it may be required only in case of native / parallel video being outputted
> as the output stream needs timing information to be decoded.
> 
> Please feel free to correct my understanding if wrong.
> 
> In the current driver, the input stream properties like width, height, frame rate,
> progressive/interlaced  are determined from the ST352 packet payload or from the
> properties detected by the core.
> 
> See the xsdirx_get_stream_properties() for details.

You're wrong. In xsdirx_get_stream_properties() you set the format information.
But you can't just change that: if the video resolution changes, then that means
that userspace needs to be informed that it has changed at the source, it has to
find and set the new timings, update the formats, possibly reallocate memory for
the buffers, update other parts of the video pipeline with the new resolution etc.

The one thing you cannot do is just pass on the new resolution and hope that the
video pipeline can handle it all.

The right sequence of events is:

1) When a change is detected at the source the driver sends the SOURCE_CHANGE
event and either stops transmitting to the video pipeline or keeps sending the
old resolution (some devices have a freewheeling mode where they can do that).

2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
any), usually stops streaming, and calls S_DV_TIMINGS to set the detected timings:
at that point the driver can configure the output towards the video pipeline with
the new timings. Userspace reallocates buffers and resumes streaming with the new
resolution.

Note that G_DV_TIMINGS returns the last configured timings, not the detected
timings: only QUERY_DV_TIMINGS does that.

In other words: userspace has to retain control of the full pipeline.

Regards,

	Hans

> 
>> Some of the controls you add in this driver can likely be dropped. Especially
>> those controls that are not specific to the Xilinx implementation but are
>> generic for any SDI receiver, should be looked at closely: those are
>> candidates for becoming standard controls.
> 
> I don't know how other SDI Receiver devices function.
> So I am assuming all these controls are Xilinx specific implementations.
> 
>>
>> But the documentation above is simply insufficient for me to tell what is
>> SDI specific and what is implementation specific.
>>
> 
> I will add more documentation for these controls.
> 
>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
>>
>> Regards,
>>
>>         Hans
> 
> Regards
> Vishal Sagar
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-15  7:55         ` Hans Verkuil
  0 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2019-06-15  7:55 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Mark Rutland, Sandip Kothari, Mauro Carvalho Chehab, Hyun Kwon,
	devicetree, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Sakari Ailus, Vishal Sagar, Dinesh Kumar,
	linux-arm-kernel, linux-media

On 6/14/19 1:44 PM, Vishal Sagar wrote:
> Hi Hans,
> 
> Thanks for reviewing this patch.
> 
>> -----Original Message-----
>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>> Sent: Wednesday, June 05, 2019 6:28 PM
>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>;
>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>> driver
>>
>> EXTERNAL EMAIL
>>
>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
>>> streams from SDI sources like SDI broadcast equipment like cameras and
>>> mixers. This block outputs either native SDI, native video or
>>> AXI4-Stream compliant data stream for further processing. Please refer
>>> to PG290 for details.
>>>
>>> The driver is used to configure the IP to add framer, search for
>>> specific modes, get the detected mode, stream parameters, errors, etc.
>>> It also generates events for video lock/unlock, bridge over/under flow.
>>>
>>> The driver supports only 10 bpc YUV 422 media bus format. It also
>>> decodes the stream parameters based on the ST352 packet embedded in the
>>> stream. In case the ST352 packet isn't present in the stream, the core's
>>> detected properties are used to set stream properties.
>>>
>>> The driver currently supports only the AXI4-Stream configuration.
>>>
>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
>>> ---
>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
>>>  drivers/media/platform/xilinx/Makefile         |    1 +
>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
>> ++++++++++++++++++++++++
>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +

<snip>

>> I am concerned about this driver: I see that none of the *_dv_timings callbacks
>> are implemented. I would expect to see that for a video receiver. There is also
>> no g_input_status implemented.
>>
>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
>>
> 
> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662 driver
> requires the timings because it is a SDI Transmitter. 
> 
> Here the timings are not required as the IP block generates a AXI4 Stream.
> I think it may be required only in case of native / parallel video being outputted
> as the output stream needs timing information to be decoded.
> 
> Please feel free to correct my understanding if wrong.
> 
> In the current driver, the input stream properties like width, height, frame rate,
> progressive/interlaced  are determined from the ST352 packet payload or from the
> properties detected by the core.
> 
> See the xsdirx_get_stream_properties() for details.

You're wrong. In xsdirx_get_stream_properties() you set the format information.
But you can't just change that: if the video resolution changes, then that means
that userspace needs to be informed that it has changed at the source, it has to
find and set the new timings, update the formats, possibly reallocate memory for
the buffers, update other parts of the video pipeline with the new resolution etc.

The one thing you cannot do is just pass on the new resolution and hope that the
video pipeline can handle it all.

The right sequence of events is:

1) When a change is detected at the source the driver sends the SOURCE_CHANGE
event and either stops transmitting to the video pipeline or keeps sending the
old resolution (some devices have a freewheeling mode where they can do that).

2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
any), usually stops streaming, and calls S_DV_TIMINGS to set the detected timings:
at that point the driver can configure the output towards the video pipeline with
the new timings. Userspace reallocates buffers and resumes streaming with the new
resolution.

Note that G_DV_TIMINGS returns the last configured timings, not the detected
timings: only QUERY_DV_TIMINGS does that.

In other words: userspace has to retain control of the full pipeline.

Regards,

	Hans

> 
>> Some of the controls you add in this driver can likely be dropped. Especially
>> those controls that are not specific to the Xilinx implementation but are
>> generic for any SDI receiver, should be looked at closely: those are
>> candidates for becoming standard controls.
> 
> I don't know how other SDI Receiver devices function.
> So I am assuming all these controls are Xilinx specific implementations.
> 
>>
>> But the documentation above is simply insufficient for me to tell what is
>> SDI specific and what is implementation specific.
>>
> 
> I will add more documentation for these controls.
> 
>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
>>
>> Regards,
>>
>>         Hans
> 
> Regards
> Vishal Sagar
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-15  7:55         ` Hans Verkuil
  0 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2019-06-15  7:55 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Mark Rutland, Sandip Kothari, Mauro Carvalho Chehab, Hyun Kwon,
	devicetree, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Sakari Ailus, Vishal Sagar, Dinesh Kumar,
	linux-arm-kernel, linux-media

On 6/14/19 1:44 PM, Vishal Sagar wrote:
> Hi Hans,
> 
> Thanks for reviewing this patch.
> 
>> -----Original Message-----
>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>> Sent: Wednesday, June 05, 2019 6:28 PM
>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>;
>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>> driver
>>
>> EXTERNAL EMAIL
>>
>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
>>> streams from SDI sources like SDI broadcast equipment like cameras and
>>> mixers. This block outputs either native SDI, native video or
>>> AXI4-Stream compliant data stream for further processing. Please refer
>>> to PG290 for details.
>>>
>>> The driver is used to configure the IP to add framer, search for
>>> specific modes, get the detected mode, stream parameters, errors, etc.
>>> It also generates events for video lock/unlock, bridge over/under flow.
>>>
>>> The driver supports only 10 bpc YUV 422 media bus format. It also
>>> decodes the stream parameters based on the ST352 packet embedded in the
>>> stream. In case the ST352 packet isn't present in the stream, the core's
>>> detected properties are used to set stream properties.
>>>
>>> The driver currently supports only the AXI4-Stream configuration.
>>>
>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
>>> ---
>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
>>>  drivers/media/platform/xilinx/Makefile         |    1 +
>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
>> ++++++++++++++++++++++++
>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +

<snip>

>> I am concerned about this driver: I see that none of the *_dv_timings callbacks
>> are implemented. I would expect to see that for a video receiver. There is also
>> no g_input_status implemented.
>>
>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
>>
> 
> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662 driver
> requires the timings because it is a SDI Transmitter. 
> 
> Here the timings are not required as the IP block generates a AXI4 Stream.
> I think it may be required only in case of native / parallel video being outputted
> as the output stream needs timing information to be decoded.
> 
> Please feel free to correct my understanding if wrong.
> 
> In the current driver, the input stream properties like width, height, frame rate,
> progressive/interlaced  are determined from the ST352 packet payload or from the
> properties detected by the core.
> 
> See the xsdirx_get_stream_properties() for details.

You're wrong. In xsdirx_get_stream_properties() you set the format information.
But you can't just change that: if the video resolution changes, then that means
that userspace needs to be informed that it has changed at the source, it has to
find and set the new timings, update the formats, possibly reallocate memory for
the buffers, update other parts of the video pipeline with the new resolution etc.

The one thing you cannot do is just pass on the new resolution and hope that the
video pipeline can handle it all.

The right sequence of events is:

1) When a change is detected at the source the driver sends the SOURCE_CHANGE
event and either stops transmitting to the video pipeline or keeps sending the
old resolution (some devices have a freewheeling mode where they can do that).

2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
any), usually stops streaming, and calls S_DV_TIMINGS to set the detected timings:
at that point the driver can configure the output towards the video pipeline with
the new timings. Userspace reallocates buffers and resumes streaming with the new
resolution.

Note that G_DV_TIMINGS returns the last configured timings, not the detected
timings: only QUERY_DV_TIMINGS does that.

In other words: userspace has to retain control of the full pipeline.

Regards,

	Hans

> 
>> Some of the controls you add in this driver can likely be dropped. Especially
>> those controls that are not specific to the Xilinx implementation but are
>> generic for any SDI receiver, should be looked at closely: those are
>> candidates for becoming standard controls.
> 
> I don't know how other SDI Receiver devices function.
> So I am assuming all these controls are Xilinx specific implementations.
> 
>>
>> But the documentation above is simply insufficient for me to tell what is
>> SDI specific and what is implementation specific.
>>
> 
> I will add more documentation for these controls.
> 
>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
>>
>> Regards,
>>
>>         Hans
> 
> Regards
> Vishal Sagar
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-15  7:55         ` Hans Verkuil
  (?)
@ 2019-06-18 11:51           ` Vishal Sagar
  -1 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-18 11:51 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus

Hi Hans,

> -----Original Message-----
> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> Sent: Saturday, June 15, 2019 1:25 PM
> To: Vishal Sagar <vsagar@xilinx.com>
> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
> <sakari.ailus@linux.intel.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> On 6/14/19 1:44 PM, Vishal Sagar wrote:
> > Hi Hans,
> >
> > Thanks for reviewing this patch.
> >
> >> -----Original Message-----
> >> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >> Sent: Wednesday, June 05, 2019 6:28 PM
> >> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
> <hyunk@xilinx.com>;
> >> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
> >> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
> >> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
> >> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> >> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> >> driver
> >>
> >> EXTERNAL EMAIL
> >>
> >> On 6/4/19 3:55 PM, Vishal Sagar wrote:
> >>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> >>> streams from SDI sources like SDI broadcast equipment like cameras and
> >>> mixers. This block outputs either native SDI, native video or
> >>> AXI4-Stream compliant data stream for further processing. Please refer
> >>> to PG290 for details.
> >>>
> >>> The driver is used to configure the IP to add framer, search for
> >>> specific modes, get the detected mode, stream parameters, errors, etc.
> >>> It also generates events for video lock/unlock, bridge over/under flow.
> >>>
> >>> The driver supports only 10 bpc YUV 422 media bus format. It also
> >>> decodes the stream parameters based on the ST352 packet embedded in
> the
> >>> stream. In case the ST352 packet isn't present in the stream, the core's
> >>> detected properties are used to set stream properties.
> >>>
> >>> The driver currently supports only the AXI4-Stream configuration.
> >>>
> >>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> >>> ---
> >>>  drivers/media/platform/xilinx/Kconfig          |   11 +
> >>>  drivers/media/platform/xilinx/Makefile         |    1 +
> >>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> >> ++++++++++++++++++++++++
> >>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> 
> <snip>
> 
> >> I am concerned about this driver: I see that none of the *_dv_timings
> callbacks
> >> are implemented. I would expect to see that for a video receiver. There is
> also
> >> no g_input_status implemented.
> >>
> >> Take a look at another SDI driver: drivers/media/spi/gs1662.c
> >>
> >
> > I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
> driver
> > requires the timings because it is a SDI Transmitter.
> >
> > Here the timings are not required as the IP block generates a AXI4 Stream.
> > I think it may be required only in case of native / parallel video being
> outputted
> > as the output stream needs timing information to be decoded.
> >
> > Please feel free to correct my understanding if wrong.
> >
> > In the current driver, the input stream properties like width, height, frame
> rate,
> > progressive/interlaced  are determined from the ST352 packet payload or
> from the
> > properties detected by the core.
> >
> > See the xsdirx_get_stream_properties() for details.
> 
> You're wrong. In xsdirx_get_stream_properties() you set the format
> information.
> But you can't just change that: if the video resolution changes, then that means
> that userspace needs to be informed that it has changed at the source, it has to
> find and set the new timings, update the formats, possibly reallocate memory
> for
> the buffers, update other parts of the video pipeline with the new resolution
> etc.
> 
> The one thing you cannot do is just pass on the new resolution and hope that
> the
> video pipeline can handle it all.
> 
> The right sequence of events is:
> 
> 1) When a change is detected at the source the driver sends the
> SOURCE_CHANGE
> event and either stops transmitting to the video pipeline or keeps sending the
> old resolution (some devices have a freewheeling mode where they can do
> that).
> 
> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
> timings:
> at that point the driver can configure the output towards the video pipeline
> with
> the new timings. Userspace reallocates buffers and resumes streaming with the
> new
> resolution.
> 

Thanks for the explanation!

I will remove the extraneous video unlock event and stop the streaming when video lock / unlock interrupt occurs.
I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL in case video is unlocked.

My assumption is that on SOURCE_CHANGE event, application can stop the pipeline and then 
call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type (progressive / interlaced) and frame rate.
Is this assumption correct? 

Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE event?

I also don't see any V4L2 framework supported events for overflow and underflow.
Is it ok to keep these or should they be removed too? 

Regards

Vishal Sagar

> Note that G_DV_TIMINGS returns the last configured timings, not the detected
> timings: only QUERY_DV_TIMINGS does that.
> 
> In other words: userspace has to retain control of the full pipeline.
> 
> Regards,
> 
> 	Hans
> 
> >
> >> Some of the controls you add in this driver can likely be dropped. Especially
> >> those controls that are not specific to the Xilinx implementation but are
> >> generic for any SDI receiver, should be looked at closely: those are
> >> candidates for becoming standard controls.
> >
> > I don't know how other SDI Receiver devices function.
> > So I am assuming all these controls are Xilinx specific implementations.
> >
> >>
> >> But the documentation above is simply insufficient for me to tell what is
> >> SDI specific and what is implementation specific.
> >>
> >
> > I will add more documentation for these controls.
> >
> >> Also, I'm no SDI expert, certainly not for the UHD-SDI.
> >>
> >> Regards,
> >>
> >>         Hans
> >
> > Regards
> > Vishal Sagar
> >


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-18 11:51           ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-18 11:51 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus

Hi Hans,

> -----Original Message-----
> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> Sent: Saturday, June 15, 2019 1:25 PM
> To: Vishal Sagar <vsagar@xilinx.com>
> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
> <sakari.ailus@linux.intel.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> On 6/14/19 1:44 PM, Vishal Sagar wrote:
> > Hi Hans,
> >
> > Thanks for reviewing this patch.
> >
> >> -----Original Message-----
> >> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >> Sent: Wednesday, June 05, 2019 6:28 PM
> >> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
> <hyunk@xilinx.com>;
> >> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
> >> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
> >> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
> >> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> >> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> >> driver
> >>
> >> EXTERNAL EMAIL
> >>
> >> On 6/4/19 3:55 PM, Vishal Sagar wrote:
> >>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> >>> streams from SDI sources like SDI broadcast equipment like cameras and
> >>> mixers. This block outputs either native SDI, native video or
> >>> AXI4-Stream compliant data stream for further processing. Please refer
> >>> to PG290 for details.
> >>>
> >>> The driver is used to configure the IP to add framer, search for
> >>> specific modes, get the detected mode, stream parameters, errors, etc.
> >>> It also generates events for video lock/unlock, bridge over/under flow.
> >>>
> >>> The driver supports only 10 bpc YUV 422 media bus format. It also
> >>> decodes the stream parameters based on the ST352 packet embedded in
> the
> >>> stream. In case the ST352 packet isn't present in the stream, the core's
> >>> detected properties are used to set stream properties.
> >>>
> >>> The driver currently supports only the AXI4-Stream configuration.
> >>>
> >>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> >>> ---
> >>>  drivers/media/platform/xilinx/Kconfig          |   11 +
> >>>  drivers/media/platform/xilinx/Makefile         |    1 +
> >>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> >> ++++++++++++++++++++++++
> >>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> 
> <snip>
> 
> >> I am concerned about this driver: I see that none of the *_dv_timings
> callbacks
> >> are implemented. I would expect to see that for a video receiver. There is
> also
> >> no g_input_status implemented.
> >>
> >> Take a look at another SDI driver: drivers/media/spi/gs1662.c
> >>
> >
> > I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
> driver
> > requires the timings because it is a SDI Transmitter.
> >
> > Here the timings are not required as the IP block generates a AXI4 Stream.
> > I think it may be required only in case of native / parallel video being
> outputted
> > as the output stream needs timing information to be decoded.
> >
> > Please feel free to correct my understanding if wrong.
> >
> > In the current driver, the input stream properties like width, height, frame
> rate,
> > progressive/interlaced  are determined from the ST352 packet payload or
> from the
> > properties detected by the core.
> >
> > See the xsdirx_get_stream_properties() for details.
> 
> You're wrong. In xsdirx_get_stream_properties() you set the format
> information.
> But you can't just change that: if the video resolution changes, then that means
> that userspace needs to be informed that it has changed at the source, it has to
> find and set the new timings, update the formats, possibly reallocate memory
> for
> the buffers, update other parts of the video pipeline with the new resolution
> etc.
> 
> The one thing you cannot do is just pass on the new resolution and hope that
> the
> video pipeline can handle it all.
> 
> The right sequence of events is:
> 
> 1) When a change is detected at the source the driver sends the
> SOURCE_CHANGE
> event and either stops transmitting to the video pipeline or keeps sending the
> old resolution (some devices have a freewheeling mode where they can do
> that).
> 
> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
> timings:
> at that point the driver can configure the output towards the video pipeline
> with
> the new timings. Userspace reallocates buffers and resumes streaming with the
> new
> resolution.
> 

Thanks for the explanation!

I will remove the extraneous video unlock event and stop the streaming when video lock / unlock interrupt occurs.
I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL in case video is unlocked.

My assumption is that on SOURCE_CHANGE event, application can stop the pipeline and then 
call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type (progressive / interlaced) and frame rate.
Is this assumption correct? 

Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE event?

I also don't see any V4L2 framework supported events for overflow and underflow.
Is it ok to keep these or should they be removed too? 

Regards

Vishal Sagar

> Note that G_DV_TIMINGS returns the last configured timings, not the detected
> timings: only QUERY_DV_TIMINGS does that.
> 
> In other words: userspace has to retain control of the full pipeline.
> 
> Regards,
> 
> 	Hans
> 
> >
> >> Some of the controls you add in this driver can likely be dropped. Especially
> >> those controls that are not specific to the Xilinx implementation but are
> >> generic for any SDI receiver, should be looked at closely: those are
> >> candidates for becoming standard controls.
> >
> > I don't know how other SDI Receiver devices function.
> > So I am assuming all these controls are Xilinx specific implementations.
> >
> >>
> >> But the documentation above is simply insufficient for me to tell what is
> >> SDI specific and what is implementation specific.
> >>
> >
> > I will add more documentation for these controls.
> >
> >> Also, I'm no SDI expert, certainly not for the UHD-SDI.
> >>
> >> Regards,
> >>
> >>         Hans
> >
> > Regards
> > Vishal Sagar
> >


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-18 11:51           ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-18 11:51 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Mark Rutland, Sandip Kothari, Mauro Carvalho Chehab, Hyun Kwon,
	devicetree, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Sakari Ailus, Vishal Sagar, Dinesh Kumar,
	linux-arm-kernel, linux-media

Hi Hans,

> -----Original Message-----
> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> Sent: Saturday, June 15, 2019 1:25 PM
> To: Vishal Sagar <vsagar@xilinx.com>
> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
> <sakari.ailus@linux.intel.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> On 6/14/19 1:44 PM, Vishal Sagar wrote:
> > Hi Hans,
> >
> > Thanks for reviewing this patch.
> >
> >> -----Original Message-----
> >> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >> Sent: Wednesday, June 05, 2019 6:28 PM
> >> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
> <hyunk@xilinx.com>;
> >> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
> >> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
> >> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
> >> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> >> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> >> driver
> >>
> >> EXTERNAL EMAIL
> >>
> >> On 6/4/19 3:55 PM, Vishal Sagar wrote:
> >>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> >>> streams from SDI sources like SDI broadcast equipment like cameras and
> >>> mixers. This block outputs either native SDI, native video or
> >>> AXI4-Stream compliant data stream for further processing. Please refer
> >>> to PG290 for details.
> >>>
> >>> The driver is used to configure the IP to add framer, search for
> >>> specific modes, get the detected mode, stream parameters, errors, etc.
> >>> It also generates events for video lock/unlock, bridge over/under flow.
> >>>
> >>> The driver supports only 10 bpc YUV 422 media bus format. It also
> >>> decodes the stream parameters based on the ST352 packet embedded in
> the
> >>> stream. In case the ST352 packet isn't present in the stream, the core's
> >>> detected properties are used to set stream properties.
> >>>
> >>> The driver currently supports only the AXI4-Stream configuration.
> >>>
> >>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> >>> ---
> >>>  drivers/media/platform/xilinx/Kconfig          |   11 +
> >>>  drivers/media/platform/xilinx/Makefile         |    1 +
> >>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> >> ++++++++++++++++++++++++
> >>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> 
> <snip>
> 
> >> I am concerned about this driver: I see that none of the *_dv_timings
> callbacks
> >> are implemented. I would expect to see that for a video receiver. There is
> also
> >> no g_input_status implemented.
> >>
> >> Take a look at another SDI driver: drivers/media/spi/gs1662.c
> >>
> >
> > I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
> driver
> > requires the timings because it is a SDI Transmitter.
> >
> > Here the timings are not required as the IP block generates a AXI4 Stream.
> > I think it may be required only in case of native / parallel video being
> outputted
> > as the output stream needs timing information to be decoded.
> >
> > Please feel free to correct my understanding if wrong.
> >
> > In the current driver, the input stream properties like width, height, frame
> rate,
> > progressive/interlaced  are determined from the ST352 packet payload or
> from the
> > properties detected by the core.
> >
> > See the xsdirx_get_stream_properties() for details.
> 
> You're wrong. In xsdirx_get_stream_properties() you set the format
> information.
> But you can't just change that: if the video resolution changes, then that means
> that userspace needs to be informed that it has changed at the source, it has to
> find and set the new timings, update the formats, possibly reallocate memory
> for
> the buffers, update other parts of the video pipeline with the new resolution
> etc.
> 
> The one thing you cannot do is just pass on the new resolution and hope that
> the
> video pipeline can handle it all.
> 
> The right sequence of events is:
> 
> 1) When a change is detected at the source the driver sends the
> SOURCE_CHANGE
> event and either stops transmitting to the video pipeline or keeps sending the
> old resolution (some devices have a freewheeling mode where they can do
> that).
> 
> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
> timings:
> at that point the driver can configure the output towards the video pipeline
> with
> the new timings. Userspace reallocates buffers and resumes streaming with the
> new
> resolution.
> 

Thanks for the explanation!

I will remove the extraneous video unlock event and stop the streaming when video lock / unlock interrupt occurs.
I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL in case video is unlocked.

My assumption is that on SOURCE_CHANGE event, application can stop the pipeline and then 
call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type (progressive / interlaced) and frame rate.
Is this assumption correct? 

Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE event?

I also don't see any V4L2 framework supported events for overflow and underflow.
Is it ok to keep these or should they be removed too? 

Regards

Vishal Sagar

> Note that G_DV_TIMINGS returns the last configured timings, not the detected
> timings: only QUERY_DV_TIMINGS does that.
> 
> In other words: userspace has to retain control of the full pipeline.
> 
> Regards,
> 
> 	Hans
> 
> >
> >> Some of the controls you add in this driver can likely be dropped. Especially
> >> those controls that are not specific to the Xilinx implementation but are
> >> generic for any SDI receiver, should be looked at closely: those are
> >> candidates for becoming standard controls.
> >
> > I don't know how other SDI Receiver devices function.
> > So I am assuming all these controls are Xilinx specific implementations.
> >
> >>
> >> But the documentation above is simply insufficient for me to tell what is
> >> SDI specific and what is implementation specific.
> >>
> >
> > I will add more documentation for these controls.
> >
> >> Also, I'm no SDI expert, certainly not for the UHD-SDI.
> >>
> >> Regards,
> >>
> >>         Hans
> >
> > Regards
> > Vishal Sagar
> >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-18 11:51           ` Vishal Sagar
  (?)
@ 2019-06-18 12:08             ` Hans Verkuil
  -1 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2019-06-18 12:08 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus

On 6/18/19 1:51 PM, Vishal Sagar wrote:
> Hi Hans,
> 
>> -----Original Message-----
>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>> Sent: Saturday, June 15, 2019 1:25 PM
>> To: Vishal Sagar <vsagar@xilinx.com>
>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
>> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
>> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
>> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
>> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
>> <sakari.ailus@linux.intel.com>
>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>> driver
>>
>> On 6/14/19 1:44 PM, Vishal Sagar wrote:
>>> Hi Hans,
>>>
>>> Thanks for reviewing this patch.
>>>
>>>> -----Original Message-----
>>>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>>>> Sent: Wednesday, June 05, 2019 6:28 PM
>>>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
>> <hyunk@xilinx.com>;
>>>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
>>>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
>>>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
>>>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>>>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>>>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
>>>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>>>> driver
>>>>
>>>> EXTERNAL EMAIL
>>>>
>>>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
>>>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
>>>>> streams from SDI sources like SDI broadcast equipment like cameras and
>>>>> mixers. This block outputs either native SDI, native video or
>>>>> AXI4-Stream compliant data stream for further processing. Please refer
>>>>> to PG290 for details.
>>>>>
>>>>> The driver is used to configure the IP to add framer, search for
>>>>> specific modes, get the detected mode, stream parameters, errors, etc.
>>>>> It also generates events for video lock/unlock, bridge over/under flow.
>>>>>
>>>>> The driver supports only 10 bpc YUV 422 media bus format. It also
>>>>> decodes the stream parameters based on the ST352 packet embedded in
>> the
>>>>> stream. In case the ST352 packet isn't present in the stream, the core's
>>>>> detected properties are used to set stream properties.
>>>>>
>>>>> The driver currently supports only the AXI4-Stream configuration.
>>>>>
>>>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
>>>>> ---
>>>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
>>>>>  drivers/media/platform/xilinx/Makefile         |    1 +
>>>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
>>>> ++++++++++++++++++++++++
>>>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>>>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>>>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>>
>> <snip>
>>
>>>> I am concerned about this driver: I see that none of the *_dv_timings
>> callbacks
>>>> are implemented. I would expect to see that for a video receiver. There is
>> also
>>>> no g_input_status implemented.
>>>>
>>>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
>>>>
>>>
>>> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
>> driver
>>> requires the timings because it is a SDI Transmitter.
>>>
>>> Here the timings are not required as the IP block generates a AXI4 Stream.
>>> I think it may be required only in case of native / parallel video being
>> outputted
>>> as the output stream needs timing information to be decoded.
>>>
>>> Please feel free to correct my understanding if wrong.
>>>
>>> In the current driver, the input stream properties like width, height, frame
>> rate,
>>> progressive/interlaced  are determined from the ST352 packet payload or
>> from the
>>> properties detected by the core.
>>>
>>> See the xsdirx_get_stream_properties() for details.
>>
>> You're wrong. In xsdirx_get_stream_properties() you set the format
>> information.
>> But you can't just change that: if the video resolution changes, then that means
>> that userspace needs to be informed that it has changed at the source, it has to
>> find and set the new timings, update the formats, possibly reallocate memory
>> for
>> the buffers, update other parts of the video pipeline with the new resolution
>> etc.
>>
>> The one thing you cannot do is just pass on the new resolution and hope that
>> the
>> video pipeline can handle it all.
>>
>> The right sequence of events is:
>>
>> 1) When a change is detected at the source the driver sends the
>> SOURCE_CHANGE
>> event and either stops transmitting to the video pipeline or keeps sending the
>> old resolution (some devices have a freewheeling mode where they can do
>> that).
>>
>> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
>> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
>> timings:
>> at that point the driver can configure the output towards the video pipeline
>> with
>> the new timings. Userspace reallocates buffers and resumes streaming with the
>> new
>> resolution.
>>
> 
> Thanks for the explanation!
> 
> I will remove the extraneous video unlock event and stop the streaming when video lock / unlock interrupt occurs.
> I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL in case video is unlocked.
> 
> My assumption is that on SOURCE_CHANGE event, application can stop the pipeline and then 
> call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type (progressive / interlaced) and frame rate.
> Is this assumption correct? 

No :-)

After SOURCE_CHANGE is received an application calls QUERY_DV_TIMINGS. If that
returns valid timings, then the application calls S_DV_TIMINGS with the
detected timings. The driver will now update the format, frame interval, etc.
according to the new timings. And the application can use that to reconfigure
the video pipeline.

> 
> Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE event?

Yes.

> 
> I also don't see any V4L2 framework supported events for overflow and underflow.
> Is it ok to keep these or should they be removed too? 

under/overflow of what? Internal fifos? You can keep the custom events for that.

Regards,

	Hans

> 
> Regards
> 
> Vishal Sagar
> 
>> Note that G_DV_TIMINGS returns the last configured timings, not the detected
>> timings: only QUERY_DV_TIMINGS does that.
>>
>> In other words: userspace has to retain control of the full pipeline.
>>
>> Regards,
>>
>> 	Hans
>>
>>>
>>>> Some of the controls you add in this driver can likely be dropped. Especially
>>>> those controls that are not specific to the Xilinx implementation but are
>>>> generic for any SDI receiver, should be looked at closely: those are
>>>> candidates for becoming standard controls.
>>>
>>> I don't know how other SDI Receiver devices function.
>>> So I am assuming all these controls are Xilinx specific implementations.
>>>
>>>>
>>>> But the documentation above is simply insufficient for me to tell what is
>>>> SDI specific and what is implementation specific.
>>>>
>>>
>>> I will add more documentation for these controls.
>>>
>>>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
>>>>
>>>> Regards,
>>>>
>>>>         Hans
>>>
>>> Regards
>>> Vishal Sagar
>>>
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-18 12:08             ` Hans Verkuil
  0 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2019-06-18 12:08 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus

On 6/18/19 1:51 PM, Vishal Sagar wrote:
> Hi Hans,
> 
>> -----Original Message-----
>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>> Sent: Saturday, June 15, 2019 1:25 PM
>> To: Vishal Sagar <vsagar@xilinx.com>
>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
>> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
>> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
>> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
>> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
>> <sakari.ailus@linux.intel.com>
>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>> driver
>>
>> On 6/14/19 1:44 PM, Vishal Sagar wrote:
>>> Hi Hans,
>>>
>>> Thanks for reviewing this patch.
>>>
>>>> -----Original Message-----
>>>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>>>> Sent: Wednesday, June 05, 2019 6:28 PM
>>>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
>> <hyunk@xilinx.com>;
>>>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
>>>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
>>>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
>>>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>>>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>>>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
>>>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>>>> driver
>>>>
>>>> EXTERNAL EMAIL
>>>>
>>>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
>>>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
>>>>> streams from SDI sources like SDI broadcast equipment like cameras and
>>>>> mixers. This block outputs either native SDI, native video or
>>>>> AXI4-Stream compliant data stream for further processing. Please refer
>>>>> to PG290 for details.
>>>>>
>>>>> The driver is used to configure the IP to add framer, search for
>>>>> specific modes, get the detected mode, stream parameters, errors, etc.
>>>>> It also generates events for video lock/unlock, bridge over/under flow.
>>>>>
>>>>> The driver supports only 10 bpc YUV 422 media bus format. It also
>>>>> decodes the stream parameters based on the ST352 packet embedded in
>> the
>>>>> stream. In case the ST352 packet isn't present in the stream, the core's
>>>>> detected properties are used to set stream properties.
>>>>>
>>>>> The driver currently supports only the AXI4-Stream configuration.
>>>>>
>>>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
>>>>> ---
>>>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
>>>>>  drivers/media/platform/xilinx/Makefile         |    1 +
>>>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
>>>> ++++++++++++++++++++++++
>>>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>>>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>>>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>>
>> <snip>
>>
>>>> I am concerned about this driver: I see that none of the *_dv_timings
>> callbacks
>>>> are implemented. I would expect to see that for a video receiver. There is
>> also
>>>> no g_input_status implemented.
>>>>
>>>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
>>>>
>>>
>>> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
>> driver
>>> requires the timings because it is a SDI Transmitter.
>>>
>>> Here the timings are not required as the IP block generates a AXI4 Stream.
>>> I think it may be required only in case of native / parallel video being
>> outputted
>>> as the output stream needs timing information to be decoded.
>>>
>>> Please feel free to correct my understanding if wrong.
>>>
>>> In the current driver, the input stream properties like width, height, frame
>> rate,
>>> progressive/interlaced  are determined from the ST352 packet payload or
>> from the
>>> properties detected by the core.
>>>
>>> See the xsdirx_get_stream_properties() for details.
>>
>> You're wrong. In xsdirx_get_stream_properties() you set the format
>> information.
>> But you can't just change that: if the video resolution changes, then that means
>> that userspace needs to be informed that it has changed at the source, it has to
>> find and set the new timings, update the formats, possibly reallocate memory
>> for
>> the buffers, update other parts of the video pipeline with the new resolution
>> etc.
>>
>> The one thing you cannot do is just pass on the new resolution and hope that
>> the
>> video pipeline can handle it all.
>>
>> The right sequence of events is:
>>
>> 1) When a change is detected at the source the driver sends the
>> SOURCE_CHANGE
>> event and either stops transmitting to the video pipeline or keeps sending the
>> old resolution (some devices have a freewheeling mode where they can do
>> that).
>>
>> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
>> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
>> timings:
>> at that point the driver can configure the output towards the video pipeline
>> with
>> the new timings. Userspace reallocates buffers and resumes streaming with the
>> new
>> resolution.
>>
> 
> Thanks for the explanation!
> 
> I will remove the extraneous video unlock event and stop the streaming when video lock / unlock interrupt occurs.
> I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL in case video is unlocked.
> 
> My assumption is that on SOURCE_CHANGE event, application can stop the pipeline and then 
> call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type (progressive / interlaced) and frame rate.
> Is this assumption correct? 

No :-)

After SOURCE_CHANGE is received an application calls QUERY_DV_TIMINGS. If that
returns valid timings, then the application calls S_DV_TIMINGS with the
detected timings. The driver will now update the format, frame interval, etc.
according to the new timings. And the application can use that to reconfigure
the video pipeline.

> 
> Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE event?

Yes.

> 
> I also don't see any V4L2 framework supported events for overflow and underflow.
> Is it ok to keep these or should they be removed too? 

under/overflow of what? Internal fifos? You can keep the custom events for that.

Regards,

	Hans

> 
> Regards
> 
> Vishal Sagar
> 
>> Note that G_DV_TIMINGS returns the last configured timings, not the detected
>> timings: only QUERY_DV_TIMINGS does that.
>>
>> In other words: userspace has to retain control of the full pipeline.
>>
>> Regards,
>>
>> 	Hans
>>
>>>
>>>> Some of the controls you add in this driver can likely be dropped. Especially
>>>> those controls that are not specific to the Xilinx implementation but are
>>>> generic for any SDI receiver, should be looked at closely: those are
>>>> candidates for becoming standard controls.
>>>
>>> I don't know how other SDI Receiver devices function.
>>> So I am assuming all these controls are Xilinx specific implementations.
>>>
>>>>
>>>> But the documentation above is simply insufficient for me to tell what is
>>>> SDI specific and what is implementation specific.
>>>>
>>>
>>> I will add more documentation for these controls.
>>>
>>>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
>>>>
>>>> Regards,
>>>>
>>>>         Hans
>>>
>>> Regards
>>> Vishal Sagar
>>>
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-18 12:08             ` Hans Verkuil
  0 siblings, 0 replies; 41+ messages in thread
From: Hans Verkuil @ 2019-06-18 12:08 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Mark Rutland, Sandip Kothari, Mauro Carvalho Chehab, Hyun Kwon,
	devicetree, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Sakari Ailus, Vishal Sagar, Dinesh Kumar,
	linux-arm-kernel, linux-media

On 6/18/19 1:51 PM, Vishal Sagar wrote:
> Hi Hans,
> 
>> -----Original Message-----
>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>> Sent: Saturday, June 15, 2019 1:25 PM
>> To: Vishal Sagar <vsagar@xilinx.com>
>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
>> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
>> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
>> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
>> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
>> <sakari.ailus@linux.intel.com>
>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>> driver
>>
>> On 6/14/19 1:44 PM, Vishal Sagar wrote:
>>> Hi Hans,
>>>
>>> Thanks for reviewing this patch.
>>>
>>>> -----Original Message-----
>>>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
>>>> Sent: Wednesday, June 05, 2019 6:28 PM
>>>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
>> <hyunk@xilinx.com>;
>>>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
>>>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob
>>>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
>>>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
>>>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
>>>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
>>>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>>>> driver
>>>>
>>>> EXTERNAL EMAIL
>>>>
>>>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
>>>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
>>>>> streams from SDI sources like SDI broadcast equipment like cameras and
>>>>> mixers. This block outputs either native SDI, native video or
>>>>> AXI4-Stream compliant data stream for further processing. Please refer
>>>>> to PG290 for details.
>>>>>
>>>>> The driver is used to configure the IP to add framer, search for
>>>>> specific modes, get the detected mode, stream parameters, errors, etc.
>>>>> It also generates events for video lock/unlock, bridge over/under flow.
>>>>>
>>>>> The driver supports only 10 bpc YUV 422 media bus format. It also
>>>>> decodes the stream parameters based on the ST352 packet embedded in
>> the
>>>>> stream. In case the ST352 packet isn't present in the stream, the core's
>>>>> detected properties are used to set stream properties.
>>>>>
>>>>> The driver currently supports only the AXI4-Stream configuration.
>>>>>
>>>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
>>>>> ---
>>>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
>>>>>  drivers/media/platform/xilinx/Makefile         |    1 +
>>>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
>>>> ++++++++++++++++++++++++
>>>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>>>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>>>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>>
>> <snip>
>>
>>>> I am concerned about this driver: I see that none of the *_dv_timings
>> callbacks
>>>> are implemented. I would expect to see that for a video receiver. There is
>> also
>>>> no g_input_status implemented.
>>>>
>>>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
>>>>
>>>
>>> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
>> driver
>>> requires the timings because it is a SDI Transmitter.
>>>
>>> Here the timings are not required as the IP block generates a AXI4 Stream.
>>> I think it may be required only in case of native / parallel video being
>> outputted
>>> as the output stream needs timing information to be decoded.
>>>
>>> Please feel free to correct my understanding if wrong.
>>>
>>> In the current driver, the input stream properties like width, height, frame
>> rate,
>>> progressive/interlaced  are determined from the ST352 packet payload or
>> from the
>>> properties detected by the core.
>>>
>>> See the xsdirx_get_stream_properties() for details.
>>
>> You're wrong. In xsdirx_get_stream_properties() you set the format
>> information.
>> But you can't just change that: if the video resolution changes, then that means
>> that userspace needs to be informed that it has changed at the source, it has to
>> find and set the new timings, update the formats, possibly reallocate memory
>> for
>> the buffers, update other parts of the video pipeline with the new resolution
>> etc.
>>
>> The one thing you cannot do is just pass on the new resolution and hope that
>> the
>> video pipeline can handle it all.
>>
>> The right sequence of events is:
>>
>> 1) When a change is detected at the source the driver sends the
>> SOURCE_CHANGE
>> event and either stops transmitting to the video pipeline or keeps sending the
>> old resolution (some devices have a freewheeling mode where they can do
>> that).
>>
>> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings (if
>> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
>> timings:
>> at that point the driver can configure the output towards the video pipeline
>> with
>> the new timings. Userspace reallocates buffers and resumes streaming with the
>> new
>> resolution.
>>
> 
> Thanks for the explanation!
> 
> I will remove the extraneous video unlock event and stop the streaming when video lock / unlock interrupt occurs.
> I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL in case video is unlocked.
> 
> My assumption is that on SOURCE_CHANGE event, application can stop the pipeline and then 
> call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type (progressive / interlaced) and frame rate.
> Is this assumption correct? 

No :-)

After SOURCE_CHANGE is received an application calls QUERY_DV_TIMINGS. If that
returns valid timings, then the application calls S_DV_TIMINGS with the
detected timings. The driver will now update the format, frame interval, etc.
according to the new timings. And the application can use that to reconfigure
the video pipeline.

> 
> Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE event?

Yes.

> 
> I also don't see any V4L2 framework supported events for overflow and underflow.
> Is it ok to keep these or should they be removed too? 

under/overflow of what? Internal fifos? You can keep the custom events for that.

Regards,

	Hans

> 
> Regards
> 
> Vishal Sagar
> 
>> Note that G_DV_TIMINGS returns the last configured timings, not the detected
>> timings: only QUERY_DV_TIMINGS does that.
>>
>> In other words: userspace has to retain control of the full pipeline.
>>
>> Regards,
>>
>> 	Hans
>>
>>>
>>>> Some of the controls you add in this driver can likely be dropped. Especially
>>>> those controls that are not specific to the Xilinx implementation but are
>>>> generic for any SDI receiver, should be looked at closely: those are
>>>> candidates for becoming standard controls.
>>>
>>> I don't know how other SDI Receiver devices function.
>>> So I am assuming all these controls are Xilinx specific implementations.
>>>
>>>>
>>>> But the documentation above is simply insufficient for me to tell what is
>>>> SDI specific and what is implementation specific.
>>>>
>>>
>>> I will add more documentation for these controls.
>>>
>>>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
>>>>
>>>> Regards,
>>>>
>>>>         Hans
>>>
>>> Regards
>>> Vishal Sagar
>>>
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-18 12:08             ` Hans Verkuil
  (?)
@ 2019-06-18 12:45               ` Vishal Sagar
  -1 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-18 12:45 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus


Hi Hans,

> -----Original Message-----
> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> Sent: Tuesday, June 18, 2019 5:38 PM
> To: Vishal Sagar <vsagar@xilinx.com>
> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
> <sakari.ailus@linux.intel.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> On 6/18/19 1:51 PM, Vishal Sagar wrote:
> > Hi Hans,
> >
> >> -----Original Message-----
> >> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >> Sent: Saturday, June 15, 2019 1:25 PM
> >> To: Vishal Sagar <vsagar@xilinx.com>
> >> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> >> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent
> Pinchart
> >> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> >> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> >> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari
> Ailus
> >> <sakari.ailus@linux.intel.com>
> >> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> >> driver
> >>
> >> On 6/14/19 1:44 PM, Vishal Sagar wrote:
> >>> Hi Hans,
> >>>
> >>> Thanks for reviewing this patch.
> >>>
> >>>> -----Original Message-----
> >>>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >>>> Sent: Wednesday, June 05, 2019 6:28 PM
> >>>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
> >> <hyunk@xilinx.com>;
> >>>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
> >>>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>;
> Rob
> >>>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
> >>>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >>>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >>>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> >>>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx
> Subsystem
> >>>> driver
> >>>>
> >>>> EXTERNAL EMAIL
> >>>>
> >>>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
> >>>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> >>>>> streams from SDI sources like SDI broadcast equipment like cameras and
> >>>>> mixers. This block outputs either native SDI, native video or
> >>>>> AXI4-Stream compliant data stream for further processing. Please refer
> >>>>> to PG290 for details.
> >>>>>
> >>>>> The driver is used to configure the IP to add framer, search for
> >>>>> specific modes, get the detected mode, stream parameters, errors, etc.
> >>>>> It also generates events for video lock/unlock, bridge over/under flow.
> >>>>>
> >>>>> The driver supports only 10 bpc YUV 422 media bus format. It also
> >>>>> decodes the stream parameters based on the ST352 packet embedded in
> >> the
> >>>>> stream. In case the ST352 packet isn't present in the stream, the core's
> >>>>> detected properties are used to set stream properties.
> >>>>>
> >>>>> The driver currently supports only the AXI4-Stream configuration.
> >>>>>
> >>>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> >>>>> ---
> >>>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
> >>>>>  drivers/media/platform/xilinx/Makefile         |    1 +
> >>>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> >>>> ++++++++++++++++++++++++
> >>>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >>>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >>>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> >>
> >> <snip>
> >>
> >>>> I am concerned about this driver: I see that none of the *_dv_timings
> >> callbacks
> >>>> are implemented. I would expect to see that for a video receiver. There is
> >> also
> >>>> no g_input_status implemented.
> >>>>
> >>>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
> >>>>
> >>>
> >>> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
> >> driver
> >>> requires the timings because it is a SDI Transmitter.
> >>>
> >>> Here the timings are not required as the IP block generates a AXI4 Stream.
> >>> I think it may be required only in case of native / parallel video being
> >> outputted
> >>> as the output stream needs timing information to be decoded.
> >>>
> >>> Please feel free to correct my understanding if wrong.
> >>>
> >>> In the current driver, the input stream properties like width, height, frame
> >> rate,
> >>> progressive/interlaced  are determined from the ST352 packet payload or
> >> from the
> >>> properties detected by the core.
> >>>
> >>> See the xsdirx_get_stream_properties() for details.
> >>
> >> You're wrong. In xsdirx_get_stream_properties() you set the format
> >> information.
> >> But you can't just change that: if the video resolution changes, then that
> means
> >> that userspace needs to be informed that it has changed at the source, it has
> to
> >> find and set the new timings, update the formats, possibly reallocate
> memory
> >> for
> >> the buffers, update other parts of the video pipeline with the new resolution
> >> etc.
> >>
> >> The one thing you cannot do is just pass on the new resolution and hope
> that
> >> the
> >> video pipeline can handle it all.
> >>
> >> The right sequence of events is:
> >>
> >> 1) When a change is detected at the source the driver sends the
> >> SOURCE_CHANGE
> >> event and either stops transmitting to the video pipeline or keeps sending
> the
> >> old resolution (some devices have a freewheeling mode where they can do
> >> that).
> >>
> >> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings
> (if
> >> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
> >> timings:
> >> at that point the driver can configure the output towards the video pipeline
> >> with
> >> the new timings. Userspace reallocates buffers and resumes streaming with
> the
> >> new
> >> resolution.
> >>
> >
> > Thanks for the explanation!
> >
> > I will remove the extraneous video unlock event and stop the streaming when
> video lock / unlock interrupt occurs.
> > I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC |
> V4L2_IN_ST_NO_SIGNAL in case video is unlocked.
> >
> > My assumption is that on SOURCE_CHANGE event, application can stop the
> pipeline and then
> > call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type
> (progressive / interlaced) and frame rate.
> > Is this assumption correct?
> 
> No :-)
> 

Good to have that cleared. :-D

> After SOURCE_CHANGE is received an application calls QUERY_DV_TIMINGS. If
> that
> returns valid timings, then the application calls S_DV_TIMINGS with the
> detected timings. The driver will now update the format, frame interval, etc.
> according to the new timings. And the application can use that to reconfigure
> the video pipeline.
> 
> >
> > Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE
> event?
> 
> Yes.
> 

Thanks again for clarifying this. 

> >
> > I also don't see any V4L2 framework supported events for overflow and
> underflow.
> > Is it ok to keep these or should they be removed too?
> 
> under/overflow of what? Internal fifos? You can keep the custom events for
> that.
>

Yep these are custom events for internal fifos. I will keep them.

Regards
Vishal Sagar

> Regards,
> 
> 	Hans
> 
> >
> > Regards
> >
> > Vishal Sagar
> >
> >> Note that G_DV_TIMINGS returns the last configured timings, not the
> detected
> >> timings: only QUERY_DV_TIMINGS does that.
> >>
> >> In other words: userspace has to retain control of the full pipeline.
> >>
> >> Regards,
> >>
> >> 	Hans
> >>
> >>>
> >>>> Some of the controls you add in this driver can likely be dropped.
> Especially
> >>>> those controls that are not specific to the Xilinx implementation but are
> >>>> generic for any SDI receiver, should be looked at closely: those are
> >>>> candidates for becoming standard controls.
> >>>
> >>> I don't know how other SDI Receiver devices function.
> >>> So I am assuming all these controls are Xilinx specific implementations.
> >>>
> >>>>
> >>>> But the documentation above is simply insufficient for me to tell what is
> >>>> SDI specific and what is implementation specific.
> >>>>
> >>>
> >>> I will add more documentation for these controls.
> >>>
> >>>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
> >>>>
> >>>> Regards,
> >>>>
> >>>>         Hans
> >>>
> >>> Regards
> >>> Vishal Sagar
> >>>
> >


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-18 12:45               ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-18 12:45 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-kernel, linux-media, linux-arm-kernel, devicetree,
	Dinesh Kumar, Sandip Kothari, Vishal Sagar, Hyun Kwon,
	Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, Sakari Ailus


Hi Hans,

> -----Original Message-----
> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> Sent: Tuesday, June 18, 2019 5:38 PM
> To: Vishal Sagar <vsagar@xilinx.com>
> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
> <sakari.ailus@linux.intel.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> On 6/18/19 1:51 PM, Vishal Sagar wrote:
> > Hi Hans,
> >
> >> -----Original Message-----
> >> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >> Sent: Saturday, June 15, 2019 1:25 PM
> >> To: Vishal Sagar <vsagar@xilinx.com>
> >> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> >> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent
> Pinchart
> >> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> >> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> >> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari
> Ailus
> >> <sakari.ailus@linux.intel.com>
> >> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> >> driver
> >>
> >> On 6/14/19 1:44 PM, Vishal Sagar wrote:
> >>> Hi Hans,
> >>>
> >>> Thanks for reviewing this patch.
> >>>
> >>>> -----Original Message-----
> >>>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >>>> Sent: Wednesday, June 05, 2019 6:28 PM
> >>>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
> >> <hyunk@xilinx.com>;
> >>>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
> >>>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>;
> Rob
> >>>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
> >>>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >>>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >>>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> >>>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx
> Subsystem
> >>>> driver
> >>>>
> >>>> EXTERNAL EMAIL
> >>>>
> >>>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
> >>>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> >>>>> streams from SDI sources like SDI broadcast equipment like cameras and
> >>>>> mixers. This block outputs either native SDI, native video or
> >>>>> AXI4-Stream compliant data stream for further processing. Please refer
> >>>>> to PG290 for details.
> >>>>>
> >>>>> The driver is used to configure the IP to add framer, search for
> >>>>> specific modes, get the detected mode, stream parameters, errors, etc.
> >>>>> It also generates events for video lock/unlock, bridge over/under flow.
> >>>>>
> >>>>> The driver supports only 10 bpc YUV 422 media bus format. It also
> >>>>> decodes the stream parameters based on the ST352 packet embedded in
> >> the
> >>>>> stream. In case the ST352 packet isn't present in the stream, the core's
> >>>>> detected properties are used to set stream properties.
> >>>>>
> >>>>> The driver currently supports only the AXI4-Stream configuration.
> >>>>>
> >>>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> >>>>> ---
> >>>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
> >>>>>  drivers/media/platform/xilinx/Makefile         |    1 +
> >>>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> >>>> ++++++++++++++++++++++++
> >>>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >>>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >>>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> >>
> >> <snip>
> >>
> >>>> I am concerned about this driver: I see that none of the *_dv_timings
> >> callbacks
> >>>> are implemented. I would expect to see that for a video receiver. There is
> >> also
> >>>> no g_input_status implemented.
> >>>>
> >>>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
> >>>>
> >>>
> >>> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
> >> driver
> >>> requires the timings because it is a SDI Transmitter.
> >>>
> >>> Here the timings are not required as the IP block generates a AXI4 Stream.
> >>> I think it may be required only in case of native / parallel video being
> >> outputted
> >>> as the output stream needs timing information to be decoded.
> >>>
> >>> Please feel free to correct my understanding if wrong.
> >>>
> >>> In the current driver, the input stream properties like width, height, frame
> >> rate,
> >>> progressive/interlaced  are determined from the ST352 packet payload or
> >> from the
> >>> properties detected by the core.
> >>>
> >>> See the xsdirx_get_stream_properties() for details.
> >>
> >> You're wrong. In xsdirx_get_stream_properties() you set the format
> >> information.
> >> But you can't just change that: if the video resolution changes, then that
> means
> >> that userspace needs to be informed that it has changed at the source, it has
> to
> >> find and set the new timings, update the formats, possibly reallocate
> memory
> >> for
> >> the buffers, update other parts of the video pipeline with the new resolution
> >> etc.
> >>
> >> The one thing you cannot do is just pass on the new resolution and hope
> that
> >> the
> >> video pipeline can handle it all.
> >>
> >> The right sequence of events is:
> >>
> >> 1) When a change is detected at the source the driver sends the
> >> SOURCE_CHANGE
> >> event and either stops transmitting to the video pipeline or keeps sending
> the
> >> old resolution (some devices have a freewheeling mode where they can do
> >> that).
> >>
> >> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings
> (if
> >> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
> >> timings:
> >> at that point the driver can configure the output towards the video pipeline
> >> with
> >> the new timings. Userspace reallocates buffers and resumes streaming with
> the
> >> new
> >> resolution.
> >>
> >
> > Thanks for the explanation!
> >
> > I will remove the extraneous video unlock event and stop the streaming when
> video lock / unlock interrupt occurs.
> > I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC |
> V4L2_IN_ST_NO_SIGNAL in case video is unlocked.
> >
> > My assumption is that on SOURCE_CHANGE event, application can stop the
> pipeline and then
> > call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type
> (progressive / interlaced) and frame rate.
> > Is this assumption correct?
> 
> No :-)
> 

Good to have that cleared. :-D

> After SOURCE_CHANGE is received an application calls QUERY_DV_TIMINGS. If
> that
> returns valid timings, then the application calls S_DV_TIMINGS with the
> detected timings. The driver will now update the format, frame interval, etc.
> according to the new timings. And the application can use that to reconfigure
> the video pipeline.
> 
> >
> > Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE
> event?
> 
> Yes.
> 

Thanks again for clarifying this. 

> >
> > I also don't see any V4L2 framework supported events for overflow and
> underflow.
> > Is it ok to keep these or should they be removed too?
> 
> under/overflow of what? Internal fifos? You can keep the custom events for
> that.
>

Yep these are custom events for internal fifos. I will keep them.

Regards
Vishal Sagar

> Regards,
> 
> 	Hans
> 
> >
> > Regards
> >
> > Vishal Sagar
> >
> >> Note that G_DV_TIMINGS returns the last configured timings, not the
> detected
> >> timings: only QUERY_DV_TIMINGS does that.
> >>
> >> In other words: userspace has to retain control of the full pipeline.
> >>
> >> Regards,
> >>
> >> 	Hans
> >>
> >>>
> >>>> Some of the controls you add in this driver can likely be dropped.
> Especially
> >>>> those controls that are not specific to the Xilinx implementation but are
> >>>> generic for any SDI receiver, should be looked at closely: those are
> >>>> candidates for becoming standard controls.
> >>>
> >>> I don't know how other SDI Receiver devices function.
> >>> So I am assuming all these controls are Xilinx specific implementations.
> >>>
> >>>>
> >>>> But the documentation above is simply insufficient for me to tell what is
> >>>> SDI specific and what is implementation specific.
> >>>>
> >>>
> >>> I will add more documentation for these controls.
> >>>
> >>>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
> >>>>
> >>>> Regards,
> >>>>
> >>>>         Hans
> >>>
> >>> Regards
> >>> Vishal Sagar
> >>>
> >


^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-06-18 12:45               ` Vishal Sagar
  0 siblings, 0 replies; 41+ messages in thread
From: Vishal Sagar @ 2019-06-18 12:45 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Mark Rutland, Sandip Kothari, Mauro Carvalho Chehab, Hyun Kwon,
	devicetree, linux-kernel, Rob Herring, Michal Simek,
	Laurent Pinchart, Sakari Ailus, Vishal Sagar, Dinesh Kumar,
	linux-arm-kernel, linux-media


Hi Hans,

> -----Original Message-----
> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> Sent: Tuesday, June 18, 2019 5:38 PM
> To: Vishal Sagar <vsagar@xilinx.com>
> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent Pinchart
> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari Ailus
> <sakari.ailus@linux.intel.com>
> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
> 
> On 6/18/19 1:51 PM, Vishal Sagar wrote:
> > Hi Hans,
> >
> >> -----Original Message-----
> >> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >> Sent: Saturday, June 15, 2019 1:25 PM
> >> To: Vishal Sagar <vsagar@xilinx.com>
> >> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>; Vishal Sagar
> >> <vishal.sagar@xilinx.com>; Hyun Kwon <hyunk@xilinx.com>; Laurent
> Pinchart
> >> <laurent.pinchart@ideasonboard.com>; Mauro Carvalho Chehab
> >> <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>; Rob Herring
> >> <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>; Sakari
> Ailus
> >> <sakari.ailus@linux.intel.com>
> >> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> >> driver
> >>
> >> On 6/14/19 1:44 PM, Vishal Sagar wrote:
> >>> Hi Hans,
> >>>
> >>> Thanks for reviewing this patch.
> >>>
> >>>> -----Original Message-----
> >>>> From: Hans Verkuil [mailto:hverkuil@xs4all.nl]
> >>>> Sent: Wednesday, June 05, 2019 6:28 PM
> >>>> To: Vishal Sagar <vishal.sagar@xilinx.com>; Hyun Kwon
> >> <hyunk@xilinx.com>;
> >>>> Laurent Pinchart <laurent.pinchart@ideasonboard.com>; Mauro Carvalho
> >>>> Chehab <mchehab@kernel.org>; Michal Simek <michals@xilinx.com>;
> Rob
> >>>> Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>
> >>>> Cc: linux-kernel@vger.kernel.org; linux-media@vger.kernel.org; linux-arm-
> >>>> kernel@lists.infradead.org; devicetree@vger.kernel.org; Dinesh Kumar
> >>>> <dineshk@xilinx.com>; Sandip Kothari <sandipk@xilinx.com>
> >>>> Subject: Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx
> Subsystem
> >>>> driver
> >>>>
> >>>> EXTERNAL EMAIL
> >>>>
> >>>> On 6/4/19 3:55 PM, Vishal Sagar wrote:
> >>>>> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> >>>>> streams from SDI sources like SDI broadcast equipment like cameras and
> >>>>> mixers. This block outputs either native SDI, native video or
> >>>>> AXI4-Stream compliant data stream for further processing. Please refer
> >>>>> to PG290 for details.
> >>>>>
> >>>>> The driver is used to configure the IP to add framer, search for
> >>>>> specific modes, get the detected mode, stream parameters, errors, etc.
> >>>>> It also generates events for video lock/unlock, bridge over/under flow.
> >>>>>
> >>>>> The driver supports only 10 bpc YUV 422 media bus format. It also
> >>>>> decodes the stream parameters based on the ST352 packet embedded in
> >> the
> >>>>> stream. In case the ST352 packet isn't present in the stream, the core's
> >>>>> detected properties are used to set stream properties.
> >>>>>
> >>>>> The driver currently supports only the AXI4-Stream configuration.
> >>>>>
> >>>>> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> >>>>> ---
> >>>>>  drivers/media/platform/xilinx/Kconfig          |   11 +
> >>>>>  drivers/media/platform/xilinx/Makefile         |    1 +
> >>>>>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846
> >>>> ++++++++++++++++++++++++
> >>>>>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
> >>>>>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
> >>>>>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
> >>
> >> <snip>
> >>
> >>>> I am concerned about this driver: I see that none of the *_dv_timings
> >> callbacks
> >>>> are implemented. I would expect to see that for a video receiver. There is
> >> also
> >>>> no g_input_status implemented.
> >>>>
> >>>> Take a look at another SDI driver: drivers/media/spi/gs1662.c
> >>>>
> >>>
> >>> I had a look at the gs1662 driver for the dv_timings callbacks. The gs1662
> >> driver
> >>> requires the timings because it is a SDI Transmitter.
> >>>
> >>> Here the timings are not required as the IP block generates a AXI4 Stream.
> >>> I think it may be required only in case of native / parallel video being
> >> outputted
> >>> as the output stream needs timing information to be decoded.
> >>>
> >>> Please feel free to correct my understanding if wrong.
> >>>
> >>> In the current driver, the input stream properties like width, height, frame
> >> rate,
> >>> progressive/interlaced  are determined from the ST352 packet payload or
> >> from the
> >>> properties detected by the core.
> >>>
> >>> See the xsdirx_get_stream_properties() for details.
> >>
> >> You're wrong. In xsdirx_get_stream_properties() you set the format
> >> information.
> >> But you can't just change that: if the video resolution changes, then that
> means
> >> that userspace needs to be informed that it has changed at the source, it has
> to
> >> find and set the new timings, update the formats, possibly reallocate
> memory
> >> for
> >> the buffers, update other parts of the video pipeline with the new resolution
> >> etc.
> >>
> >> The one thing you cannot do is just pass on the new resolution and hope
> that
> >> the
> >> video pipeline can handle it all.
> >>
> >> The right sequence of events is:
> >>
> >> 1) When a change is detected at the source the driver sends the
> >> SOURCE_CHANGE
> >> event and either stops transmitting to the video pipeline or keeps sending
> the
> >> old resolution (some devices have a freewheeling mode where they can do
> >> that).
> >>
> >> 2) Userspace sees the event, calls QUERY_DV_TIMINGS to find a new timings
> (if
> >> any), usually stops streaming, and calls S_DV_TIMINGS to set the detected
> >> timings:
> >> at that point the driver can configure the output towards the video pipeline
> >> with
> >> the new timings. Userspace reallocates buffers and resumes streaming with
> the
> >> new
> >> resolution.
> >>
> >
> > Thanks for the explanation!
> >
> > I will remove the extraneous video unlock event and stop the streaming when
> video lock / unlock interrupt occurs.
> > I will also implement the g_input_status() to return V4L2_IN_ST_NO_SYNC |
> V4L2_IN_ST_NO_SIGNAL in case video is unlocked.
> >
> > My assumption is that on SOURCE_CHANGE event, application can stop the
> pipeline and then
> > call the G_FORMAT and G_FRAME_INTERVAL to get new frame size, type
> (progressive / interlaced) and frame rate.
> > Is this assumption correct?
> 
> No :-)
> 

Good to have that cleared. :-D

> After SOURCE_CHANGE is received an application calls QUERY_DV_TIMINGS. If
> that
> returns valid timings, then the application calls S_DV_TIMINGS with the
> detected timings. The driver will now update the format, frame interval, etc.
> according to the new timings. And the application can use that to reconfigure
> the video pipeline.
> 
> >
> > Is it mandatory to implement QUERY_DV_TIMINGS with SOURCE_CHANGE
> event?
> 
> Yes.
> 

Thanks again for clarifying this. 

> >
> > I also don't see any V4L2 framework supported events for overflow and
> underflow.
> > Is it ok to keep these or should they be removed too?
> 
> under/overflow of what? Internal fifos? You can keep the custom events for
> that.
>

Yep these are custom events for internal fifos. I will keep them.

Regards
Vishal Sagar

> Regards,
> 
> 	Hans
> 
> >
> > Regards
> >
> > Vishal Sagar
> >
> >> Note that G_DV_TIMINGS returns the last configured timings, not the
> detected
> >> timings: only QUERY_DV_TIMINGS does that.
> >>
> >> In other words: userspace has to retain control of the full pipeline.
> >>
> >> Regards,
> >>
> >> 	Hans
> >>
> >>>
> >>>> Some of the controls you add in this driver can likely be dropped.
> Especially
> >>>> those controls that are not specific to the Xilinx implementation but are
> >>>> generic for any SDI receiver, should be looked at closely: those are
> >>>> candidates for becoming standard controls.
> >>>
> >>> I don't know how other SDI Receiver devices function.
> >>> So I am assuming all these controls are Xilinx specific implementations.
> >>>
> >>>>
> >>>> But the documentation above is simply insufficient for me to tell what is
> >>>> SDI specific and what is implementation specific.
> >>>>
> >>>
> >>> I will add more documentation for these controls.
> >>>
> >>>> Also, I'm no SDI expert, certainly not for the UHD-SDI.
> >>>>
> >>>> Regards,
> >>>>
> >>>>         Hans
> >>>
> >>> Regards
> >>> Vishal Sagar
> >>>
> >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem
  2019-06-04 13:55   ` Vishal Sagar
@ 2019-07-08 22:50     ` Rob Herring
  -1 siblings, 0 replies; 41+ messages in thread
From: Rob Herring @ 2019-07-08 22:50 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Mark Rutland, linux-kernel, linux-media, linux-arm-kernel,
	devicetree, Dinesh Kumar, Sandip Kothari

On Tue, Jun 04, 2019 at 07:25:55PM +0530, Vishal Sagar wrote:
> Add bindings documentation for Xilinx UHD-SDI Receiver Subsystem.
> 
> The Xilinx UHD-SDI Receiver Subsystem consists of SMPTE UHD-SDI (RX) IP
> core, an SDI RX to Video Bridge IP core to convert SDI video to native
> video and a Video In to AXI4-Stream IP core to convert native video to
> AXI4-Stream.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  .../bindings/media/xilinx/xlnx,sdirxss.txt         | 80 ++++++++++++++++++++++
>  1 file changed, 80 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> new file mode 100644
> index 0000000..8445bee
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> @@ -0,0 +1,80 @@
> +
> +Xilinx SMPTE UHD-SDI Receiver Subsystem Device Tree Bindings
> +------------------------------------------------------------
> +
> +The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create systems
> +based on SMPTE SDI protocols. It receives unaligned native SDI streams from
> +the SDI GT PHY and outputs an AXI4-Stream video stream, native video, or
> +native SDI using Xilinx transceivers as the physical layer.
> +
> +The subsystem consists of
> +1 - SMPTE UHD-SDI Rx
> +2 - SDI Rx to Native Video Bridge
> +3 - Video In to AXI4-Stream Bridge
> +
> +The subsystem can capture SDI streams in utpo 12G mode and output a dual pixel
> +per clock YUV 422 or 420 10 bits per component AXI4-Stream.
> +
> +Required properties:
> +--------------------
> +- compatible: Must contain "xlnx,v-smpte-uhdsdi-rx-ss"

Only one version?

> +- reg: Physical base address and length of the registers set for the device.
> +- interrupts: Contains the interrupt line number.
> +- clocks: List of phandles to AXI4-Lite clock, core clock to SMPTE UHD-SDI Rx
> +  and Video clocks.
> +- clock-names: Must contain "s_axi_aclk", "sdi_rx_clk" and "video_out_clk" in
> +  the same order as clocks listed in clocks property.
> +- xlnx,line-rate: The maximum mode supported by the design. Possible values are
> +  are as below -
> +  12G_SDI_8DS	- 12G mode
> +  6G_SDI	-  6G mode
> +  3G_SDI	-  3G mode

I don't think a string is a good fit here. Using 3, 6, 12 with a defined 
unit as defined in property-units.txt is preferred. Register field values are 
okay for vendor specific stuff, too.

> +
> +Optional properties:
> +--------------------
> +- xlnx,include-edh: This is present when the Error Detection and Handling
> +  processor is enabled in design.
> +
> +Ports
> +-----
> +The device node shall contain one 'port' child node as defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Generally the SDI port is connected to a device like SDI Broadcast camera which
> +is independently controlled. Hence port@0 is a source port which can be
> +connected to downstream IP which can work with AXI4 Stream data.
> +
> +Required port properties:
> +-------------------------
> +- reg: 0 - for source port.
> +
> +- xlnx,video-format: This can be XVIP_VF_YUV_422 or XVIP_VF_YUV_420.
> +- xlnx,video-width: This is should be 10.

Don't we have standard properties for these?

I assume width here is really part of the pixel or bus interface format 
rather than physical signals? We typically only have a separate property 
for the latter case.

> +
> +Example:
> +		v_smpte_uhdsdi_rx_ss: v_smpte_uhdsdi_rx_ss@80000000 {

s/_/-/ in the node name. Node names are supposed to be generic when 
possible, but I don't have a suggestion here.

> +			compatible = "xlnx,v-smpte-uhdsdi-rx-ss";
> +			interrupt-parent = <&gic>;
> +			interrupts = <0 89 4>;
> +			reg = <0x0 0x80000000 0x0 0x10000>;
> +			xlnx,include-edh;
> +			xlnx,line-rate = "12G_SDI_8DS";
> +			clocks = <&clk_1>, <&si570_1>, <&clk_2>;
> +			clock-names = "s_axi_aclk", "sdi_rx_clk", "video_out_clk";
> +
> +			ports {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +
> +				port@0 {
> +					reg = <0>;
> +
> +					xlnx,video-format = <XVIP_VF_YUV_422>;
> +					xlnx,video-width = <10>;
> +
> +					sdirx_out: endpoint {
> +						remote-endpoint = <&vcap_sdirx_in>;
> +					};
> +				};
> +			};
> +		};
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem
@ 2019-07-08 22:50     ` Rob Herring
  0 siblings, 0 replies; 41+ messages in thread
From: Rob Herring @ 2019-07-08 22:50 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Mark Rutland, devicetree, Dinesh Kumar, Hyun Kwon,
	Sandip Kothari, linux-kernel, Michal Simek, Laurent Pinchart,
	Mauro Carvalho Chehab, linux-arm-kernel, linux-media

On Tue, Jun 04, 2019 at 07:25:55PM +0530, Vishal Sagar wrote:
> Add bindings documentation for Xilinx UHD-SDI Receiver Subsystem.
> 
> The Xilinx UHD-SDI Receiver Subsystem consists of SMPTE UHD-SDI (RX) IP
> core, an SDI RX to Video Bridge IP core to convert SDI video to native
> video and a Video In to AXI4-Stream IP core to convert native video to
> AXI4-Stream.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  .../bindings/media/xilinx/xlnx,sdirxss.txt         | 80 ++++++++++++++++++++++
>  1 file changed, 80 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> new file mode 100644
> index 0000000..8445bee
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> @@ -0,0 +1,80 @@
> +
> +Xilinx SMPTE UHD-SDI Receiver Subsystem Device Tree Bindings
> +------------------------------------------------------------
> +
> +The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create systems
> +based on SMPTE SDI protocols. It receives unaligned native SDI streams from
> +the SDI GT PHY and outputs an AXI4-Stream video stream, native video, or
> +native SDI using Xilinx transceivers as the physical layer.
> +
> +The subsystem consists of
> +1 - SMPTE UHD-SDI Rx
> +2 - SDI Rx to Native Video Bridge
> +3 - Video In to AXI4-Stream Bridge
> +
> +The subsystem can capture SDI streams in utpo 12G mode and output a dual pixel
> +per clock YUV 422 or 420 10 bits per component AXI4-Stream.
> +
> +Required properties:
> +--------------------
> +- compatible: Must contain "xlnx,v-smpte-uhdsdi-rx-ss"

Only one version?

> +- reg: Physical base address and length of the registers set for the device.
> +- interrupts: Contains the interrupt line number.
> +- clocks: List of phandles to AXI4-Lite clock, core clock to SMPTE UHD-SDI Rx
> +  and Video clocks.
> +- clock-names: Must contain "s_axi_aclk", "sdi_rx_clk" and "video_out_clk" in
> +  the same order as clocks listed in clocks property.
> +- xlnx,line-rate: The maximum mode supported by the design. Possible values are
> +  are as below -
> +  12G_SDI_8DS	- 12G mode
> +  6G_SDI	-  6G mode
> +  3G_SDI	-  3G mode

I don't think a string is a good fit here. Using 3, 6, 12 with a defined 
unit as defined in property-units.txt is preferred. Register field values are 
okay for vendor specific stuff, too.

> +
> +Optional properties:
> +--------------------
> +- xlnx,include-edh: This is present when the Error Detection and Handling
> +  processor is enabled in design.
> +
> +Ports
> +-----
> +The device node shall contain one 'port' child node as defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Generally the SDI port is connected to a device like SDI Broadcast camera which
> +is independently controlled. Hence port@0 is a source port which can be
> +connected to downstream IP which can work with AXI4 Stream data.
> +
> +Required port properties:
> +-------------------------
> +- reg: 0 - for source port.
> +
> +- xlnx,video-format: This can be XVIP_VF_YUV_422 or XVIP_VF_YUV_420.
> +- xlnx,video-width: This is should be 10.

Don't we have standard properties for these?

I assume width here is really part of the pixel or bus interface format 
rather than physical signals? We typically only have a separate property 
for the latter case.

> +
> +Example:
> +		v_smpte_uhdsdi_rx_ss: v_smpte_uhdsdi_rx_ss@80000000 {

s/_/-/ in the node name. Node names are supposed to be generic when 
possible, but I don't have a suggestion here.

> +			compatible = "xlnx,v-smpte-uhdsdi-rx-ss";
> +			interrupt-parent = <&gic>;
> +			interrupts = <0 89 4>;
> +			reg = <0x0 0x80000000 0x0 0x10000>;
> +			xlnx,include-edh;
> +			xlnx,line-rate = "12G_SDI_8DS";
> +			clocks = <&clk_1>, <&si570_1>, <&clk_2>;
> +			clock-names = "s_axi_aclk", "sdi_rx_clk", "video_out_clk";
> +
> +			ports {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +
> +				port@0 {
> +					reg = <0>;
> +
> +					xlnx,video-format = <XVIP_VF_YUV_422>;
> +					xlnx,video-width = <10>;
> +
> +					sdirx_out: endpoint {
> +						remote-endpoint = <&vcap_sdirx_in>;
> +					};
> +				};
> +			};
> +		};
> -- 
> 1.8.3.1
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem
  2019-06-04 13:55   ` Vishal Sagar
@ 2019-10-02  7:22     ` Sakari Ailus
  -1 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2019-10-02  7:22 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari

Hi Vishal,

On Tue, Jun 04, 2019 at 07:25:55PM +0530, Vishal Sagar wrote:
> Add bindings documentation for Xilinx UHD-SDI Receiver Subsystem.
> 
> The Xilinx UHD-SDI Receiver Subsystem consists of SMPTE UHD-SDI (RX) IP
> core, an SDI RX to Video Bridge IP core to convert SDI video to native
> video and a Video In to AXI4-Stream IP core to convert native video to
> AXI4-Stream.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  .../bindings/media/xilinx/xlnx,sdirxss.txt         | 80 ++++++++++++++++++++++
>  1 file changed, 80 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> new file mode 100644
> index 0000000..8445bee
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> @@ -0,0 +1,80 @@
> +
> +Xilinx SMPTE UHD-SDI Receiver Subsystem Device Tree Bindings
> +------------------------------------------------------------
> +
> +The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create systems
> +based on SMPTE SDI protocols. It receives unaligned native SDI streams from
> +the SDI GT PHY and outputs an AXI4-Stream video stream, native video, or
> +native SDI using Xilinx transceivers as the physical layer.
> +
> +The subsystem consists of
> +1 - SMPTE UHD-SDI Rx
> +2 - SDI Rx to Native Video Bridge
> +3 - Video In to AXI4-Stream Bridge
> +
> +The subsystem can capture SDI streams in utpo 12G mode and output a dual pixel
> +per clock YUV 422 or 420 10 bits per component AXI4-Stream.
> +
> +Required properties:
> +--------------------
> +- compatible: Must contain "xlnx,v-smpte-uhdsdi-rx-ss"
> +- reg: Physical base address and length of the registers set for the device.
> +- interrupts: Contains the interrupt line number.
> +- clocks: List of phandles to AXI4-Lite clock, core clock to SMPTE UHD-SDI Rx
> +  and Video clocks.
> +- clock-names: Must contain "s_axi_aclk", "sdi_rx_clk" and "video_out_clk" in
> +  the same order as clocks listed in clocks property.
> +- xlnx,line-rate: The maximum mode supported by the design. Possible values are
> +  are as below -
> +  12G_SDI_8DS	- 12G mode
> +  6G_SDI	-  6G mode
> +  3G_SDI	-  3G mode

Is this specific to a port?

I was wondering whether we should include this to video-interfaces.txt bus
type list, and V4L2 MBUS types. This way it could be also parsed by the
v4l2-fwnode framework.

Looking at the Wikipedia article, there are preceding standards, too, that
are referred to by a pair of letters:

<URL:https://en.wikipedia.org/wiki/Serial_digital_interface#Standards>

What does "DS" stand for?

> +
> +Optional properties:
> +--------------------
> +- xlnx,include-edh: This is present when the Error Detection and Handling
> +  processor is enabled in design.
> +
> +Ports
> +-----
> +The device node shall contain one 'port' child node as defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Generally the SDI port is connected to a device like SDI Broadcast camera which
> +is independently controlled. Hence port@0 is a source port which can be
> +connected to downstream IP which can work with AXI4 Stream data.
> +
> +Required port properties:
> +-------------------------
> +- reg: 0 - for source port.
> +
> +- xlnx,video-format: This can be XVIP_VF_YUV_422 or XVIP_VF_YUV_420.

Is this a property of the hardware?

> +- xlnx,video-width: This is should be 10.

This, too. If there's just one choice, is there a need for the property?

> +
> +Example:
> +		v_smpte_uhdsdi_rx_ss: v_smpte_uhdsdi_rx_ss@80000000 {
> +			compatible = "xlnx,v-smpte-uhdsdi-rx-ss";
> +			interrupt-parent = <&gic>;
> +			interrupts = <0 89 4>;
> +			reg = <0x0 0x80000000 0x0 0x10000>;
> +			xlnx,include-edh;
> +			xlnx,line-rate = "12G_SDI_8DS";
> +			clocks = <&clk_1>, <&si570_1>, <&clk_2>;
> +			clock-names = "s_axi_aclk", "sdi_rx_clk", "video_out_clk";
> +
> +			ports {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +
> +				port@0 {
> +					reg = <0>;
> +
> +					xlnx,video-format = <XVIP_VF_YUV_422>;
> +					xlnx,video-width = <10>;
> +
> +					sdirx_out: endpoint {
> +						remote-endpoint = <&vcap_sdirx_in>;
> +					};
> +				};
> +			};
> +		};

-- 
Regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem
@ 2019-10-02  7:22     ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2019-10-02  7:22 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Mark Rutland, devicetree, Dinesh Kumar, Hyun Kwon,
	Sandip Kothari, Michal Simek, linux-kernel, Rob Herring,
	Laurent Pinchart, Mauro Carvalho Chehab, linux-arm-kernel,
	linux-media

Hi Vishal,

On Tue, Jun 04, 2019 at 07:25:55PM +0530, Vishal Sagar wrote:
> Add bindings documentation for Xilinx UHD-SDI Receiver Subsystem.
> 
> The Xilinx UHD-SDI Receiver Subsystem consists of SMPTE UHD-SDI (RX) IP
> core, an SDI RX to Video Bridge IP core to convert SDI video to native
> video and a Video In to AXI4-Stream IP core to convert native video to
> AXI4-Stream.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  .../bindings/media/xilinx/xlnx,sdirxss.txt         | 80 ++++++++++++++++++++++
>  1 file changed, 80 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> new file mode 100644
> index 0000000..8445bee
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.txt
> @@ -0,0 +1,80 @@
> +
> +Xilinx SMPTE UHD-SDI Receiver Subsystem Device Tree Bindings
> +------------------------------------------------------------
> +
> +The SMPTE UHD-SDI Receiver (RX) Subsystem allows you to quickly create systems
> +based on SMPTE SDI protocols. It receives unaligned native SDI streams from
> +the SDI GT PHY and outputs an AXI4-Stream video stream, native video, or
> +native SDI using Xilinx transceivers as the physical layer.
> +
> +The subsystem consists of
> +1 - SMPTE UHD-SDI Rx
> +2 - SDI Rx to Native Video Bridge
> +3 - Video In to AXI4-Stream Bridge
> +
> +The subsystem can capture SDI streams in utpo 12G mode and output a dual pixel
> +per clock YUV 422 or 420 10 bits per component AXI4-Stream.
> +
> +Required properties:
> +--------------------
> +- compatible: Must contain "xlnx,v-smpte-uhdsdi-rx-ss"
> +- reg: Physical base address and length of the registers set for the device.
> +- interrupts: Contains the interrupt line number.
> +- clocks: List of phandles to AXI4-Lite clock, core clock to SMPTE UHD-SDI Rx
> +  and Video clocks.
> +- clock-names: Must contain "s_axi_aclk", "sdi_rx_clk" and "video_out_clk" in
> +  the same order as clocks listed in clocks property.
> +- xlnx,line-rate: The maximum mode supported by the design. Possible values are
> +  are as below -
> +  12G_SDI_8DS	- 12G mode
> +  6G_SDI	-  6G mode
> +  3G_SDI	-  3G mode

Is this specific to a port?

I was wondering whether we should include this to video-interfaces.txt bus
type list, and V4L2 MBUS types. This way it could be also parsed by the
v4l2-fwnode framework.

Looking at the Wikipedia article, there are preceding standards, too, that
are referred to by a pair of letters:

<URL:https://en.wikipedia.org/wiki/Serial_digital_interface#Standards>

What does "DS" stand for?

> +
> +Optional properties:
> +--------------------
> +- xlnx,include-edh: This is present when the Error Detection and Handling
> +  processor is enabled in design.
> +
> +Ports
> +-----
> +The device node shall contain one 'port' child node as defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Generally the SDI port is connected to a device like SDI Broadcast camera which
> +is independently controlled. Hence port@0 is a source port which can be
> +connected to downstream IP which can work with AXI4 Stream data.
> +
> +Required port properties:
> +-------------------------
> +- reg: 0 - for source port.
> +
> +- xlnx,video-format: This can be XVIP_VF_YUV_422 or XVIP_VF_YUV_420.

Is this a property of the hardware?

> +- xlnx,video-width: This is should be 10.

This, too. If there's just one choice, is there a need for the property?

> +
> +Example:
> +		v_smpte_uhdsdi_rx_ss: v_smpte_uhdsdi_rx_ss@80000000 {
> +			compatible = "xlnx,v-smpte-uhdsdi-rx-ss";
> +			interrupt-parent = <&gic>;
> +			interrupts = <0 89 4>;
> +			reg = <0x0 0x80000000 0x0 0x10000>;
> +			xlnx,include-edh;
> +			xlnx,line-rate = "12G_SDI_8DS";
> +			clocks = <&clk_1>, <&si570_1>, <&clk_2>;
> +			clock-names = "s_axi_aclk", "sdi_rx_clk", "video_out_clk";
> +
> +			ports {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +
> +				port@0 {
> +					reg = <0>;
> +
> +					xlnx,video-format = <XVIP_VF_YUV_422>;
> +					xlnx,video-width = <10>;
> +
> +					sdirx_out: endpoint {
> +						remote-endpoint = <&vcap_sdirx_in>;
> +					};
> +				};
> +			};
> +		};

-- 
Regards,

Sakari Ailus

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
  2019-06-04 13:55   ` Vishal Sagar
  (?)
@ 2019-10-02  8:04     ` Sakari Ailus
  -1 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2019-10-02  8:04 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Hyun Kwon, Laurent Pinchart, Mauro Carvalho Chehab, Michal Simek,
	Rob Herring, Mark Rutland, linux-kernel, linux-media,
	linux-arm-kernel, devicetree, Dinesh Kumar, Sandip Kothari

Hi Vishal,

On Tue, Jun 04, 2019 at 07:25:56PM +0530, Vishal Sagar wrote:
> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> streams from SDI sources like SDI broadcast equipment like cameras and
> mixers. This block outputs either native SDI, native video or
> AXI4-Stream compliant data stream for further processing. Please refer
> to PG290 for details.
> 
> The driver is used to configure the IP to add framer, search for
> specific modes, get the detected mode, stream parameters, errors, etc.
> It also generates events for video lock/unlock, bridge over/under flow.
> 
> The driver supports only 10 bpc YUV 422 media bus format. It also
> decodes the stream parameters based on the ST352 packet embedded in the
> stream. In case the ST352 packet isn't present in the stream, the core's
> detected properties are used to set stream properties.
> 
> The driver currently supports only the AXI4-Stream configuration.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig          |   11 +
>  drivers/media/platform/xilinx/Makefile         |    1 +
>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>  6 files changed, 1960 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
>  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index cd1a0fd..0c68caa 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
>  	  Bridge. The driver is used to set the number of active lanes and
>  	  get short packet data.
>  
> +config VIDEO_XILINX_SDIRXSS
> +	tristate "Xilinx UHD SDI Rx Subsystem"
> +	help
> +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> +	  based driver that takes input from a SDI source like SDI camera and
> +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> +	  AXI4-Stream bridge. The driver is used to set different stream
> +	  detection modes and identify stream properties to properly configure
> +	  downstream.
> +
>  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 6119a34..223f2ea 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> new file mode 100644
> index 0000000..ba2d9d0
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,1846 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.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/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-sdirxss.h>
> +#include <linux/xilinx-v4l2-events.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"
> +
> +/*
> + * SDI Rx register map, bitmask and offsets
> + */
> +#define XSDIRX_RST_CTRL_REG		0x00
> +#define XSDIRX_MDL_CTRL_REG		0x04
> +#define XSDIRX_GLBL_IER_REG		0x0C
> +#define XSDIRX_ISR_REG			0x10
> +#define XSDIRX_IER_REG			0x14
> +#define XSDIRX_ST352_VALID_REG		0x18
> +#define XSDIRX_ST352_DS1_REG		0x1C
> +#define XSDIRX_ST352_DS3_REG		0x20
> +#define XSDIRX_ST352_DS5_REG		0x24
> +#define XSDIRX_ST352_DS7_REG		0x28
> +#define XSDIRX_ST352_DS9_REG		0x2C
> +#define XSDIRX_ST352_DS11_REG		0x30
> +#define XSDIRX_ST352_DS13_REG		0x34
> +#define XSDIRX_ST352_DS15_REG		0x38
> +#define XSDIRX_VERSION_REG		0x3C
> +#define XSDIRX_SS_CONFIG_REG		0x40
> +#define XSDIRX_MODE_DET_STAT_REG	0x44
> +#define XSDIRX_TS_DET_STAT_REG		0x48
> +#define XSDIRX_EDH_STAT_REG		0x4C
> +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> +#define XSDIRX_EDH_ERRCNT_REG		0x54
> +#define XSDIRX_CRC_ERRCNT_REG		0x58
> +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> +#define XSDIRX_SB_RX_STS_REG		0x60
> +
> +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
> +
> +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
> +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> +
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
> +
> +#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
> +
> +#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
> +#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
> +#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
> +#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
> +
> +#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
> +				XSDIRX_INTR_VIDUNLOCK_MASK |\
> +				XSDIRX_INTR_OVERFLOW_MASK |\
> +				XSDIRX_INTR_UNDERFLOW_MASK)
> +
> +#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
> +#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
> +#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
> +#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
> +#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
> +#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
> +#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
> +#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
> +
> +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
> +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
> +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
> +
> +#define XSDIRX_ACTIVE_STREAMS_1		0x0
> +#define XSDIRX_ACTIVE_STREAMS_2		0x1
> +#define XSDIRX_ACTIVE_STREAMS_4		0x2
> +#define XSDIRX_ACTIVE_STREAMS_8		0x3
> +#define XSDIRX_ACTIVE_STREAMS_16	0x4
> +
> +#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
> +#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
> +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
> +#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
> +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
> +#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
> +#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
> +
> +#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
> +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
> +#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
> +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
> +#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
> +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
> +#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
> +#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
> +#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
> +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
> +#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
> +
> +#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
> +#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
> +#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
> +#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
> +#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
> +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
> +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
> +
> +#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
> +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
> +#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
> +#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
> +#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
> +#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
> +
> +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
> +
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
> +
> +/* Number of media pads */
> +#define XSDIRX_MEDIA_PADS	(1)
> +
> +#define XSDIRX_DEFAULT_WIDTH	(1920)
> +#define XSDIRX_DEFAULT_HEIGHT	(1080)
> +
> +#define XSDIRX_MAX_STR_LENGTH	16
> +
> +#define XSDIRXSS_SDI_STD_3G		0
> +#define XSDIRXSS_SDI_STD_6G		1
> +#define XSDIRXSS_SDI_STD_12G_8DS	2
> +
> +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
> +
> +#define XSDIRX_MODE_HD_MASK	0x0
> +#define XSDIRX_MODE_SD_MASK	0x1
> +#define XSDIRX_MODE_3G_MASK	0x2
> +#define XSDIRX_MODE_6G_MASK	0x4
> +#define XSDIRX_MODE_12GI_MASK	0x5
> +#define XSDIRX_MODE_12GF_MASK	0x6
> +
> +/*
> + * Maximum number of events per file handle.
> + */
> +#define XSDIRX_MAX_EVENTS	(128)
> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE_MASK	0xFF
> +#define XST352_PAYLOAD_BYTE1_SHIFT	0
> +#define XST352_PAYLOAD_BYTE2_SHIFT	8
> +#define XST352_PAYLOAD_BYTE3_SHIFT	16
> +#define XST352_PAYLOAD_BYTE4_SHIFT	24
> +
> +#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
> +#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
> +#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
> +#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
> +#define XST352_BYTE1_ST372_DL_3GB		0x8A
> +#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
> +#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
> +#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
> +#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
> +#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
> +
> +#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
> +#define XST352_BYTE2_TS_TYPE_OFFSET		15
> +#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
> +#define XST352_BYTE2_PIC_TYPE_OFFSET		14
> +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
> +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
> +
> +#define XST352_BYTE2_FPS_MASK			0xF
> +#define XST352_BYTE2_FPS_SHIFT			8
> +#define XST352_BYTE2_FPS_24F			0x2
> +#define XST352_BYTE2_FPS_24			0x3
> +#define XST352_BYTE2_FPS_48F			0x4
> +#define XST352_BYTE2_FPS_25			0x5
> +#define XST352_BYTE2_FPS_30F			0x6
> +#define XST352_BYTE2_FPS_30			0x7
> +#define XST352_BYTE2_FPS_48			0x8
> +#define XST352_BYTE2_FPS_50			0x9
> +#define XST352_BYTE2_FPS_60F			0xA
> +#define XST352_BYTE2_FPS_60			0xB
> +/* Table 4 ST 2081-10:2015 */
> +#define XST352_BYTE2_FPS_96			0xC
> +#define XST352_BYTE2_FPS_100			0xD
> +#define XST352_BYTE2_FPS_120			0xE
> +#define XST352_BYTE2_FPS_120F			0xF
> +
> +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
> +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
> +
> +#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
> +#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
> +#define XST352_BYTE3_COLOR_FORMAT_422		0x0
> +#define XST352_BYTE3_COLOR_FORMAT_420		0x3
> +
> +/**
> + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
> + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
> + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> + */
> +enum sdi_family_enc {
> +	XSDIRX_SMPTE_ST_274	= 0,
> +	XSDIRX_SMPTE_ST_296	= 1,
> +	XSDIRX_SMPTE_ST_2048_2	= 2,
> +	XSDIRX_SMPTE_ST_295	= 3,
> +	XSDIRX_NTSC		= 8,
> +	XSDIRX_PAL		= 9,
> +	XSDIRX_TS_UNKNOWN	= 15
> +};
> +
> +/**
> + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @include_edh: EDH processor presence
> + * @mode: 3G/6G/12G mode
> + * @axi_clk: Axi lite interface clock
> + * @sdirx_clk: SDI Rx GT clock
> + * @vidout_clk: Video clock
> + */
> +struct xsdirxss_core {
> +	struct device *dev;
> +	void __iomem *iomem;
> +	int irq;
> +	bool include_edh;
> +	int mode;
> +	struct clk *axi_clk;
> +	struct clk *sdirx_clk;
> +	struct clk *vidout_clk;
> +};
> +
> +/**
> + * struct xsdirxss_state - SDI Rx Subsystem device structure
> + * @core: Core structure for MIPI SDI Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @event: Holds the video unlock event
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @vip_format: format information corresponding to the active format
> + * @pads: media pads
> + * @streaming: Flag for storing streaming state
> + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xsdirxss_state {
> +	struct xsdirxss_core core;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_event event;
> +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> +	struct v4l2_mbus_framefmt default_format;
> +	struct v4l2_fract frame_interval;
> +	const struct xvip_video_format *vip_format;
> +	struct media_pad pads[XSDIRX_MEDIA_PADS];
> +	bool streaming;
> +	bool vidlocked;
> +	bool ts_is_interlaced;
> +};
> +
> +static inline struct xsdirxss_state *
> +to_xsdirxssstate(struct v4l2_subdev *subdev)
> +{
> +	return container_of(subdev, struct xsdirxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> +{
> +	return ioread32(xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> +				  u32 value)
> +{
> +	iowrite32(value, xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 clr)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> +}
> +
> +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 set)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> +}
> +
> +static void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static void xsdirx_core_enable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> +{
> +	u32 i, val;
> +
> +	mask &= XSDIRX_DETECT_ALL_MODES;
> +	if (!mask) {
> +		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> +
> +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> +
> +	if (hweight16(mask) > 1) {
> +		/* Multi mode detection as more than 1 bit set in mask */
> +		dev_dbg(core->dev, "Detect multiple modes\n");
> +		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> +			switch (mask & (1 << i)) {
> +			case BIT(XSDIRX_MODE_SD_OFFSET):

There's no really a need for the loop.

> +				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_HD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_3G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_6G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GI_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GF_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +				break;
> +			}
> +		}
> +		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> +	} else {
> +		/* Fixed Mode */
> +		u32 forced_mode_mask = 0;
> +
> +		dev_dbg(core->dev, "Detect fixed mode\n");
> +
> +		/* Find offset of first bit set */
> +		switch (__ffs(mask)) {
> +		case XSDIRX_MODE_SD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_SD_MASK;
> +			break;
> +		case XSDIRX_MODE_HD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_HD_MASK;
> +			break;
> +		case XSDIRX_MODE_3G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_3G_MASK;
> +			break;
> +		case XSDIRX_MODE_6G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_6G_MASK;
> +			break;
> +		case XSDIRX_MODE_12GI_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> +			break;
> +		case XSDIRX_MODE_12GF_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> +			break;
> +		}
> +		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> +			forced_mode_mask);
> +		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> +	}
> +
> +	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> +		val);
> +	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> +
> +	return 0;
> +}
> +
> +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
> +{
> +	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> +	val = enable & XSDIRX_EDH_ALLERR_MASK;
> +
> +	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> +{
> +	/*
> +	 * The video lock window is the amount of time for which the
> +	 * the mode and transport stream should be locked to get the
> +	 * video lock interrupt.
> +	 */
> +	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> +}
> +
> +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +}
> +
> +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}
> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +}
> +
> +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +}
> +
> +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
> +{
> +	/* The sdi to native bridge is followed by native to axis4 bridge */
> +	if (enable) {
> +		xsdirx_axis4_bridge_control(core, enable);
> +		xsdirx_vid_bridge_control(core, enable);
> +	} else {
> +		xsdirx_vid_bridge_control(core, enable);
> +		xsdirx_axis4_bridge_control(core, enable);
> +	}
> +}
> +
> +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> +{
> +	xsdirx_streamflow_control(core, false);
> +}
> +
> +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> +				   u32 framerate)
> +{
> +	switch (framerate) {
> +	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 25000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_48HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 50000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 60000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 60000;
> +		break;
> +	default:
> +		frame_interval->numerator = 1;
> +		frame_interval->denominator = 1;
> +	}
> +}
> +
> +/**
> + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> + * @state: pointer to driver state
> + *
> + * This function decodes the stream's ST352 payload (if available) to get
> + * stream properties like width, height, picture type (interlaced/progressive),
> + * etc.
> + *
> + * Return: 0 for success else errors
> + */
> +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> +{
> +	struct xsdirxss_core *core = &state->core;
> +	u32 mode, payload = 0, val, family, valid, tscan;
> +	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> +	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> +	struct v4l2_mbus_framefmt *format = &state->formats[0];
> +
> +	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +
> +	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> +		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
> +		return -EINVAL;
> +	}
> +
> +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> +				XST352_PAYLOAD_BYTE_MASK;
> +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> +				XST352_BYTE2_PIC_TYPE_OFFSET;
> +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> +				XST352_BYTE2_FPS_MASK;
> +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> +				XST352_BYTE2_TS_TYPE_OFFSET;
> +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> +	} else {
> +		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> +			mode);
> +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> +	}
> +
> +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> +	state->ts_is_interlaced = tscan ? false : true;
> +
> +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> +		state->ts_is_interlaced, family);
> +
> +	switch (mode) {
> +	case XSDIRX_MODE_HD_MASK:
> +		if (!valid) {
> +			/* No payload obtained */
> +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> +				framerate, tscan);
> +			/*
> +			 * NOTE : A progressive segmented frame pSF will be
> +			 * reported incorrectly as Interlaced as we rely on IP's
> +			 * transport scan locked bit.
> +			 */
> +			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
> +
> +			switch (framerate) {
> +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +				if (family == XSDIRX_SMPTE_ST_296) {
> +					format->width = 1280;
> +					format->height = 720;
> +					format->field = V4L2_FIELD_NONE;
> +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> +					format->width = 2048;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				} else {
> +					format->width = 1920;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				}
> +				break;
> +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +				if (family == XSDIRX_SMPTE_ST_274) {
> +					format->width = 1920;
> +					format->height = 1080;
> +				} else {
> +					format->width = 1280;
> +					format->height = 720;
> +				}
> +				format->field = V4L2_FIELD_NONE;
> +				break;
> +			default:
> +				format->width = 1920;
> +				format->height = 1080;
> +				format->field = V4L2_FIELD_NONE;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "Got the payload\n");
> +			switch (byte1) {
> +			case XST352_BYTE1_ST292_1x720L_1_5G:
> +				/* SMPTE ST 292-1 for 720 line payloads */
> +				format->width = 1280;
> +				format->height = 720;
> +				break;
> +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> +				/* SMPTE ST 292-1 for 1080 line payloads */
> +				format->height = 1080;
> +				if (active_luma)
> +					format->width = 2048;
> +				else
> +					format->width = 1920;
> +				break;
> +			default:
> +				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case XSDIRX_MODE_SD_MASK:
> +		format->field = V4L2_FIELD_ALTERNATE;
> +
> +		switch (family) {
> +		case XSDIRX_NTSC:
> +			format->width = 720;
> +			format->height = 486;
> +			break;
> +		case XSDIRX_PAL:
> +			format->width = 720;
> +			format->height = 576;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_3G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST425_2008_750L_3GB:
> +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x720L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->width = 1280;
> +			format->height = 720;
> +			break;
> +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> +			/* ST352 Table SMPTE 425-1 */
> +		case XST352_BYTE1_ST372_DL_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x1080L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->height = 1080;
> +			if (active_luma)
> +				format->width = 2048;
> +			else
> +				format->width = 1920;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_6G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> +			/* Dual link 6G */
> +		case XST352_BYTE1_ST2081_10_2160L_6G:
> +			/* Table 3 SMPTE ST 2081-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_12GI_MASK:
> +	case XSDIRX_MODE_12GF_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2082_10_2160L_12G:
> +			/* Section 4.3.1 SMPTE ST 2082-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		dev_err(core->dev, "Invalid Mode\n");
> +		return -EINVAL;
> +	}
> +
> +	if (valid) {
> +		if (pic_type)
> +			format->field = V4L2_FIELD_NONE;
> +		else
> +			format->field = V4L2_FIELD_ALTERNATE;
> +	}
> +
> +	if (format->field == V4L2_FIELD_ALTERNATE)
> +		format->height = format->height / 2;
> +
> +	switch (sampling) {
> +	case XST352_BYTE3_COLOR_FORMAT_422:
> +		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +		break;
> +	default:
> +		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> +		return -EINVAL;
> +	}
> +
> +	xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
> +		format->width, format->height, format->field, payload, val);
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		state->frame_interval.numerator,
> +		state->frame_interval.denominator);
> +	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> + * in the interrupt clear register. The interrupt status register is read only.
> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> +{
> +	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> +	struct xsdirxss_core *core = &state->core;
> +	u32 status;
> +
> +	status = xsdirxss_read(core, XSDIRX_ISR_REG);
> +	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> +		u32 val1, val2;
> +
> +		dev_dbg(core->dev, "video lock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> +
> +		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +
> +		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> +		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> +			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> +				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> +
> +			dev_dbg(core->dev, "mode & ts lock occurred\n");
> +
> +			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> +			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +
> +			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +
> +			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> +			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> +
> +			if (!xsdirx_get_stream_properties(state)) {
> +				memset(&state->event, 0, sizeof(state->event));
> +				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> +				state->event.u.src_change.changes =
> +					V4L2_EVENT_SRC_CH_RESOLUTION;
> +				v4l2_subdev_notify_event(&state->subdev,
> +							 &state->event);
> +
> +				state->vidlocked = true;
> +			} else {
> +				dev_err(core->dev, "Unable to get stream properties!\n");
> +				state->vidlocked = false;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "video unlock before video lock!\n");
> +			state->vidlocked = false;
> +		}
> +	}
> +
> +	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> +		dev_dbg(core->dev, "video unlock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> +		xsdirx_streamdowncb(core);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +
> +		state->vidlocked = false;
> +	}
> +
> +	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +
> +	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	int ret;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (sub->type) {
> +	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> +	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> +	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> +		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> +		break;
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		ret = v4l2_src_change_event_subscribe(fh, sub);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> +				      struct v4l2_fh *fh,
> +				      struct v4l2_event_subscription *sub)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> +	return v4l2_event_unsubscribe(fh, sub);
> +}
> +
> +/**
> + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> + * Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	int ret = 0;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +
> +	if (xsdirxss->streaming) {
> +		dev_err(core->dev, "Cannot set controls while streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	xsdirx_core_disable(core);
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_FRAMER:
> +		xsdirx_framer(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> +		xsdirx_setvidlockwindow(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> +		xsdirx_setedherrcnttrigger(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> +		if (ctrl->val) {
> +			if (core->mode == XSDIRXSS_SDI_STD_3G) {
> +				dev_dbg(core->dev, "Upto 3G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			if (core->mode == XSDIRXSS_SDI_STD_6G) {
> +				dev_dbg(core->dev, "Upto 6G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			ret = xsdirx_set_modedetect(core, ctrl->val);
> +		} else {
> +			dev_err(core->dev, "Select at least one mode!\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SS_EN_MASK);
> +		return -EINVAL;
> +	}
> +	xsdirx_core_enable(core);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	u32 val;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +		switch (val) {
> +		case XSDIRX_MODE_SD_MASK:
> +			ctrl->val = XSDIRX_MODE_SD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_HD_MASK:
> +			ctrl->val = XSDIRX_MODE_HD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_3G_MASK:
> +			ctrl->val = XSDIRX_MODE_3G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_6G_MASK:
> +			ctrl->val = XSDIRX_MODE_6G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GI_MASK:
> +			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GF_MASK:
> +			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> +			break;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_CRC:
> +		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> +		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		ctrl->val = xsdirxss->ts_is_interlaced;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> +		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> +		ctrl->val = 1 << val;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_IS_3GB:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> +		ctrl->val = val ? true : false;
> +		break;
> +	default:
> +		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx SDI Rx Subsystem
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	u32 data, i;
> +
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> +	for (i = 0; i < 0x28; i++) {
> +		data = xsdirxss_read(core, i * 4);
> +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> +			  i * 4, data);
> +	}
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> +	return 0;
> +}
> +
> +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, true);
> +}
> +
> +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, false);
> +}
> +
> +/**
> + * xsdirxss_g_frame_interval - Get the frame interval
> + * @sd: V4L2 Sub device
> + * @fi: Pointer to V4l2 Sub device frame interval structure
> + *
> + * This function is used to get the frame interval.
> + * The frame rate can be integral or fractional.
> + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
> + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fi->interval = xsdirxss->frame_interval;
> +
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		xsdirxss->frame_interval.numerator,
> +		xsdirxss->frame_interval.denominator);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 SDI Rx Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (enable) {
> +		if (!xsdirxss->vidlocked) {
> +			dev_dbg(core->dev, "Video is not locked\n");
> +			return -EINVAL;
> +		}
> +		if (xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Already streaming\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_start_stream(xsdirxss);
> +		xsdirxss->streaming = true;
> +		dev_dbg(core->dev, "Streaming started\n");
> +	} else {
> +		if (!xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Stopped streaming already\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_stop_stream(xsdirxss);
> +		xsdirxss->streaming = false;
> +		dev_dbg(core->dev, "Streaming stopped\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> +			  struct v4l2_subdev_pad_config *cfg,
> +				unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &xsdirxss->formats[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_format *fmt)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> +						 fmt->pad, fmt->which);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> +		fmt->format.width, fmt->format.height, fmt->format.field);
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *__format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	dev_dbg(xsdirxss->core.dev,
> +		"set width %d height %d code %d field %d colorspace %d\n",
> +		fmt->format.width, fmt->format.height,
> +		fmt->format.code, fmt->format.field,
> +		fmt->format.colorspace);
> +
> +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> +					     fmt->pad, fmt->which);
> +
> +	/* Currently reset the code to one fixed in hardware */
> +	/* TODO : Add checks for width height */
> +	fmt->format.code = __format->code;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 pad.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_open(struct v4l2_subdev *sd,
> +			 struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	*format = xsdirxss->default_format;

How about using the init_cfg op instead?

> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_close - Called on v4l2_close()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_close().
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_close(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_fh *fh)
> +{
> +	return 0;

No need to implement an empty close op.

> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xsdirxss_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> +	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> +	.s_ctrl	= xsdirxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> +		.name	= "SDI Rx : EDH Error Count Enable",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_EDH_ALLERR_MASK,
> +		.def	= 0,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> +		.name	= "SDI Rx : EDH Error Count",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> +		.name	= "SDI Rx : EDH Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
> +		.name	= "SDI Rx : Enable Framer",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.step	= 1,
> +		.def	= true,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> +		.name	= "SDI Rx : Video Lock Window",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> +		.name	= "SDI Rx : Modes search Mask",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_DETECT_ALL_MODES,
> +		.def	= XSDIRX_DETECT_ALL_MODES,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> +		.name	= "SDI Rx : Mode Detect Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= XSDIRX_MODE_SD_OFFSET,
> +		.max	= XSDIRX_MODE_12GF_OFFSET,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_CRC,
> +		.name	= "SDI Rx : CRC Error status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> +		.name	= "SDI Rx : TS is Interlaced",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> +		.name	= "SDI Rx : Active Streams",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 1,
> +		.max	= 16,
> +		.def	= 1,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
> +		.name	= "SDI Rx : Is 3GB",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> +	.log_status = xsdirxss_log_status,
> +	.subscribe_event = xsdirxss_subscribe_event,
> +	.unsubscribe_event = xsdirxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> +	.g_frame_interval = xsdirxss_g_frame_interval,
> +	.s_stream = xsdirxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> +	.get_fmt = xsdirxss_get_format,
> +	.set_fmt = xsdirxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xsdirxss_ops = {
> +	.core = &xsdirxss_core_ops,
> +	.video = &xsdirxss_video_ops,
> +	.pad = &xsdirxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> +	.open = xsdirxss_open,
> +	.close = xsdirxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> +	struct device_node *node = xsdirxss->core.dev->of_node;
> +	struct device_node *ports = NULL;
> +	struct device_node *port = NULL;
> +	unsigned int nports = 0;
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	int ret;
> +	const char *sdi_std;
> +
> +	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> +	dev_dbg(core->dev, "EDH property = %s\n",
> +		core->include_edh ? "Present" : "Absent");
> +
> +	ret = of_property_read_string(node, "xlnx,line-rate",
> +				      &sdi_std);
> +	if (ret < 0) {
> +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> +		return ret;
> +	}
> +
> +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_6G;
> +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_3G;
> +	} else {
> +		dev_err(core->dev, "Invalid Line Rate\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> +		core->mode);
> +
> +	ports = of_get_child_by_name(node, "ports");
> +	if (!ports)
> +		ports = node;
> +
> +	for_each_child_of_node(ports, port) {

Please use the graph API to parse the graph instead.

> +		const struct xvip_video_format *format;
> +		struct device_node *endpoint;
> +
> +		if (!port->name || of_node_cmp(port->name, "port"))
> +			continue;
> +
> +		format = xvip_of_get_format(port);
> +		if (IS_ERR(format)) {
> +			dev_err(core->dev, "invalid format in DT");
> +			return PTR_ERR(format);
> +		}
> +
> +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> +			format->vf_code, format->width, format->bpp);
> +
> +		if (format->vf_code != XVIP_VF_YUV_422 &&
> +		    format->vf_code != XVIP_VF_YUV_420) {
> +			dev_err(core->dev, "Incorrect UG934 video format set.\n");
> +			return -EINVAL;
> +		}
> +		xsdirxss->vip_format = format;
> +
> +		endpoint = of_get_next_child(port, NULL);
> +		if (!endpoint) {
> +			dev_err(core->dev, "No port at\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Count the number of ports. */
> +		nports++;
> +	}
> +
> +	if (nports != 1) {
> +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> +		return -EINVAL;
> +	}
> +
> +	/* Register interrupt handler */
> +	core->irq = irq_of_parse_and_map(node, 0);
> +
> +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> +	if (ret) {
> +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> +	struct v4l2_subdev *subdev;
> +	struct xsdirxss_state *xsdirxss;
> +	struct xsdirxss_core *core;
> +	struct resource *res;
> +	int ret;
> +	unsigned int num_ctrls, num_edh_ctrls = 0, i;
> +
> +	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> +	if (!xsdirxss)
> +		return -ENOMEM;
> +
> +	xsdirxss->core.dev = &pdev->dev;
> +	core = &xsdirxss->core;
> +
> +	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> +	if (IS_ERR(core->axi_clk)) {
> +		ret = PTR_ERR(core->axi_clk);
> +		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> +	if (IS_ERR(core->sdirx_clk)) {
> +		ret = PTR_ERR(core->sdirx_clk);
> +		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> +	if (IS_ERR(core->vidout_clk)) {
> +		ret = PTR_ERR(core->vidout_clk);
> +		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->axi_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->sdirx_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> +		goto rx_clk_err;
> +	}
> +
> +	ret = clk_prepare_enable(core->vidout_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> +		goto vidout_clk_err;
> +	}
> +
> +	ret = xsdirxss_parse_of(xsdirxss);
> +	if (ret < 0)
> +		goto clk_err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> +	if (IS_ERR(xsdirxss->core.iomem)) {
> +		ret = PTR_ERR(xsdirxss->core.iomem);
> +		goto clk_err;
> +	}
> +
> +	/* Reset the core */
> +	xsdirx_streamflow_control(core, false);
> +	xsdirx_core_disable(core);
> +	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_globalintr(core, true);
> +	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Initialize the default format */
> +	xsdirxss->default_format.code = xsdirxss->vip_format->code;
> +	xsdirxss->default_format.field = V4L2_FIELD_NONE;
> +	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> +	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> +	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> +	xsdirxss->formats[0] = xsdirxss->default_format;
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	subdev = &xsdirxss->subdev;
> +	v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> +	subdev->dev = &pdev->dev;
> +	subdev->internal_ops = &xsdirxss_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 = &xsdirxss_media_ops;
> +
> +	v4l2_set_subdevdata(subdev, xsdirxss);
> +
> +	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialise and register the controls */
> +	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> +	if (xsdirxss->core.include_edh)
> +		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> +
> +	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> +			       (num_ctrls + num_edh_ctrls));
> +
> +	for (i = 0; i < num_ctrls; i++) {
> +		struct v4l2_ctrl *ctrl;
> +
> +		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> +
> +		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +					    &xsdirxss_ctrls[i], NULL);
> +		if (!ctrl) {
> +			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +				xsdirxss_ctrls[i].name);
> +			goto error;
> +		}
> +	}
> +
> +	if (xsdirxss->core.include_edh) {
> +		for (i = 0; i < num_edh_ctrls; i++) {
> +			struct v4l2_ctrl *ctrl;
> +
> +			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +				i, xsdirxss_edh_ctrls[i].name,
> +				xsdirxss_edh_ctrls[i].id);
> +
> +			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +						    &xsdirxss_edh_ctrls[i],
> +						    NULL);
> +			if (!ctrl) {
> +				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +					xsdirxss_edh_ctrls[i].name);
> +				goto error;
> +			}
> +		}
> +	} else {
> +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");
> +	}
> +
> +	if (xsdirxss->ctrl_handler.error) {
> +		dev_err(&pdev->dev, "failed to add controls\n");
> +		ret = xsdirxss->ctrl_handler.error;
> +		goto error;
> +	}
> +
> +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> +
> +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to set controls\n");
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, xsdirxss);
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register subdev\n");
> +		goto error;
> +	}
> +
> +	xsdirxss->streaming = false;
> +
> +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> +
> +	xsdirx_core_enable(core);
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +
> +clk_err:
> +	clk_disable_unprepare(core->vidout_clk);
> +vidout_clk_err:
> +	clk_disable_unprepare(core->sdirx_clk);
> +rx_clk_err:
> +	clk_disable_unprepare(core->axi_clk);
> +	return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> +
> +static struct platform_driver xsdirxss_driver = {
> +	.driver = {
> +		.name		= "xilinx-sdirxss",
> +		.of_match_table	= xsdirxss_of_id_table,
> +	},
> +	.probe			= xsdirxss_probe,
> +	.remove			= xsdirxss_remove,
> +};
> +
> +module_platform_driver(xsdirxss_driver);
> +
> +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
> new file mode 100644
> index 0000000..983a43b
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx SDI Rx Subsystem mode and flag definitions.
> + *
> + * Copyright (C) 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * This enum is used to prepare the bitmask of modes to be detected
> + */
> +enum {
> +	XSDIRX_MODE_SD_OFFSET = 0,
> +	XSDIRX_MODE_HD_OFFSET,
> +	XSDIRX_MODE_3G_OFFSET,
> +	XSDIRX_MODE_6G_OFFSET,
> +	XSDIRX_MODE_12GI_OFFSET,
> +	XSDIRX_MODE_12GF_OFFSET,
> +	XSDIRX_MODE_NUM_SUPPORTED,
> +};
> +
> +#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
> +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GF_OFFSET))
> +
> +/*
> + * EDH Error Types
> + * ANC - Ancillary Data Packet Errors
> + * FF - Full Field Errors
> + * AP - Active Portion Errors
> + */
> +
> +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> +
> +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index f023623..4c68a10 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -83,4 +83,34 @@
>  /* Reset all event counters */
>  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
>  
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
> +
> +/* Framer Control */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
> +/* Video Lock Window Control */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
> +/* Mode search Control */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
> +/* Get Detected Mode control */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
> +/* Get CRC error status */
> +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
> +/* Get EDH error count control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
> +/* Get EDH status control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
> +/* Get Transport Interlaced status */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
> +/* Get Active Streams count */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
> +/* Is Mode 3GB */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
> +
>  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
> index 2aa357c..7b47595 100644
> --- a/include/uapi/linux/xilinx-v4l2-events.h
> +++ b/include/uapi/linux/xilinx-v4l2-events.h
> @@ -18,4 +18,13 @@
>  /* Stream Line Buffer full */
>  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
>  
> +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
> +/* Video Unlock event */
> +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> +/* Video in to AXI4 Stream core underflowed */
> +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> +/* Video in to AXI4 Stream core overflowed */
> +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> +
>  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> -- 
> 1.8.3.1

This must be old.

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-10-02  8:04     ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2019-10-02  8:04 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Mark Rutland, devicetree, Dinesh Kumar, Hyun Kwon,
	Sandip Kothari, Michal Simek, linux-kernel, Rob Herring,
	Laurent Pinchart, Mauro Carvalho Chehab, linux-arm-kernel,
	linux-media

Hi Vishal,

On Tue, Jun 04, 2019 at 07:25:56PM +0530, Vishal Sagar wrote:
> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> streams from SDI sources like SDI broadcast equipment like cameras and
> mixers. This block outputs either native SDI, native video or
> AXI4-Stream compliant data stream for further processing. Please refer
> to PG290 for details.
> 
> The driver is used to configure the IP to add framer, search for
> specific modes, get the detected mode, stream parameters, errors, etc.
> It also generates events for video lock/unlock, bridge over/under flow.
> 
> The driver supports only 10 bpc YUV 422 media bus format. It also
> decodes the stream parameters based on the ST352 packet embedded in the
> stream. In case the ST352 packet isn't present in the stream, the core's
> detected properties are used to set stream properties.
> 
> The driver currently supports only the AXI4-Stream configuration.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig          |   11 +
>  drivers/media/platform/xilinx/Makefile         |    1 +
>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>  6 files changed, 1960 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
>  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index cd1a0fd..0c68caa 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
>  	  Bridge. The driver is used to set the number of active lanes and
>  	  get short packet data.
>  
> +config VIDEO_XILINX_SDIRXSS
> +	tristate "Xilinx UHD SDI Rx Subsystem"
> +	help
> +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> +	  based driver that takes input from a SDI source like SDI camera and
> +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> +	  AXI4-Stream bridge. The driver is used to set different stream
> +	  detection modes and identify stream properties to properly configure
> +	  downstream.
> +
>  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 6119a34..223f2ea 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> new file mode 100644
> index 0000000..ba2d9d0
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,1846 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.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/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-sdirxss.h>
> +#include <linux/xilinx-v4l2-events.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"
> +
> +/*
> + * SDI Rx register map, bitmask and offsets
> + */
> +#define XSDIRX_RST_CTRL_REG		0x00
> +#define XSDIRX_MDL_CTRL_REG		0x04
> +#define XSDIRX_GLBL_IER_REG		0x0C
> +#define XSDIRX_ISR_REG			0x10
> +#define XSDIRX_IER_REG			0x14
> +#define XSDIRX_ST352_VALID_REG		0x18
> +#define XSDIRX_ST352_DS1_REG		0x1C
> +#define XSDIRX_ST352_DS3_REG		0x20
> +#define XSDIRX_ST352_DS5_REG		0x24
> +#define XSDIRX_ST352_DS7_REG		0x28
> +#define XSDIRX_ST352_DS9_REG		0x2C
> +#define XSDIRX_ST352_DS11_REG		0x30
> +#define XSDIRX_ST352_DS13_REG		0x34
> +#define XSDIRX_ST352_DS15_REG		0x38
> +#define XSDIRX_VERSION_REG		0x3C
> +#define XSDIRX_SS_CONFIG_REG		0x40
> +#define XSDIRX_MODE_DET_STAT_REG	0x44
> +#define XSDIRX_TS_DET_STAT_REG		0x48
> +#define XSDIRX_EDH_STAT_REG		0x4C
> +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> +#define XSDIRX_EDH_ERRCNT_REG		0x54
> +#define XSDIRX_CRC_ERRCNT_REG		0x58
> +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> +#define XSDIRX_SB_RX_STS_REG		0x60
> +
> +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
> +
> +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
> +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> +
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
> +
> +#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
> +
> +#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
> +#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
> +#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
> +#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
> +
> +#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
> +				XSDIRX_INTR_VIDUNLOCK_MASK |\
> +				XSDIRX_INTR_OVERFLOW_MASK |\
> +				XSDIRX_INTR_UNDERFLOW_MASK)
> +
> +#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
> +#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
> +#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
> +#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
> +#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
> +#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
> +#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
> +#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
> +
> +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
> +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
> +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
> +
> +#define XSDIRX_ACTIVE_STREAMS_1		0x0
> +#define XSDIRX_ACTIVE_STREAMS_2		0x1
> +#define XSDIRX_ACTIVE_STREAMS_4		0x2
> +#define XSDIRX_ACTIVE_STREAMS_8		0x3
> +#define XSDIRX_ACTIVE_STREAMS_16	0x4
> +
> +#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
> +#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
> +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
> +#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
> +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
> +#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
> +#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
> +
> +#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
> +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
> +#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
> +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
> +#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
> +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
> +#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
> +#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
> +#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
> +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
> +#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
> +
> +#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
> +#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
> +#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
> +#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
> +#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
> +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
> +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
> +
> +#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
> +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
> +#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
> +#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
> +#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
> +#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
> +
> +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
> +
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
> +
> +/* Number of media pads */
> +#define XSDIRX_MEDIA_PADS	(1)
> +
> +#define XSDIRX_DEFAULT_WIDTH	(1920)
> +#define XSDIRX_DEFAULT_HEIGHT	(1080)
> +
> +#define XSDIRX_MAX_STR_LENGTH	16
> +
> +#define XSDIRXSS_SDI_STD_3G		0
> +#define XSDIRXSS_SDI_STD_6G		1
> +#define XSDIRXSS_SDI_STD_12G_8DS	2
> +
> +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
> +
> +#define XSDIRX_MODE_HD_MASK	0x0
> +#define XSDIRX_MODE_SD_MASK	0x1
> +#define XSDIRX_MODE_3G_MASK	0x2
> +#define XSDIRX_MODE_6G_MASK	0x4
> +#define XSDIRX_MODE_12GI_MASK	0x5
> +#define XSDIRX_MODE_12GF_MASK	0x6
> +
> +/*
> + * Maximum number of events per file handle.
> + */
> +#define XSDIRX_MAX_EVENTS	(128)
> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE_MASK	0xFF
> +#define XST352_PAYLOAD_BYTE1_SHIFT	0
> +#define XST352_PAYLOAD_BYTE2_SHIFT	8
> +#define XST352_PAYLOAD_BYTE3_SHIFT	16
> +#define XST352_PAYLOAD_BYTE4_SHIFT	24
> +
> +#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
> +#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
> +#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
> +#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
> +#define XST352_BYTE1_ST372_DL_3GB		0x8A
> +#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
> +#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
> +#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
> +#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
> +#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
> +
> +#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
> +#define XST352_BYTE2_TS_TYPE_OFFSET		15
> +#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
> +#define XST352_BYTE2_PIC_TYPE_OFFSET		14
> +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
> +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
> +
> +#define XST352_BYTE2_FPS_MASK			0xF
> +#define XST352_BYTE2_FPS_SHIFT			8
> +#define XST352_BYTE2_FPS_24F			0x2
> +#define XST352_BYTE2_FPS_24			0x3
> +#define XST352_BYTE2_FPS_48F			0x4
> +#define XST352_BYTE2_FPS_25			0x5
> +#define XST352_BYTE2_FPS_30F			0x6
> +#define XST352_BYTE2_FPS_30			0x7
> +#define XST352_BYTE2_FPS_48			0x8
> +#define XST352_BYTE2_FPS_50			0x9
> +#define XST352_BYTE2_FPS_60F			0xA
> +#define XST352_BYTE2_FPS_60			0xB
> +/* Table 4 ST 2081-10:2015 */
> +#define XST352_BYTE2_FPS_96			0xC
> +#define XST352_BYTE2_FPS_100			0xD
> +#define XST352_BYTE2_FPS_120			0xE
> +#define XST352_BYTE2_FPS_120F			0xF
> +
> +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
> +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
> +
> +#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
> +#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
> +#define XST352_BYTE3_COLOR_FORMAT_422		0x0
> +#define XST352_BYTE3_COLOR_FORMAT_420		0x3
> +
> +/**
> + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
> + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
> + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> + */
> +enum sdi_family_enc {
> +	XSDIRX_SMPTE_ST_274	= 0,
> +	XSDIRX_SMPTE_ST_296	= 1,
> +	XSDIRX_SMPTE_ST_2048_2	= 2,
> +	XSDIRX_SMPTE_ST_295	= 3,
> +	XSDIRX_NTSC		= 8,
> +	XSDIRX_PAL		= 9,
> +	XSDIRX_TS_UNKNOWN	= 15
> +};
> +
> +/**
> + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @include_edh: EDH processor presence
> + * @mode: 3G/6G/12G mode
> + * @axi_clk: Axi lite interface clock
> + * @sdirx_clk: SDI Rx GT clock
> + * @vidout_clk: Video clock
> + */
> +struct xsdirxss_core {
> +	struct device *dev;
> +	void __iomem *iomem;
> +	int irq;
> +	bool include_edh;
> +	int mode;
> +	struct clk *axi_clk;
> +	struct clk *sdirx_clk;
> +	struct clk *vidout_clk;
> +};
> +
> +/**
> + * struct xsdirxss_state - SDI Rx Subsystem device structure
> + * @core: Core structure for MIPI SDI Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @event: Holds the video unlock event
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @vip_format: format information corresponding to the active format
> + * @pads: media pads
> + * @streaming: Flag for storing streaming state
> + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xsdirxss_state {
> +	struct xsdirxss_core core;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_event event;
> +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> +	struct v4l2_mbus_framefmt default_format;
> +	struct v4l2_fract frame_interval;
> +	const struct xvip_video_format *vip_format;
> +	struct media_pad pads[XSDIRX_MEDIA_PADS];
> +	bool streaming;
> +	bool vidlocked;
> +	bool ts_is_interlaced;
> +};
> +
> +static inline struct xsdirxss_state *
> +to_xsdirxssstate(struct v4l2_subdev *subdev)
> +{
> +	return container_of(subdev, struct xsdirxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> +{
> +	return ioread32(xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> +				  u32 value)
> +{
> +	iowrite32(value, xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 clr)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> +}
> +
> +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 set)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> +}
> +
> +static void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static void xsdirx_core_enable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> +{
> +	u32 i, val;
> +
> +	mask &= XSDIRX_DETECT_ALL_MODES;
> +	if (!mask) {
> +		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> +
> +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> +
> +	if (hweight16(mask) > 1) {
> +		/* Multi mode detection as more than 1 bit set in mask */
> +		dev_dbg(core->dev, "Detect multiple modes\n");
> +		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> +			switch (mask & (1 << i)) {
> +			case BIT(XSDIRX_MODE_SD_OFFSET):

There's no really a need for the loop.

> +				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_HD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_3G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_6G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GI_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GF_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +				break;
> +			}
> +		}
> +		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> +	} else {
> +		/* Fixed Mode */
> +		u32 forced_mode_mask = 0;
> +
> +		dev_dbg(core->dev, "Detect fixed mode\n");
> +
> +		/* Find offset of first bit set */
> +		switch (__ffs(mask)) {
> +		case XSDIRX_MODE_SD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_SD_MASK;
> +			break;
> +		case XSDIRX_MODE_HD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_HD_MASK;
> +			break;
> +		case XSDIRX_MODE_3G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_3G_MASK;
> +			break;
> +		case XSDIRX_MODE_6G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_6G_MASK;
> +			break;
> +		case XSDIRX_MODE_12GI_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> +			break;
> +		case XSDIRX_MODE_12GF_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> +			break;
> +		}
> +		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> +			forced_mode_mask);
> +		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> +	}
> +
> +	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> +		val);
> +	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> +
> +	return 0;
> +}
> +
> +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
> +{
> +	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> +	val = enable & XSDIRX_EDH_ALLERR_MASK;
> +
> +	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> +{
> +	/*
> +	 * The video lock window is the amount of time for which the
> +	 * the mode and transport stream should be locked to get the
> +	 * video lock interrupt.
> +	 */
> +	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> +}
> +
> +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +}
> +
> +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}
> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +}
> +
> +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +}
> +
> +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
> +{
> +	/* The sdi to native bridge is followed by native to axis4 bridge */
> +	if (enable) {
> +		xsdirx_axis4_bridge_control(core, enable);
> +		xsdirx_vid_bridge_control(core, enable);
> +	} else {
> +		xsdirx_vid_bridge_control(core, enable);
> +		xsdirx_axis4_bridge_control(core, enable);
> +	}
> +}
> +
> +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> +{
> +	xsdirx_streamflow_control(core, false);
> +}
> +
> +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> +				   u32 framerate)
> +{
> +	switch (framerate) {
> +	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 25000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_48HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 50000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 60000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 60000;
> +		break;
> +	default:
> +		frame_interval->numerator = 1;
> +		frame_interval->denominator = 1;
> +	}
> +}
> +
> +/**
> + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> + * @state: pointer to driver state
> + *
> + * This function decodes the stream's ST352 payload (if available) to get
> + * stream properties like width, height, picture type (interlaced/progressive),
> + * etc.
> + *
> + * Return: 0 for success else errors
> + */
> +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> +{
> +	struct xsdirxss_core *core = &state->core;
> +	u32 mode, payload = 0, val, family, valid, tscan;
> +	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> +	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> +	struct v4l2_mbus_framefmt *format = &state->formats[0];
> +
> +	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +
> +	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> +		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
> +		return -EINVAL;
> +	}
> +
> +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> +				XST352_PAYLOAD_BYTE_MASK;
> +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> +				XST352_BYTE2_PIC_TYPE_OFFSET;
> +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> +				XST352_BYTE2_FPS_MASK;
> +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> +				XST352_BYTE2_TS_TYPE_OFFSET;
> +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> +	} else {
> +		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> +			mode);
> +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> +	}
> +
> +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> +	state->ts_is_interlaced = tscan ? false : true;
> +
> +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> +		state->ts_is_interlaced, family);
> +
> +	switch (mode) {
> +	case XSDIRX_MODE_HD_MASK:
> +		if (!valid) {
> +			/* No payload obtained */
> +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> +				framerate, tscan);
> +			/*
> +			 * NOTE : A progressive segmented frame pSF will be
> +			 * reported incorrectly as Interlaced as we rely on IP's
> +			 * transport scan locked bit.
> +			 */
> +			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
> +
> +			switch (framerate) {
> +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +				if (family == XSDIRX_SMPTE_ST_296) {
> +					format->width = 1280;
> +					format->height = 720;
> +					format->field = V4L2_FIELD_NONE;
> +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> +					format->width = 2048;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				} else {
> +					format->width = 1920;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				}
> +				break;
> +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +				if (family == XSDIRX_SMPTE_ST_274) {
> +					format->width = 1920;
> +					format->height = 1080;
> +				} else {
> +					format->width = 1280;
> +					format->height = 720;
> +				}
> +				format->field = V4L2_FIELD_NONE;
> +				break;
> +			default:
> +				format->width = 1920;
> +				format->height = 1080;
> +				format->field = V4L2_FIELD_NONE;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "Got the payload\n");
> +			switch (byte1) {
> +			case XST352_BYTE1_ST292_1x720L_1_5G:
> +				/* SMPTE ST 292-1 for 720 line payloads */
> +				format->width = 1280;
> +				format->height = 720;
> +				break;
> +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> +				/* SMPTE ST 292-1 for 1080 line payloads */
> +				format->height = 1080;
> +				if (active_luma)
> +					format->width = 2048;
> +				else
> +					format->width = 1920;
> +				break;
> +			default:
> +				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case XSDIRX_MODE_SD_MASK:
> +		format->field = V4L2_FIELD_ALTERNATE;
> +
> +		switch (family) {
> +		case XSDIRX_NTSC:
> +			format->width = 720;
> +			format->height = 486;
> +			break;
> +		case XSDIRX_PAL:
> +			format->width = 720;
> +			format->height = 576;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_3G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST425_2008_750L_3GB:
> +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x720L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->width = 1280;
> +			format->height = 720;
> +			break;
> +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> +			/* ST352 Table SMPTE 425-1 */
> +		case XST352_BYTE1_ST372_DL_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x1080L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->height = 1080;
> +			if (active_luma)
> +				format->width = 2048;
> +			else
> +				format->width = 1920;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_6G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> +			/* Dual link 6G */
> +		case XST352_BYTE1_ST2081_10_2160L_6G:
> +			/* Table 3 SMPTE ST 2081-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_12GI_MASK:
> +	case XSDIRX_MODE_12GF_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2082_10_2160L_12G:
> +			/* Section 4.3.1 SMPTE ST 2082-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		dev_err(core->dev, "Invalid Mode\n");
> +		return -EINVAL;
> +	}
> +
> +	if (valid) {
> +		if (pic_type)
> +			format->field = V4L2_FIELD_NONE;
> +		else
> +			format->field = V4L2_FIELD_ALTERNATE;
> +	}
> +
> +	if (format->field == V4L2_FIELD_ALTERNATE)
> +		format->height = format->height / 2;
> +
> +	switch (sampling) {
> +	case XST352_BYTE3_COLOR_FORMAT_422:
> +		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +		break;
> +	default:
> +		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> +		return -EINVAL;
> +	}
> +
> +	xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
> +		format->width, format->height, format->field, payload, val);
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		state->frame_interval.numerator,
> +		state->frame_interval.denominator);
> +	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> + * in the interrupt clear register. The interrupt status register is read only.
> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> +{
> +	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> +	struct xsdirxss_core *core = &state->core;
> +	u32 status;
> +
> +	status = xsdirxss_read(core, XSDIRX_ISR_REG);
> +	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> +		u32 val1, val2;
> +
> +		dev_dbg(core->dev, "video lock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> +
> +		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +
> +		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> +		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> +			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> +				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> +
> +			dev_dbg(core->dev, "mode & ts lock occurred\n");
> +
> +			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> +			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +
> +			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +
> +			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> +			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> +
> +			if (!xsdirx_get_stream_properties(state)) {
> +				memset(&state->event, 0, sizeof(state->event));
> +				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> +				state->event.u.src_change.changes =
> +					V4L2_EVENT_SRC_CH_RESOLUTION;
> +				v4l2_subdev_notify_event(&state->subdev,
> +							 &state->event);
> +
> +				state->vidlocked = true;
> +			} else {
> +				dev_err(core->dev, "Unable to get stream properties!\n");
> +				state->vidlocked = false;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "video unlock before video lock!\n");
> +			state->vidlocked = false;
> +		}
> +	}
> +
> +	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> +		dev_dbg(core->dev, "video unlock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> +		xsdirx_streamdowncb(core);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +
> +		state->vidlocked = false;
> +	}
> +
> +	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +
> +	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	int ret;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (sub->type) {
> +	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> +	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> +	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> +		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> +		break;
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		ret = v4l2_src_change_event_subscribe(fh, sub);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> +				      struct v4l2_fh *fh,
> +				      struct v4l2_event_subscription *sub)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> +	return v4l2_event_unsubscribe(fh, sub);
> +}
> +
> +/**
> + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> + * Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	int ret = 0;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +
> +	if (xsdirxss->streaming) {
> +		dev_err(core->dev, "Cannot set controls while streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	xsdirx_core_disable(core);
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_FRAMER:
> +		xsdirx_framer(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> +		xsdirx_setvidlockwindow(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> +		xsdirx_setedherrcnttrigger(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> +		if (ctrl->val) {
> +			if (core->mode == XSDIRXSS_SDI_STD_3G) {
> +				dev_dbg(core->dev, "Upto 3G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			if (core->mode == XSDIRXSS_SDI_STD_6G) {
> +				dev_dbg(core->dev, "Upto 6G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			ret = xsdirx_set_modedetect(core, ctrl->val);
> +		} else {
> +			dev_err(core->dev, "Select at least one mode!\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SS_EN_MASK);
> +		return -EINVAL;
> +	}
> +	xsdirx_core_enable(core);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	u32 val;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +		switch (val) {
> +		case XSDIRX_MODE_SD_MASK:
> +			ctrl->val = XSDIRX_MODE_SD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_HD_MASK:
> +			ctrl->val = XSDIRX_MODE_HD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_3G_MASK:
> +			ctrl->val = XSDIRX_MODE_3G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_6G_MASK:
> +			ctrl->val = XSDIRX_MODE_6G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GI_MASK:
> +			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GF_MASK:
> +			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> +			break;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_CRC:
> +		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> +		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		ctrl->val = xsdirxss->ts_is_interlaced;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> +		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> +		ctrl->val = 1 << val;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_IS_3GB:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> +		ctrl->val = val ? true : false;
> +		break;
> +	default:
> +		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx SDI Rx Subsystem
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	u32 data, i;
> +
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> +	for (i = 0; i < 0x28; i++) {
> +		data = xsdirxss_read(core, i * 4);
> +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> +			  i * 4, data);
> +	}
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> +	return 0;
> +}
> +
> +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, true);
> +}
> +
> +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, false);
> +}
> +
> +/**
> + * xsdirxss_g_frame_interval - Get the frame interval
> + * @sd: V4L2 Sub device
> + * @fi: Pointer to V4l2 Sub device frame interval structure
> + *
> + * This function is used to get the frame interval.
> + * The frame rate can be integral or fractional.
> + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
> + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fi->interval = xsdirxss->frame_interval;
> +
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		xsdirxss->frame_interval.numerator,
> +		xsdirxss->frame_interval.denominator);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 SDI Rx Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (enable) {
> +		if (!xsdirxss->vidlocked) {
> +			dev_dbg(core->dev, "Video is not locked\n");
> +			return -EINVAL;
> +		}
> +		if (xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Already streaming\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_start_stream(xsdirxss);
> +		xsdirxss->streaming = true;
> +		dev_dbg(core->dev, "Streaming started\n");
> +	} else {
> +		if (!xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Stopped streaming already\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_stop_stream(xsdirxss);
> +		xsdirxss->streaming = false;
> +		dev_dbg(core->dev, "Streaming stopped\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> +			  struct v4l2_subdev_pad_config *cfg,
> +				unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &xsdirxss->formats[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_format *fmt)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> +						 fmt->pad, fmt->which);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> +		fmt->format.width, fmt->format.height, fmt->format.field);
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *__format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	dev_dbg(xsdirxss->core.dev,
> +		"set width %d height %d code %d field %d colorspace %d\n",
> +		fmt->format.width, fmt->format.height,
> +		fmt->format.code, fmt->format.field,
> +		fmt->format.colorspace);
> +
> +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> +					     fmt->pad, fmt->which);
> +
> +	/* Currently reset the code to one fixed in hardware */
> +	/* TODO : Add checks for width height */
> +	fmt->format.code = __format->code;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 pad.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_open(struct v4l2_subdev *sd,
> +			 struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	*format = xsdirxss->default_format;

How about using the init_cfg op instead?

> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_close - Called on v4l2_close()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_close().
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_close(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_fh *fh)
> +{
> +	return 0;

No need to implement an empty close op.

> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xsdirxss_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> +	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> +	.s_ctrl	= xsdirxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> +		.name	= "SDI Rx : EDH Error Count Enable",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_EDH_ALLERR_MASK,
> +		.def	= 0,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> +		.name	= "SDI Rx : EDH Error Count",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> +		.name	= "SDI Rx : EDH Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
> +		.name	= "SDI Rx : Enable Framer",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.step	= 1,
> +		.def	= true,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> +		.name	= "SDI Rx : Video Lock Window",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> +		.name	= "SDI Rx : Modes search Mask",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_DETECT_ALL_MODES,
> +		.def	= XSDIRX_DETECT_ALL_MODES,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> +		.name	= "SDI Rx : Mode Detect Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= XSDIRX_MODE_SD_OFFSET,
> +		.max	= XSDIRX_MODE_12GF_OFFSET,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_CRC,
> +		.name	= "SDI Rx : CRC Error status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> +		.name	= "SDI Rx : TS is Interlaced",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> +		.name	= "SDI Rx : Active Streams",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 1,
> +		.max	= 16,
> +		.def	= 1,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
> +		.name	= "SDI Rx : Is 3GB",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> +	.log_status = xsdirxss_log_status,
> +	.subscribe_event = xsdirxss_subscribe_event,
> +	.unsubscribe_event = xsdirxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> +	.g_frame_interval = xsdirxss_g_frame_interval,
> +	.s_stream = xsdirxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> +	.get_fmt = xsdirxss_get_format,
> +	.set_fmt = xsdirxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xsdirxss_ops = {
> +	.core = &xsdirxss_core_ops,
> +	.video = &xsdirxss_video_ops,
> +	.pad = &xsdirxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> +	.open = xsdirxss_open,
> +	.close = xsdirxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> +	struct device_node *node = xsdirxss->core.dev->of_node;
> +	struct device_node *ports = NULL;
> +	struct device_node *port = NULL;
> +	unsigned int nports = 0;
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	int ret;
> +	const char *sdi_std;
> +
> +	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> +	dev_dbg(core->dev, "EDH property = %s\n",
> +		core->include_edh ? "Present" : "Absent");
> +
> +	ret = of_property_read_string(node, "xlnx,line-rate",
> +				      &sdi_std);
> +	if (ret < 0) {
> +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> +		return ret;
> +	}
> +
> +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_6G;
> +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_3G;
> +	} else {
> +		dev_err(core->dev, "Invalid Line Rate\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> +		core->mode);
> +
> +	ports = of_get_child_by_name(node, "ports");
> +	if (!ports)
> +		ports = node;
> +
> +	for_each_child_of_node(ports, port) {

Please use the graph API to parse the graph instead.

> +		const struct xvip_video_format *format;
> +		struct device_node *endpoint;
> +
> +		if (!port->name || of_node_cmp(port->name, "port"))
> +			continue;
> +
> +		format = xvip_of_get_format(port);
> +		if (IS_ERR(format)) {
> +			dev_err(core->dev, "invalid format in DT");
> +			return PTR_ERR(format);
> +		}
> +
> +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> +			format->vf_code, format->width, format->bpp);
> +
> +		if (format->vf_code != XVIP_VF_YUV_422 &&
> +		    format->vf_code != XVIP_VF_YUV_420) {
> +			dev_err(core->dev, "Incorrect UG934 video format set.\n");
> +			return -EINVAL;
> +		}
> +		xsdirxss->vip_format = format;
> +
> +		endpoint = of_get_next_child(port, NULL);
> +		if (!endpoint) {
> +			dev_err(core->dev, "No port at\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Count the number of ports. */
> +		nports++;
> +	}
> +
> +	if (nports != 1) {
> +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> +		return -EINVAL;
> +	}
> +
> +	/* Register interrupt handler */
> +	core->irq = irq_of_parse_and_map(node, 0);
> +
> +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> +	if (ret) {
> +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> +	struct v4l2_subdev *subdev;
> +	struct xsdirxss_state *xsdirxss;
> +	struct xsdirxss_core *core;
> +	struct resource *res;
> +	int ret;
> +	unsigned int num_ctrls, num_edh_ctrls = 0, i;
> +
> +	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> +	if (!xsdirxss)
> +		return -ENOMEM;
> +
> +	xsdirxss->core.dev = &pdev->dev;
> +	core = &xsdirxss->core;
> +
> +	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> +	if (IS_ERR(core->axi_clk)) {
> +		ret = PTR_ERR(core->axi_clk);
> +		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> +	if (IS_ERR(core->sdirx_clk)) {
> +		ret = PTR_ERR(core->sdirx_clk);
> +		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> +	if (IS_ERR(core->vidout_clk)) {
> +		ret = PTR_ERR(core->vidout_clk);
> +		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->axi_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->sdirx_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> +		goto rx_clk_err;
> +	}
> +
> +	ret = clk_prepare_enable(core->vidout_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> +		goto vidout_clk_err;
> +	}
> +
> +	ret = xsdirxss_parse_of(xsdirxss);
> +	if (ret < 0)
> +		goto clk_err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> +	if (IS_ERR(xsdirxss->core.iomem)) {
> +		ret = PTR_ERR(xsdirxss->core.iomem);
> +		goto clk_err;
> +	}
> +
> +	/* Reset the core */
> +	xsdirx_streamflow_control(core, false);
> +	xsdirx_core_disable(core);
> +	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_globalintr(core, true);
> +	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Initialize the default format */
> +	xsdirxss->default_format.code = xsdirxss->vip_format->code;
> +	xsdirxss->default_format.field = V4L2_FIELD_NONE;
> +	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> +	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> +	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> +	xsdirxss->formats[0] = xsdirxss->default_format;
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	subdev = &xsdirxss->subdev;
> +	v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> +	subdev->dev = &pdev->dev;
> +	subdev->internal_ops = &xsdirxss_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 = &xsdirxss_media_ops;
> +
> +	v4l2_set_subdevdata(subdev, xsdirxss);
> +
> +	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialise and register the controls */
> +	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> +	if (xsdirxss->core.include_edh)
> +		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> +
> +	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> +			       (num_ctrls + num_edh_ctrls));
> +
> +	for (i = 0; i < num_ctrls; i++) {
> +		struct v4l2_ctrl *ctrl;
> +
> +		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> +
> +		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +					    &xsdirxss_ctrls[i], NULL);
> +		if (!ctrl) {
> +			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +				xsdirxss_ctrls[i].name);
> +			goto error;
> +		}
> +	}
> +
> +	if (xsdirxss->core.include_edh) {
> +		for (i = 0; i < num_edh_ctrls; i++) {
> +			struct v4l2_ctrl *ctrl;
> +
> +			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +				i, xsdirxss_edh_ctrls[i].name,
> +				xsdirxss_edh_ctrls[i].id);
> +
> +			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +						    &xsdirxss_edh_ctrls[i],
> +						    NULL);
> +			if (!ctrl) {
> +				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +					xsdirxss_edh_ctrls[i].name);
> +				goto error;
> +			}
> +		}
> +	} else {
> +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");
> +	}
> +
> +	if (xsdirxss->ctrl_handler.error) {
> +		dev_err(&pdev->dev, "failed to add controls\n");
> +		ret = xsdirxss->ctrl_handler.error;
> +		goto error;
> +	}
> +
> +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> +
> +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to set controls\n");
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, xsdirxss);
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register subdev\n");
> +		goto error;
> +	}
> +
> +	xsdirxss->streaming = false;
> +
> +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> +
> +	xsdirx_core_enable(core);
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +
> +clk_err:
> +	clk_disable_unprepare(core->vidout_clk);
> +vidout_clk_err:
> +	clk_disable_unprepare(core->sdirx_clk);
> +rx_clk_err:
> +	clk_disable_unprepare(core->axi_clk);
> +	return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> +
> +static struct platform_driver xsdirxss_driver = {
> +	.driver = {
> +		.name		= "xilinx-sdirxss",
> +		.of_match_table	= xsdirxss_of_id_table,
> +	},
> +	.probe			= xsdirxss_probe,
> +	.remove			= xsdirxss_remove,
> +};
> +
> +module_platform_driver(xsdirxss_driver);
> +
> +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
> new file mode 100644
> index 0000000..983a43b
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx SDI Rx Subsystem mode and flag definitions.
> + *
> + * Copyright (C) 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * This enum is used to prepare the bitmask of modes to be detected
> + */
> +enum {
> +	XSDIRX_MODE_SD_OFFSET = 0,
> +	XSDIRX_MODE_HD_OFFSET,
> +	XSDIRX_MODE_3G_OFFSET,
> +	XSDIRX_MODE_6G_OFFSET,
> +	XSDIRX_MODE_12GI_OFFSET,
> +	XSDIRX_MODE_12GF_OFFSET,
> +	XSDIRX_MODE_NUM_SUPPORTED,
> +};
> +
> +#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
> +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GF_OFFSET))
> +
> +/*
> + * EDH Error Types
> + * ANC - Ancillary Data Packet Errors
> + * FF - Full Field Errors
> + * AP - Active Portion Errors
> + */
> +
> +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> +
> +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index f023623..4c68a10 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -83,4 +83,34 @@
>  /* Reset all event counters */
>  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
>  
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
> +
> +/* Framer Control */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
> +/* Video Lock Window Control */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
> +/* Mode search Control */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
> +/* Get Detected Mode control */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
> +/* Get CRC error status */
> +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
> +/* Get EDH error count control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
> +/* Get EDH status control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
> +/* Get Transport Interlaced status */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
> +/* Get Active Streams count */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
> +/* Is Mode 3GB */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
> +
>  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
> index 2aa357c..7b47595 100644
> --- a/include/uapi/linux/xilinx-v4l2-events.h
> +++ b/include/uapi/linux/xilinx-v4l2-events.h
> @@ -18,4 +18,13 @@
>  /* Stream Line Buffer full */
>  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
>  
> +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
> +/* Video Unlock event */
> +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> +/* Video in to AXI4 Stream core underflowed */
> +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> +/* Video in to AXI4 Stream core overflowed */
> +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> +
>  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> -- 
> 1.8.3.1

This must be old.

-- 
Kind regards,

Sakari Ailus

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver
@ 2019-10-02  8:04     ` Sakari Ailus
  0 siblings, 0 replies; 41+ messages in thread
From: Sakari Ailus @ 2019-10-02  8:04 UTC (permalink / raw)
  To: Vishal Sagar
  Cc: Mark Rutland, devicetree, Dinesh Kumar, Hyun Kwon,
	Sandip Kothari, Michal Simek, linux-kernel, Rob Herring,
	Laurent Pinchart, Mauro Carvalho Chehab, linux-arm-kernel,
	linux-media

Hi Vishal,

On Tue, Jun 04, 2019 at 07:25:56PM +0530, Vishal Sagar wrote:
> The Xilinx UHD-SDI Rx subsystem soft IP is used to capture native SDI
> streams from SDI sources like SDI broadcast equipment like cameras and
> mixers. This block outputs either native SDI, native video or
> AXI4-Stream compliant data stream for further processing. Please refer
> to PG290 for details.
> 
> The driver is used to configure the IP to add framer, search for
> specific modes, get the detected mode, stream parameters, errors, etc.
> It also generates events for video lock/unlock, bridge over/under flow.
> 
> The driver supports only 10 bpc YUV 422 media bus format. It also
> decodes the stream parameters based on the ST352 packet embedded in the
> stream. In case the ST352 packet isn't present in the stream, the core's
> detected properties are used to set stream properties.
> 
> The driver currently supports only the AXI4-Stream configuration.
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig          |   11 +
>  drivers/media/platform/xilinx/Makefile         |    1 +
>  drivers/media/platform/xilinx/xilinx-sdirxss.c | 1846 ++++++++++++++++++++++++
>  include/uapi/linux/xilinx-sdirxss.h            |   63 +
>  include/uapi/linux/xilinx-v4l2-controls.h      |   30 +
>  include/uapi/linux/xilinx-v4l2-events.h        |    9 +
>  6 files changed, 1960 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
>  create mode 100644 include/uapi/linux/xilinx-sdirxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index cd1a0fd..0c68caa 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -20,6 +20,17 @@ config VIDEO_XILINX_CSI2RXSS
>  	  Bridge. The driver is used to set the number of active lanes and
>  	  get short packet data.
>  
> +config VIDEO_XILINX_SDIRXSS
> +	tristate "Xilinx UHD SDI Rx Subsystem"
> +	help
> +	  Driver for Xilinx UHD-SDI Rx Subsystem. This is a V4L sub-device
> +	  based driver that takes input from a SDI source like SDI camera and
> +	  converts it into an AXI4-Stream. The subsystem comprises of a SMPTE
> +	  UHD-SDI Rx core, a SDI Rx to Native Video bridge and a Video In to
> +	  AXI4-Stream bridge. The driver is used to set different stream
> +	  detection modes and identify stream properties to properly configure
> +	  downstream.
> +
>  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 6119a34..223f2ea 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -4,5 +4,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_SDIRXSS) += xilinx-sdirxss.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-sdirxss.c b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> new file mode 100644
> index 0000000..ba2d9d0
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,1846 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.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/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-sdirxss.h>
> +#include <linux/xilinx-v4l2-events.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"
> +
> +/*
> + * SDI Rx register map, bitmask and offsets
> + */
> +#define XSDIRX_RST_CTRL_REG		0x00
> +#define XSDIRX_MDL_CTRL_REG		0x04
> +#define XSDIRX_GLBL_IER_REG		0x0C
> +#define XSDIRX_ISR_REG			0x10
> +#define XSDIRX_IER_REG			0x14
> +#define XSDIRX_ST352_VALID_REG		0x18
> +#define XSDIRX_ST352_DS1_REG		0x1C
> +#define XSDIRX_ST352_DS3_REG		0x20
> +#define XSDIRX_ST352_DS5_REG		0x24
> +#define XSDIRX_ST352_DS7_REG		0x28
> +#define XSDIRX_ST352_DS9_REG		0x2C
> +#define XSDIRX_ST352_DS11_REG		0x30
> +#define XSDIRX_ST352_DS13_REG		0x34
> +#define XSDIRX_ST352_DS15_REG		0x38
> +#define XSDIRX_VERSION_REG		0x3C
> +#define XSDIRX_SS_CONFIG_REG		0x40
> +#define XSDIRX_MODE_DET_STAT_REG	0x44
> +#define XSDIRX_TS_DET_STAT_REG		0x48
> +#define XSDIRX_EDH_STAT_REG		0x4C
> +#define XSDIRX_EDH_ERRCNT_EN_REG	0x50
> +#define XSDIRX_EDH_ERRCNT_REG		0x54
> +#define XSDIRX_CRC_ERRCNT_REG		0x58
> +#define XSDIRX_VID_LOCK_WINDOW_REG	0x5C
> +#define XSDIRX_SB_RX_STS_REG		0x60
> +
> +#define XSDIRX_RST_CTRL_SS_EN_MASK			BIT(0)
> +#define XSDIRX_RST_CTRL_SRST_MASK			BIT(1)
> +#define XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK		BIT(2)
> +#define XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK		BIT(3)
> +#define XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK		BIT(8)
> +#define XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK	BIT(9)
> +
> +#define XSDIRX_MDL_CTRL_FRM_EN_MASK		BIT(4)
> +#define XSDIRX_MDL_CTRL_MODE_DET_EN_MASK	BIT(5)
> +#define XSDIRX_MDL_CTRL_MODE_HD_EN_MASK		BIT(8)
> +#define XSDIRX_MDL_CTRL_MODE_SD_EN_MASK		BIT(9)
> +#define XSDIRX_MDL_CTRL_MODE_3G_EN_MASK		BIT(10)
> +#define XSDIRX_MDL_CTRL_MODE_6G_EN_MASK		BIT(11)
> +#define XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK	BIT(12)
> +#define XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK	BIT(13)
> +#define XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK	GENMASK(13, 8)
> +
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET	16
> +#define XSDIRX_MDL_CTRL_FORCED_MODE_MASK	GENMASK(18, 16)
> +
> +#define XSDIRX_GLBL_INTR_EN_MASK	BIT(0)
> +
> +#define XSDIRX_INTR_VIDLOCK_MASK	BIT(0)
> +#define XSDIRX_INTR_VIDUNLOCK_MASK	BIT(1)
> +#define XSDIRX_INTR_OVERFLOW_MASK	BIT(9)
> +#define XSDIRX_INTR_UNDERFLOW_MASK	BIT(10)
> +
> +#define XSDIRX_INTR_ALL_MASK	(XSDIRX_INTR_VIDLOCK_MASK |\
> +				XSDIRX_INTR_VIDUNLOCK_MASK |\
> +				XSDIRX_INTR_OVERFLOW_MASK |\
> +				XSDIRX_INTR_UNDERFLOW_MASK)
> +
> +#define XSDIRX_ST352_VALID_DS1_MASK	BIT(0)
> +#define XSDIRX_ST352_VALID_DS3_MASK	BIT(1)
> +#define XSDIRX_ST352_VALID_DS5_MASK	BIT(2)
> +#define XSDIRX_ST352_VALID_DS7_MASK	BIT(3)
> +#define XSDIRX_ST352_VALID_DS9_MASK	BIT(4)
> +#define XSDIRX_ST352_VALID_DS11_MASK	BIT(5)
> +#define XSDIRX_ST352_VALID_DS13_MASK	BIT(6)
> +#define XSDIRX_ST352_VALID_DS15_MASK	BIT(7)
> +
> +#define XSDIRX_MODE_DET_STAT_RX_MODE_MASK	GENMASK(2, 0)
> +#define XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK	BIT(3)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK	GENMASK(6, 4)
> +#define XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET	4
> +#define XSDIRX_MODE_DET_STAT_LVLB_3G_MASK	BIT(7)
> +
> +#define XSDIRX_ACTIVE_STREAMS_1		0x0
> +#define XSDIRX_ACTIVE_STREAMS_2		0x1
> +#define XSDIRX_ACTIVE_STREAMS_4		0x2
> +#define XSDIRX_ACTIVE_STREAMS_8		0x3
> +#define XSDIRX_ACTIVE_STREAMS_16	0x4
> +
> +#define XSDIRX_TS_DET_STAT_LOCKED_MASK		BIT(0)
> +#define XSDIRX_TS_DET_STAT_SCAN_MASK		BIT(1)
> +#define XSDIRX_TS_DET_STAT_SCAN_OFFSET		(1)
> +#define XSDIRX_TS_DET_STAT_FAMILY_MASK		GENMASK(7, 4)
> +#define XSDIRX_TS_DET_STAT_FAMILY_OFFSET	(4)
> +#define XSDIRX_TS_DET_STAT_RATE_MASK		GENMASK(11, 8)
> +#define XSDIRX_TS_DET_STAT_RATE_OFFSET		(8)
> +
> +#define XSDIRX_TS_DET_STAT_RATE_NONE		0x0
> +#define XSDIRX_TS_DET_STAT_RATE_23_98HZ		0x2
> +#define XSDIRX_TS_DET_STAT_RATE_24HZ		0x3
> +#define XSDIRX_TS_DET_STAT_RATE_47_95HZ		0x4
> +#define XSDIRX_TS_DET_STAT_RATE_25HZ		0x5
> +#define XSDIRX_TS_DET_STAT_RATE_29_97HZ		0x6
> +#define XSDIRX_TS_DET_STAT_RATE_30HZ		0x7
> +#define XSDIRX_TS_DET_STAT_RATE_48HZ		0x8
> +#define XSDIRX_TS_DET_STAT_RATE_50HZ		0x9
> +#define XSDIRX_TS_DET_STAT_RATE_59_94HZ		0xA
> +#define XSDIRX_TS_DET_STAT_RATE_60HZ		0xB
> +
> +#define XSDIRX_EDH_STAT_EDH_AP_MASK	BIT(0)
> +#define XSDIRX_EDH_STAT_EDH_FF_MASK	BIT(1)
> +#define XSDIRX_EDH_STAT_EDH_ANC_MASK	BIT(2)
> +#define XSDIRX_EDH_STAT_AP_FLAG_MASK	GENMASK(8, 4)
> +#define XSDIRX_EDH_STAT_FF_FLAG_MASK	GENMASK(13, 9)
> +#define XSDIRX_EDH_STAT_ANC_FLAG_MASK	GENMASK(18, 14)
> +#define XSDIRX_EDH_STAT_PKT_FLAG_MASK	GENMASK(22, 19)
> +
> +#define XSDIRX_EDH_ERRCNT_COUNT_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_CRC_ERRCNT_COUNT_MASK	GENMASK(31, 16)
> +#define XSDIRX_CRC_ERRCNT_DS_CRC_MASK	GENMASK(15, 0)
> +
> +#define XSDIRX_VERSION_REV_MASK		GENMASK(7, 0)
> +#define XSDIRX_VERSION_PATCHID_MASK	GENMASK(11, 8)
> +#define XSDIRX_VERSION_VER_REV_MASK	GENMASK(15, 12)
> +#define XSDIRX_VERSION_VER_MIN_MASK	GENMASK(23, 16)
> +#define XSDIRX_VERSION_VER_MAJ_MASK	GENMASK(31, 24)
> +
> +#define XSDIRX_SS_CONFIG_EDH_INCLUDED_MASK		BIT(1)
> +
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_DONE_MASK	BIT(0)
> +#define XSDIRX_STAT_SB_RX_TDATA_CHANGE_FAIL_MASK	BIT(1)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_RESETDONE_MASK	BIT(2)
> +#define XSDIRX_STAT_SB_RX_TDATA_GT_BITRATE_MASK		BIT(3)
> +
> +/* Number of media pads */
> +#define XSDIRX_MEDIA_PADS	(1)
> +
> +#define XSDIRX_DEFAULT_WIDTH	(1920)
> +#define XSDIRX_DEFAULT_HEIGHT	(1080)
> +
> +#define XSDIRX_MAX_STR_LENGTH	16
> +
> +#define XSDIRXSS_SDI_STD_3G		0
> +#define XSDIRXSS_SDI_STD_6G		1
> +#define XSDIRXSS_SDI_STD_12G_8DS	2
> +
> +#define XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW	0x3000
> +
> +#define XSDIRX_MODE_HD_MASK	0x0
> +#define XSDIRX_MODE_SD_MASK	0x1
> +#define XSDIRX_MODE_3G_MASK	0x2
> +#define XSDIRX_MODE_6G_MASK	0x4
> +#define XSDIRX_MODE_12GI_MASK	0x5
> +#define XSDIRX_MODE_12GF_MASK	0x6
> +
> +/*
> + * Maximum number of events per file handle.
> + */
> +#define XSDIRX_MAX_EVENTS	(128)
> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE_MASK	0xFF
> +#define XST352_PAYLOAD_BYTE1_SHIFT	0
> +#define XST352_PAYLOAD_BYTE2_SHIFT	8
> +#define XST352_PAYLOAD_BYTE3_SHIFT	16
> +#define XST352_PAYLOAD_BYTE4_SHIFT	24
> +
> +#define XST352_BYTE1_ST292_1x720L_1_5G		0x84
> +#define XST352_BYTE1_ST292_1x1080L_1_5G		0x85
> +#define XST352_BYTE1_ST425_2008_750L_3GB	0x88
> +#define XST352_BYTE1_ST425_2008_1125L_3GA	0x89
> +#define XST352_BYTE1_ST372_DL_3GB		0x8A
> +#define XST352_BYTE1_ST372_2x720L_3GB		0x8B
> +#define XST352_BYTE1_ST372_2x1080L_3GB		0x8C
> +#define XST352_BYTE1_ST2081_10_2160L_6G		0xC0
> +#define XST352_BYTE1_ST2081_10_DL_2160L_6G	0xC2
> +#define XST352_BYTE1_ST2082_10_2160L_12G	0xCE
> +
> +#define XST352_BYTE2_TS_TYPE_MASK		BIT(15)
> +#define XST352_BYTE2_TS_TYPE_OFFSET		15
> +#define XST352_BYTE2_PIC_TYPE_MASK		BIT(14)
> +#define XST352_BYTE2_PIC_TYPE_OFFSET		14
> +#define XST352_BYTE2_TS_PIC_TYPE_INTERLACED	0
> +#define XST352_BYTE2_TS_PIC_TYPE_PROGRESSIVE	1
> +
> +#define XST352_BYTE2_FPS_MASK			0xF
> +#define XST352_BYTE2_FPS_SHIFT			8
> +#define XST352_BYTE2_FPS_24F			0x2
> +#define XST352_BYTE2_FPS_24			0x3
> +#define XST352_BYTE2_FPS_48F			0x4
> +#define XST352_BYTE2_FPS_25			0x5
> +#define XST352_BYTE2_FPS_30F			0x6
> +#define XST352_BYTE2_FPS_30			0x7
> +#define XST352_BYTE2_FPS_48			0x8
> +#define XST352_BYTE2_FPS_50			0x9
> +#define XST352_BYTE2_FPS_60F			0xA
> +#define XST352_BYTE2_FPS_60			0xB
> +/* Table 4 ST 2081-10:2015 */
> +#define XST352_BYTE2_FPS_96			0xC
> +#define XST352_BYTE2_FPS_100			0xD
> +#define XST352_BYTE2_FPS_120			0xE
> +#define XST352_BYTE2_FPS_120F			0xF
> +
> +#define XST352_BYTE3_ACT_LUMA_COUNT_MASK	BIT(22)
> +#define XST352_BYTE3_ACT_LUMA_COUNT_OFFSET	22
> +
> +#define XST352_BYTE3_COLOR_FORMAT_MASK		GENMASK(19, 16)
> +#define XST352_BYTE3_COLOR_FORMAT_OFFSET	16
> +#define XST352_BYTE3_COLOR_FORMAT_422		0x0
> +#define XST352_BYTE3_COLOR_FORMAT_420		0x3
> +
> +/**
> + * enum sdi_family_enc - SDI Transport Video Format Detected with Active Pixels
> + * @XSDIRX_SMPTE_ST_274: SMPTE ST 274 detected with AP 1920x1080
> + * @XSDIRX_SMPTE_ST_296: SMPTE ST 296 detected with AP 1280x720
> + * @XSDIRX_SMPTE_ST_2048_2: SMPTE ST 2048-2 detected with AP 2048x1080
> + * @XSDIRX_SMPTE_ST_295: SMPTE ST 295 detected with AP 1920x1080
> + * @XSDIRX_NTSC: NTSC encoding detected with AP 720x486
> + * @XSDIRX_PAL: PAL encoding detected with AP 720x576
> + * @XSDIRX_TS_UNKNOWN: Unknown SMPTE Transport family type
> + */
> +enum sdi_family_enc {
> +	XSDIRX_SMPTE_ST_274	= 0,
> +	XSDIRX_SMPTE_ST_296	= 1,
> +	XSDIRX_SMPTE_ST_2048_2	= 2,
> +	XSDIRX_SMPTE_ST_295	= 3,
> +	XSDIRX_NTSC		= 8,
> +	XSDIRX_PAL		= 9,
> +	XSDIRX_TS_UNKNOWN	= 15
> +};
> +
> +/**
> + * struct xsdirxss_core - Core configuration SDI Rx Subsystem device structure
> + * @dev: Platform structure
> + * @iomem: Base address of subsystem
> + * @irq: requested irq number
> + * @include_edh: EDH processor presence
> + * @mode: 3G/6G/12G mode
> + * @axi_clk: Axi lite interface clock
> + * @sdirx_clk: SDI Rx GT clock
> + * @vidout_clk: Video clock
> + */
> +struct xsdirxss_core {
> +	struct device *dev;
> +	void __iomem *iomem;
> +	int irq;
> +	bool include_edh;
> +	int mode;
> +	struct clk *axi_clk;
> +	struct clk *sdirx_clk;
> +	struct clk *vidout_clk;
> +};
> +
> +/**
> + * struct xsdirxss_state - SDI Rx Subsystem device structure
> + * @core: Core structure for MIPI SDI Rx Subsystem
> + * @subdev: The v4l2 subdev structure
> + * @ctrl_handler: control handler
> + * @event: Holds the video unlock event
> + * @formats: Active V4L2 formats on each pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @vip_format: format information corresponding to the active format
> + * @pads: media pads
> + * @streaming: Flag for storing streaming state
> + * @vidlocked: Flag indicating SDI Rx has locked onto video stream
> + * @ts_is_interlaced: Flag indicating Transport Stream is interlaced.
> + *
> + * This structure contains the device driver related parameters
> + */
> +struct xsdirxss_state {
> +	struct xsdirxss_core core;
> +	struct v4l2_subdev subdev;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +	struct v4l2_event event;
> +	struct v4l2_mbus_framefmt formats[XSDIRX_MEDIA_PADS];
> +	struct v4l2_mbus_framefmt default_format;
> +	struct v4l2_fract frame_interval;
> +	const struct xvip_video_format *vip_format;
> +	struct media_pad pads[XSDIRX_MEDIA_PADS];
> +	bool streaming;
> +	bool vidlocked;
> +	bool ts_is_interlaced;
> +};
> +
> +static inline struct xsdirxss_state *
> +to_xsdirxssstate(struct v4l2_subdev *subdev)
> +{
> +	return container_of(subdev, struct xsdirxss_state, subdev);
> +}
> +
> +/*
> + * Register related operations
> + */
> +static inline u32 xsdirxss_read(struct xsdirxss_core *xsdirxss, u32 addr)
> +{
> +	return ioread32(xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_write(struct xsdirxss_core *xsdirxss, u32 addr,
> +				  u32 value)
> +{
> +	iowrite32(value, xsdirxss->iomem + addr);
> +}
> +
> +static inline void xsdirxss_clr(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 clr)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) & ~clr);
> +}
> +
> +static inline void xsdirxss_set(struct xsdirxss_core *xsdirxss, u32 addr,
> +				u32 set)
> +{
> +	xsdirxss_write(xsdirxss, addr, xsdirxss_read(xsdirxss, addr) | set);
> +}
> +
> +static void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static void xsdirx_core_enable(struct xsdirxss_core *core)
> +{
> +	xsdirxss_set(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static int xsdirx_set_modedetect(struct xsdirxss_core *core, u16 mask)
> +{
> +	u32 i, val;
> +
> +	mask &= XSDIRX_DETECT_ALL_MODES;
> +	if (!mask) {
> +		dev_err(core->dev, "Invalid bit mask = 0x%08x\n", mask);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(core->dev, "mask = 0x%x\n", mask);
> +
> +	val = xsdirxss_read(core, XSDIRX_MDL_CTRL_REG);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_DET_EN_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_MODE_AUTO_DET_MASK);
> +	val &= ~(XSDIRX_MDL_CTRL_FORCED_MODE_MASK);
> +
> +	if (hweight16(mask) > 1) {
> +		/* Multi mode detection as more than 1 bit set in mask */
> +		dev_dbg(core->dev, "Detect multiple modes\n");
> +		for (i = 0; i < XSDIRX_MODE_NUM_SUPPORTED; i++) {
> +			switch (mask & (1 << i)) {
> +			case BIT(XSDIRX_MODE_SD_OFFSET):

There's no really a need for the loop.

> +				val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_HD_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_3G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_6G_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GI_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> +				break;
> +			case BIT(XSDIRX_MODE_12GF_OFFSET):
> +				val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +				break;
> +			}
> +		}
> +		val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> +	} else {
> +		/* Fixed Mode */
> +		u32 forced_mode_mask = 0;
> +
> +		dev_dbg(core->dev, "Detect fixed mode\n");
> +
> +		/* Find offset of first bit set */
> +		switch (__ffs(mask)) {
> +		case XSDIRX_MODE_SD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_SD_MASK;
> +			break;
> +		case XSDIRX_MODE_HD_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_HD_MASK;
> +			break;
> +		case XSDIRX_MODE_3G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_3G_MASK;
> +			break;
> +		case XSDIRX_MODE_6G_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_6G_MASK;
> +			break;
> +		case XSDIRX_MODE_12GI_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GI_MASK;
> +			break;
> +		case XSDIRX_MODE_12GF_OFFSET:
> +			forced_mode_mask = XSDIRX_MODE_12GF_MASK;
> +			break;
> +		}
> +		dev_dbg(core->dev, "Forced Mode Mask : 0x%x\n",
> +			forced_mode_mask);
> +		val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> +	}
> +
> +	dev_dbg(core->dev, "Modes to be detected : sdi ctrl reg = 0x%08x\n",
> +		val);
> +	xsdirxss_write(core, XSDIRX_MDL_CTRL_REG, val);
> +
> +	return 0;
> +}
> +
> +static void xsdirx_framer(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_MDL_CTRL_REG,
> +			     XSDIRX_MDL_CTRL_FRM_EN_MASK);
> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)
> +{
> +	u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> +	val = enable & XSDIRX_EDH_ALLERR_MASK;
> +
> +	xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static void xsdirx_setvidlockwindow(struct xsdirxss_core *core, u32 val)
> +{
> +	/*
> +	 * The video lock window is the amount of time for which the
> +	 * the mode and transport stream should be locked to get the
> +	 * video lock interrupt.
> +	 */
> +	xsdirxss_write(core, XSDIRX_VID_LOCK_WINDOW_REG, val);
> +}
> +
> +static void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_enableintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static void xsdirx_globalintr(struct xsdirxss_core *core, bool flag)
> +{
> +	if (flag)
> +		xsdirxss_set(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_GLBL_IER_REG,
> +			     XSDIRX_GLBL_INTR_EN_MASK);
> +}
> +
> +static void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> +	xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}
> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK);
> +}
> +
> +static void xsdirx_axis4_bridge_control(struct xsdirxss_core *core, bool enable)
> +{
> +	if (enable)
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +	else
> +		xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_VIDIN_AXI4S_MOD_ENB_MASK);
> +}
> +
> +static void xsdirx_streamflow_control(struct xsdirxss_core *core, bool enable)
> +{
> +	/* The sdi to native bridge is followed by native to axis4 bridge */
> +	if (enable) {
> +		xsdirx_axis4_bridge_control(core, enable);
> +		xsdirx_vid_bridge_control(core, enable);
> +	} else {
> +		xsdirx_vid_bridge_control(core, enable);
> +		xsdirx_axis4_bridge_control(core, enable);
> +	}
> +}
> +
> +static void xsdirx_streamdowncb(struct xsdirxss_core *core)
> +{
> +	xsdirx_streamflow_control(core, false);
> +}
> +
> +static void xsdirxss_get_framerate(struct v4l2_fract *frame_interval,
> +				   u32 framerate)
> +{
> +	switch (framerate) {
> +	case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 24000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 25000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 30000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_47_95HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_48HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 48000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 50000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +		frame_interval->numerator = 1001;
> +		frame_interval->denominator = 60000;
> +		break;
> +	case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +		frame_interval->numerator = 1000;
> +		frame_interval->denominator = 60000;
> +		break;
> +	default:
> +		frame_interval->numerator = 1;
> +		frame_interval->denominator = 1;
> +	}
> +}
> +
> +/**
> + * xsdirx_get_stream_properties - Get SDI Rx stream properties
> + * @state: pointer to driver state
> + *
> + * This function decodes the stream's ST352 payload (if available) to get
> + * stream properties like width, height, picture type (interlaced/progressive),
> + * etc.
> + *
> + * Return: 0 for success else errors
> + */
> +static int xsdirx_get_stream_properties(struct xsdirxss_state *state)
> +{
> +	struct xsdirxss_core *core = &state->core;
> +	u32 mode, payload = 0, val, family, valid, tscan;
> +	u8 byte1 = 0, active_luma = 0, pic_type = 0, framerate = 0;
> +	u8 sampling = XST352_BYTE3_COLOR_FORMAT_422;
> +	struct v4l2_mbus_framefmt *format = &state->formats[0];
> +
> +	mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +	mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +	valid = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +
> +	if (mode >= XSDIRX_MODE_3G_MASK && !valid) {
> +		dev_err(core->dev, "No valid ST352 payload present even for 3G mode and above\n");
> +		return -EINVAL;
> +	}
> +
> +	val = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +	if (valid & XSDIRX_ST352_VALID_DS1_MASK) {
> +		payload = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +		byte1 = (payload >> XST352_PAYLOAD_BYTE1_SHIFT) &
> +				XST352_PAYLOAD_BYTE_MASK;
> +		active_luma = (payload & XST352_BYTE3_ACT_LUMA_COUNT_MASK) >>
> +				XST352_BYTE3_ACT_LUMA_COUNT_OFFSET;
> +		pic_type = (payload & XST352_BYTE2_PIC_TYPE_MASK) >>
> +				XST352_BYTE2_PIC_TYPE_OFFSET;
> +		framerate = (payload >> XST352_BYTE2_FPS_SHIFT) &
> +				XST352_BYTE2_FPS_MASK;
> +		tscan = (payload & XST352_BYTE2_TS_TYPE_MASK) >>
> +				XST352_BYTE2_TS_TYPE_OFFSET;
> +		sampling = (payload & XST352_BYTE3_COLOR_FORMAT_MASK) >>
> +			   XST352_BYTE3_COLOR_FORMAT_OFFSET;
> +	} else {
> +		dev_dbg(core->dev, "No ST352 payload available : Mode = %d\n",
> +			mode);
> +		framerate = (val & XSDIRX_TS_DET_STAT_RATE_MASK) >>
> +				XSDIRX_TS_DET_STAT_RATE_OFFSET;
> +		tscan = (val & XSDIRX_TS_DET_STAT_SCAN_MASK) >>
> +				XSDIRX_TS_DET_STAT_SCAN_OFFSET;
> +	}
> +
> +	family = (val & XSDIRX_TS_DET_STAT_FAMILY_MASK) >>
> +		  XSDIRX_TS_DET_STAT_FAMILY_OFFSET;
> +	state->ts_is_interlaced = tscan ? false : true;
> +
> +	dev_dbg(core->dev, "ts_is_interlaced = %d, family = %d\n",
> +		state->ts_is_interlaced, family);
> +
> +	switch (mode) {
> +	case XSDIRX_MODE_HD_MASK:
> +		if (!valid) {
> +			/* No payload obtained */
> +			dev_dbg(core->dev, "frame rate : %d, tscan = %d\n",
> +				framerate, tscan);
> +			/*
> +			 * NOTE : A progressive segmented frame pSF will be
> +			 * reported incorrectly as Interlaced as we rely on IP's
> +			 * transport scan locked bit.
> +			 */
> +			dev_warn(core->dev, "pSF will be incorrectly reported as Interlaced\n");
> +
> +			switch (framerate) {
> +			case XSDIRX_TS_DET_STAT_RATE_23_98HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_24HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_25HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_29_97HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_30HZ:
> +				if (family == XSDIRX_SMPTE_ST_296) {
> +					format->width = 1280;
> +					format->height = 720;
> +					format->field = V4L2_FIELD_NONE;
> +				} else if (family == XSDIRX_SMPTE_ST_2048_2) {
> +					format->width = 2048;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				} else {
> +					format->width = 1920;
> +					format->height = 1080;
> +					if (tscan)
> +						format->field = V4L2_FIELD_NONE;
> +					else
> +						format->field =
> +							V4L2_FIELD_ALTERNATE;
> +				}
> +				break;
> +			case XSDIRX_TS_DET_STAT_RATE_50HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_59_94HZ:
> +			case XSDIRX_TS_DET_STAT_RATE_60HZ:
> +				if (family == XSDIRX_SMPTE_ST_274) {
> +					format->width = 1920;
> +					format->height = 1080;
> +				} else {
> +					format->width = 1280;
> +					format->height = 720;
> +				}
> +				format->field = V4L2_FIELD_NONE;
> +				break;
> +			default:
> +				format->width = 1920;
> +				format->height = 1080;
> +				format->field = V4L2_FIELD_NONE;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "Got the payload\n");
> +			switch (byte1) {
> +			case XST352_BYTE1_ST292_1x720L_1_5G:
> +				/* SMPTE ST 292-1 for 720 line payloads */
> +				format->width = 1280;
> +				format->height = 720;
> +				break;
> +			case XST352_BYTE1_ST292_1x1080L_1_5G:
> +				/* SMPTE ST 292-1 for 1080 line payloads */
> +				format->height = 1080;
> +				if (active_luma)
> +					format->width = 2048;
> +				else
> +					format->width = 1920;
> +				break;
> +			default:
> +				dev_dbg(core->dev, "Unknown HD Mode SMPTE standard\n");
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case XSDIRX_MODE_SD_MASK:
> +		format->field = V4L2_FIELD_ALTERNATE;
> +
> +		switch (family) {
> +		case XSDIRX_NTSC:
> +			format->width = 720;
> +			format->height = 486;
> +			break;
> +		case XSDIRX_PAL:
> +			format->width = 720;
> +			format->height = 576;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown SD Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_3G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST425_2008_750L_3GB:
> +			/* Sec 4.1.6.1 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x720L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->width = 1280;
> +			format->height = 720;
> +			break;
> +		case XST352_BYTE1_ST425_2008_1125L_3GA:
> +			/* ST352 Table SMPTE 425-1 */
> +		case XST352_BYTE1_ST372_DL_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +		case XST352_BYTE1_ST372_2x1080L_3GB:
> +			/* Table 13 SMPTE 425-2008 */
> +			format->height = 1080;
> +			if (active_luma)
> +				format->width = 2048;
> +			else
> +				format->width = 1920;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 3G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_6G_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2081_10_DL_2160L_6G:
> +			/* Dual link 6G */
> +		case XST352_BYTE1_ST2081_10_2160L_6G:
> +			/* Table 3 SMPTE ST 2081-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 6G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	case XSDIRX_MODE_12GI_MASK:
> +	case XSDIRX_MODE_12GF_MASK:
> +		switch (byte1) {
> +		case XST352_BYTE1_ST2082_10_2160L_12G:
> +			/* Section 4.3.1 SMPTE ST 2082-10 */
> +			format->height = 2160;
> +			if (active_luma)
> +				format->width = 4096;
> +			else
> +				format->width = 3840;
> +			break;
> +		default:
> +			dev_dbg(core->dev, "Unknown 12G Mode SMPTE standard\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		dev_err(core->dev, "Invalid Mode\n");
> +		return -EINVAL;
> +	}
> +
> +	if (valid) {
> +		if (pic_type)
> +			format->field = V4L2_FIELD_NONE;
> +		else
> +			format->field = V4L2_FIELD_ALTERNATE;
> +	}
> +
> +	if (format->field == V4L2_FIELD_ALTERNATE)
> +		format->height = format->height / 2;
> +
> +	switch (sampling) {
> +	case XST352_BYTE3_COLOR_FORMAT_422:
> +		format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +		break;
> +	default:
> +		dev_err(core->dev, "Unsupported color format : %d\n", sampling);
> +		return -EINVAL;
> +	}
> +
> +	xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d payload = 0x%08x ts = 0x%08x\n",
> +		format->width, format->height, format->field, payload, val);
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		state->frame_interval.numerator,
> +		state->frame_interval.denominator);
> +	dev_dbg(core->dev, "Stream code = 0x%x\n", format->code);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_irq_handler - Interrupt handler for SDI Rx
> + * @irq: IRQ number
> + * @dev_id: Pointer to device state
> + *
> + * The SDI Rx interrupts are cleared by first setting and then clearing the bits
> + * in the interrupt clear register. The interrupt status register is read only.
> + *
> + * Return: IRQ_HANDLED after handling interrupts
> + */
> +static irqreturn_t xsdirxss_irq_handler(int irq, void *dev_id)
> +{
> +	struct xsdirxss_state *state = (struct xsdirxss_state *)dev_id;
> +	struct xsdirxss_core *core = &state->core;
> +	u32 status;
> +
> +	status = xsdirxss_read(core, XSDIRX_ISR_REG);
> +	dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
> +
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	if (status & XSDIRX_INTR_VIDLOCK_MASK) {
> +		u32 val1, val2;
> +
> +		dev_dbg(core->dev, "video lock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDLOCK_MASK);
> +
> +		val1 = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val2 = xsdirxss_read(core, XSDIRX_TS_DET_STAT_REG);
> +
> +		if ((val1 & XSDIRX_MODE_DET_STAT_MODE_LOCK_MASK) &&
> +		    (val2 & XSDIRX_TS_DET_STAT_LOCKED_MASK)) {
> +			u32 mask = XSDIRX_RST_CTRL_RST_CRC_ERRCNT_MASK |
> +				   XSDIRX_RST_CTRL_RST_EDH_ERRCNT_MASK;
> +
> +			dev_dbg(core->dev, "mode & ts lock occurred\n");
> +
> +			xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> +			xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +
> +			val1 = xsdirxss_read(core, XSDIRX_ST352_VALID_REG);
> +			val2 = xsdirxss_read(core, XSDIRX_ST352_DS1_REG);
> +
> +			dev_dbg(core->dev, "valid st352 mask = 0x%08x\n", val1);
> +			dev_dbg(core->dev, "st352 payload = 0x%08x\n", val2);
> +
> +			if (!xsdirx_get_stream_properties(state)) {
> +				memset(&state->event, 0, sizeof(state->event));
> +				state->event.type = V4L2_EVENT_SOURCE_CHANGE;
> +				state->event.u.src_change.changes =
> +					V4L2_EVENT_SRC_CH_RESOLUTION;
> +				v4l2_subdev_notify_event(&state->subdev,
> +							 &state->event);
> +
> +				state->vidlocked = true;
> +			} else {
> +				dev_err(core->dev, "Unable to get stream properties!\n");
> +				state->vidlocked = false;
> +			}
> +		} else {
> +			dev_dbg(core->dev, "video unlock before video lock!\n");
> +			state->vidlocked = false;
> +		}
> +	}
> +
> +	if (status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> +		dev_dbg(core->dev, "video unlock interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_VIDUNLOCK_MASK);
> +		xsdirx_streamdowncb(core);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_VIDUNLOCK;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +
> +		state->vidlocked = false;
> +	}
> +
> +	if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_UNDERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_UNDERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +
> +	if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> +		dev_dbg(core->dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +		xsdirx_clearintr(core, XSDIRX_INTR_OVERFLOW_MASK);
> +
> +		memset(&state->event, 0, sizeof(state->event));
> +		state->event.type = V4L2_EVENT_XLNXSDIRX_OVERFLOW;
> +		v4l2_subdev_notify_event(&state->subdev, &state->event);
> +	}
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * xsdirxss_subscribe_event - Subscribe to video lock and unlock event
> + * @sd: V4L2 Sub device
> + * @fh: V4L2 File Handle
> + * @sub: Subcribe event structure
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	int ret;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (sub->type) {
> +	case V4L2_EVENT_XLNXSDIRX_VIDUNLOCK:
> +	case V4L2_EVENT_XLNXSDIRX_UNDERFLOW:
> +	case V4L2_EVENT_XLNXSDIRX_OVERFLOW:
> +		ret = v4l2_event_subscribe(fh, sub, XSDIRX_MAX_EVENTS, NULL);
> +		break;
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		ret = v4l2_src_change_event_subscribe(fh, sub);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Event subscribed : 0x%08x\n", sub->type);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_unsubscribe_event(struct v4l2_subdev *sd,
> +				      struct v4l2_fh *fh,
> +				      struct v4l2_event_subscription *sub)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "Event unsubscribe : 0x%08x\n", sub->type);
> +	return v4l2_event_unsubscribe(fh, sub);
> +}
> +
> +/**
> + * xsdirxss_s_ctrl - This is used to set the Xilinx SDI Rx V4L2 controls
> + * @ctrl: V4L2 control to be set
> + *
> + * This function is used to set the V4L2 controls for the Xilinx SDI Rx
> + * Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	int ret = 0;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	dev_dbg(core->dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +
> +	if (xsdirxss->streaming) {
> +		dev_err(core->dev, "Cannot set controls while streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	xsdirx_core_disable(core);
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_FRAMER:
> +		xsdirx_framer(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> +		xsdirx_setvidlockwindow(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> +		xsdirx_setedherrcnttrigger(core, ctrl->val);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> +		if (ctrl->val) {
> +			if (core->mode == XSDIRXSS_SDI_STD_3G) {
> +				dev_dbg(core->dev, "Upto 3G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_6G_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			if (core->mode == XSDIRXSS_SDI_STD_6G) {
> +				dev_dbg(core->dev, "Upto 6G supported\n");
> +				ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> +					       BIT(XSDIRX_MODE_12GF_OFFSET));
> +			}
> +
> +			ret = xsdirx_set_modedetect(core, ctrl->val);
> +		} else {
> +			dev_err(core->dev, "Select at least one mode!\n");
> +			return -EINVAL;
> +		}
> +		break;
> +	default:
> +		xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> +			     XSDIRX_RST_CTRL_SS_EN_MASK);
> +		return -EINVAL;
> +	}
> +	xsdirx_core_enable(core);
> +	return ret;
> +}
> +
> +/**
> + * xsdirxss_g_volatile_ctrl - get the Xilinx SDI Rx controls
> + * @ctrl: Pointer to V4L2 control
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	u32 val;
> +	struct xsdirxss_state *xsdirxss =
> +		container_of(ctrl->handler,
> +			     struct xsdirxss_state, ctrl_handler);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> +		switch (val) {
> +		case XSDIRX_MODE_SD_MASK:
> +			ctrl->val = XSDIRX_MODE_SD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_HD_MASK:
> +			ctrl->val = XSDIRX_MODE_HD_OFFSET;
> +			break;
> +		case XSDIRX_MODE_3G_MASK:
> +			ctrl->val = XSDIRX_MODE_3G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_6G_MASK:
> +			ctrl->val = XSDIRX_MODE_6G_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GI_MASK:
> +			ctrl->val = XSDIRX_MODE_12GI_OFFSET;
> +			break;
> +		case XSDIRX_MODE_12GF_MASK:
> +			ctrl->val = XSDIRX_MODE_12GF_OFFSET;
> +			break;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_CRC:
> +		ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> +		xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_EDH_STATUS:
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +		if (val == XSDIRX_MODE_SD_MASK) {
> +			ctrl->val = xsdirxss_read(core, XSDIRX_EDH_STAT_REG);
> +		} else {
> +			dev_dbg(core->dev, "%d - not in SD mode\n", ctrl->id);
> +			return -EINVAL;
> +		}
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		ctrl->val = xsdirxss->ts_is_interlaced;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_ACT_STREAM_MASK;
> +		val >>= XSDIRX_MODE_DET_STAT_ACT_STREAM_OFFSET;
> +		ctrl->val = 1 << val;
> +		break;
> +	case V4L2_CID_XILINX_SDIRX_IS_3GB:
> +		if (!xsdirxss->vidlocked) {
> +			dev_err(core->dev, "Can't get values when video not locked!\n");
> +			return -EINVAL;
> +		}
> +		val = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> +		val &= XSDIRX_MODE_DET_STAT_LVLB_3G_MASK;
> +		ctrl->val = val ? true : false;
> +		break;
> +	default:
> +		dev_err(core->dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "Get ctrl id = 0x%08x val = 0x%08x\n",
> +		ctrl->id, ctrl->val);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_log_status - Logs the status of the SDI Rx Subsystem
> + * @sd: Pointer to V4L2 subdevice structure
> + *
> + * This function prints the current status of Xilinx SDI Rx Subsystem
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_log_status(struct v4l2_subdev *sd)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	u32 data, i;
> +
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> +	for (i = 0; i < 0x28; i++) {
> +		data = xsdirxss_read(core, i * 4);
> +		v4l2_info(sd, "offset 0x%08x data 0x%08x\n",
> +			  i * 4, data);
> +	}
> +	v4l2_info(sd, "***** SDI Rx subsystem reg dump end *****\n");
> +	return 0;
> +}
> +
> +static void xsdirxss_start_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, true);
> +}
> +
> +static void xsdirxss_stop_stream(struct xsdirxss_state *xsdirxss)
> +{
> +	xsdirx_streamflow_control(&xsdirxss->core, false);
> +}
> +
> +/**
> + * xsdirxss_g_frame_interval - Get the frame interval
> + * @sd: V4L2 Sub device
> + * @fi: Pointer to V4l2 Sub device frame interval structure
> + *
> + * This function is used to get the frame interval.
> + * The frame rate can be integral or fractional.
> + * Integral frame rate e.g. numerator = 1000, denominator = 24000 => 24 fps
> + * Fractional frame rate e.g. numerator = 1001, denominator = 24000 => 23.97 fps
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_g_frame_interval(struct v4l2_subdev *sd,
> +				     struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fi->interval = xsdirxss->frame_interval;
> +
> +	dev_dbg(core->dev, "frame rate numerator = %d denominator = %d\n",
> +		xsdirxss->frame_interval.numerator,
> +		xsdirxss->frame_interval.denominator);
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 SDI Rx Subsystem.
> + *
> + * Return: 0 on success, errors otherwise
> + */
> +static int xsdirxss_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (enable) {
> +		if (!xsdirxss->vidlocked) {
> +			dev_dbg(core->dev, "Video is not locked\n");
> +			return -EINVAL;
> +		}
> +		if (xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Already streaming\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_start_stream(xsdirxss);
> +		xsdirxss->streaming = true;
> +		dev_dbg(core->dev, "Streaming started\n");
> +	} else {
> +		if (!xsdirxss->streaming) {
> +			dev_dbg(core->dev, "Stopped streaming already\n");
> +			return -EINVAL;
> +		}
> +
> +		xsdirxss_stop_stream(xsdirxss);
> +		xsdirxss->streaming = false;
> +		dev_dbg(core->dev, "Streaming stopped\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__xsdirxss_get_pad_format(struct xsdirxss_state *xsdirxss,
> +			  struct v4l2_subdev_pad_config *cfg,
> +				unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(&xsdirxss->subdev, cfg, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &xsdirxss->formats[pad];
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * xsdirxss_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 xsdirxss_get_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +					struct v4l2_subdev_format *fmt)
> +{
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +
> +	if (!xsdirxss->vidlocked) {
> +		dev_err(core->dev, "Video not locked!\n");
> +		return -EINVAL;
> +	}
> +
> +	fmt->format = *__xsdirxss_get_pad_format(xsdirxss, cfg,
> +						 fmt->pad, fmt->which);
> +
> +	dev_dbg(core->dev, "Stream width = %d height = %d Field = %d\n",
> +		fmt->format.width, fmt->format.height, fmt->format.field);
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_set_format(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_pad_config *cfg,
> +				struct v4l2_subdev_format *fmt)
> +{
> +	struct v4l2_mbus_framefmt *__format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	dev_dbg(xsdirxss->core.dev,
> +		"set width %d height %d code %d field %d colorspace %d\n",
> +		fmt->format.width, fmt->format.height,
> +		fmt->format.code, fmt->format.field,
> +		fmt->format.colorspace);
> +
> +	__format = __xsdirxss_get_pad_format(xsdirxss, cfg,
> +					     fmt->pad, fmt->which);
> +
> +	/* Currently reset the code to one fixed in hardware */
> +	/* TODO : Add checks for width height */
> +	fmt->format.code = __format->code;
> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_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 pad.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_open(struct v4l2_subdev *sd,
> +			 struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_mbus_framefmt *format;
> +	struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> +	format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	*format = xsdirxss->default_format;

How about using the init_cfg op instead?

> +
> +	return 0;
> +}
> +
> +/**
> + * xsdirxss_close - Called on v4l2_close()
> + * @sd: Pointer to V4L2 sub device structure
> + * @fh: Pointer to V4L2 File handle
> + *
> + * This function is called on v4l2_close().
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_close(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_fh *fh)
> +{
> +	return 0;

No need to implement an empty close op.

> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Media Operations
> + */
> +
> +static const struct media_entity_operations xsdirxss_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate
> +};
> +
> +static const struct v4l2_ctrl_ops xsdirxss_ctrl_ops = {
> +	.g_volatile_ctrl = xsdirxss_g_volatile_ctrl,
> +	.s_ctrl	= xsdirxss_s_ctrl
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_edh_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE,
> +		.name	= "SDI Rx : EDH Error Count Enable",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_EDH_ALLERR_MASK,
> +		.def	= 0,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_ERRCNT,
> +		.name	= "SDI Rx : EDH Error Count",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_EDH_STATUS,
> +		.name	= "SDI Rx : EDH Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static struct v4l2_ctrl_config xsdirxss_ctrls[] = {
> +	{
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_FRAMER,
> +		.name	= "SDI Rx : Enable Framer",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.step	= 1,
> +		.def	= true,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW,
> +		.name	= "SDI Rx : Video Lock Window",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= XSDIRX_DEFAULT_VIDEO_LOCK_WINDOW,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_SEARCH_MODES,
> +		.name	= "SDI Rx : Modes search Mask",
> +		.type	= V4L2_CTRL_TYPE_BITMASK,
> +		.min	= 0,
> +		.max	= XSDIRX_DETECT_ALL_MODES,
> +		.def	= XSDIRX_DETECT_ALL_MODES,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_MODE_DETECT,
> +		.name	= "SDI Rx : Mode Detect Status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= XSDIRX_MODE_SD_OFFSET,
> +		.max	= XSDIRX_MODE_12GF_OFFSET,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_CRC,
> +		.name	= "SDI Rx : CRC Error status",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 0,
> +		.max	= 0xFFFFFFFF,
> +		.step	= 1,
> +		.def	= 0,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED,
> +		.name	= "SDI Rx : TS is Interlaced",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS,
> +		.name	= "SDI Rx : Active Streams",
> +		.type	= V4L2_CTRL_TYPE_INTEGER,
> +		.min	= 1,
> +		.max	= 16,
> +		.def	= 1,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}, {
> +		.ops	= &xsdirxss_ctrl_ops,
> +		.id	= V4L2_CID_XILINX_SDIRX_IS_3GB,
> +		.name	= "SDI Rx : Is 3GB",
> +		.type	= V4L2_CTRL_TYPE_BOOLEAN,
> +		.min	= false,
> +		.max	= true,
> +		.def	= false,
> +		.step	= 1,
> +		.flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
> +	}
> +};
> +
> +static const struct v4l2_subdev_core_ops xsdirxss_core_ops = {
> +	.log_status = xsdirxss_log_status,
> +	.subscribe_event = xsdirxss_subscribe_event,
> +	.unsubscribe_event = xsdirxss_unsubscribe_event
> +};
> +
> +static struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> +	.g_frame_interval = xsdirxss_g_frame_interval,
> +	.s_stream = xsdirxss_s_stream
> +};
> +
> +static struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> +	.get_fmt = xsdirxss_get_format,
> +	.set_fmt = xsdirxss_set_format,
> +};
> +
> +static struct v4l2_subdev_ops xsdirxss_ops = {
> +	.core = &xsdirxss_core_ops,
> +	.video = &xsdirxss_video_ops,
> +	.pad = &xsdirxss_pad_ops
> +};
> +
> +static const struct v4l2_subdev_internal_ops xsdirxss_internal_ops = {
> +	.open = xsdirxss_open,
> +	.close = xsdirxss_close
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> +	struct device_node *node = xsdirxss->core.dev->of_node;
> +	struct device_node *ports = NULL;
> +	struct device_node *port = NULL;
> +	unsigned int nports = 0;
> +	struct xsdirxss_core *core = &xsdirxss->core;
> +	int ret;
> +	const char *sdi_std;
> +
> +	core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> +	dev_dbg(core->dev, "EDH property = %s\n",
> +		core->include_edh ? "Present" : "Absent");
> +
> +	ret = of_property_read_string(node, "xlnx,line-rate",
> +				      &sdi_std);
> +	if (ret < 0) {
> +		dev_err(core->dev, "xlnx,line-rate property not found\n");
> +		return ret;
> +	}
> +
> +	if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_12G_8DS;
> +	} else if (!strncmp(sdi_std, "6G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_6G;
> +	} else if (!strncmp(sdi_std, "3G_SDI", XSDIRX_MAX_STR_LENGTH)) {
> +		core->mode = XSDIRXSS_SDI_STD_3G;
> +	} else {
> +		dev_err(core->dev, "Invalid Line Rate\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(core->dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> +		core->mode);
> +
> +	ports = of_get_child_by_name(node, "ports");
> +	if (!ports)
> +		ports = node;
> +
> +	for_each_child_of_node(ports, port) {

Please use the graph API to parse the graph instead.

> +		const struct xvip_video_format *format;
> +		struct device_node *endpoint;
> +
> +		if (!port->name || of_node_cmp(port->name, "port"))
> +			continue;
> +
> +		format = xvip_of_get_format(port);
> +		if (IS_ERR(format)) {
> +			dev_err(core->dev, "invalid format in DT");
> +			return PTR_ERR(format);
> +		}
> +
> +		dev_dbg(core->dev, "vf_code = %d bpc = %d bpp = %d\n",
> +			format->vf_code, format->width, format->bpp);
> +
> +		if (format->vf_code != XVIP_VF_YUV_422 &&
> +		    format->vf_code != XVIP_VF_YUV_420) {
> +			dev_err(core->dev, "Incorrect UG934 video format set.\n");
> +			return -EINVAL;
> +		}
> +		xsdirxss->vip_format = format;
> +
> +		endpoint = of_get_next_child(port, NULL);
> +		if (!endpoint) {
> +			dev_err(core->dev, "No port at\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Count the number of ports. */
> +		nports++;
> +	}
> +
> +	if (nports != 1) {
> +		dev_err(core->dev, "invalid number of ports %u\n", nports);
> +		return -EINVAL;
> +	}
> +
> +	/* Register interrupt handler */
> +	core->irq = irq_of_parse_and_map(node, 0);
> +
> +	ret = devm_request_irq(core->dev, core->irq, xsdirxss_irq_handler,
> +			       IRQF_SHARED, "xilinx-sdirxss", xsdirxss);
> +	if (ret) {
> +		dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> +	struct v4l2_subdev *subdev;
> +	struct xsdirxss_state *xsdirxss;
> +	struct xsdirxss_core *core;
> +	struct resource *res;
> +	int ret;
> +	unsigned int num_ctrls, num_edh_ctrls = 0, i;
> +
> +	xsdirxss = devm_kzalloc(&pdev->dev, sizeof(*xsdirxss), GFP_KERNEL);
> +	if (!xsdirxss)
> +		return -ENOMEM;
> +
> +	xsdirxss->core.dev = &pdev->dev;
> +	core = &xsdirxss->core;
> +
> +	core->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
> +	if (IS_ERR(core->axi_clk)) {
> +		ret = PTR_ERR(core->axi_clk);
> +		dev_err(&pdev->dev, "failed to get s_axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->sdirx_clk = devm_clk_get(&pdev->dev, "sdi_rx_clk");
> +	if (IS_ERR(core->sdirx_clk)) {
> +		ret = PTR_ERR(core->sdirx_clk);
> +		dev_err(&pdev->dev, "failed to get sdi_rx_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	core->vidout_clk = devm_clk_get(&pdev->dev, "video_out_clk");
> +	if (IS_ERR(core->vidout_clk)) {
> +		ret = PTR_ERR(core->vidout_clk);
> +		dev_err(&pdev->dev, "failed to get video_out_aclk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->axi_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable axi_clk (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(core->sdirx_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable sdirx_clk (%d)\n", ret);
> +		goto rx_clk_err;
> +	}
> +
> +	ret = clk_prepare_enable(core->vidout_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable vidout_clk (%d)\n", ret);
> +		goto vidout_clk_err;
> +	}
> +
> +	ret = xsdirxss_parse_of(xsdirxss);
> +	if (ret < 0)
> +		goto clk_err;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	xsdirxss->core.iomem = devm_ioremap_resource(xsdirxss->core.dev, res);
> +	if (IS_ERR(xsdirxss->core.iomem)) {
> +		ret = PTR_ERR(xsdirxss->core.iomem);
> +		goto clk_err;
> +	}
> +
> +	/* Reset the core */
> +	xsdirx_streamflow_control(core, false);
> +	xsdirx_core_disable(core);
> +	xsdirx_clearintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> +	xsdirx_globalintr(core, true);
> +	xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	xsdirxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	/* Initialize the default format */
> +	xsdirxss->default_format.code = xsdirxss->vip_format->code;
> +	xsdirxss->default_format.field = V4L2_FIELD_NONE;
> +	xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> +	xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> +	xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> +	xsdirxss->formats[0] = xsdirxss->default_format;
> +
> +	/* Initialize V4L2 subdevice and media entity */
> +	subdev = &xsdirxss->subdev;
> +	v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> +	subdev->dev = &pdev->dev;
> +	subdev->internal_ops = &xsdirxss_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 = &xsdirxss_media_ops;
> +
> +	v4l2_set_subdevdata(subdev, xsdirxss);
> +
> +	ret = media_entity_pads_init(&subdev->entity, 1, xsdirxss->pads);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* Initialise and register the controls */
> +	num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> +	if (xsdirxss->core.include_edh)
> +		num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
> +
> +	v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> +			       (num_ctrls + num_edh_ctrls));
> +
> +	for (i = 0; i < num_ctrls; i++) {
> +		struct v4l2_ctrl *ctrl;
> +
> +		dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +			i, xsdirxss_ctrls[i].name, xsdirxss_ctrls[i].id);
> +
> +		ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +					    &xsdirxss_ctrls[i], NULL);
> +		if (!ctrl) {
> +			dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +				xsdirxss_ctrls[i].name);
> +			goto error;
> +		}
> +	}
> +
> +	if (xsdirxss->core.include_edh) {
> +		for (i = 0; i < num_edh_ctrls; i++) {
> +			struct v4l2_ctrl *ctrl;
> +
> +			dev_dbg(xsdirxss->core.dev, "%d %s ctrl = 0x%x\n",
> +				i, xsdirxss_edh_ctrls[i].name,
> +				xsdirxss_edh_ctrls[i].id);
> +
> +			ctrl = v4l2_ctrl_new_custom(&xsdirxss->ctrl_handler,
> +						    &xsdirxss_edh_ctrls[i],
> +						    NULL);
> +			if (!ctrl) {
> +				dev_dbg(xsdirxss->core.dev, "Failed to add %s ctrl\n",
> +					xsdirxss_edh_ctrls[i].name);
> +				goto error;
> +			}
> +		}
> +	} else {
> +		dev_dbg(xsdirxss->core.dev, "Not registering the EDH controls as EDH is disabled in IP\n");
> +	}
> +
> +	if (xsdirxss->ctrl_handler.error) {
> +		dev_err(&pdev->dev, "failed to add controls\n");
> +		ret = xsdirxss->ctrl_handler.error;
> +		goto error;
> +	}
> +
> +	subdev->ctrl_handler = &xsdirxss->ctrl_handler;
> +
> +	ret = v4l2_ctrl_handler_setup(&xsdirxss->ctrl_handler);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to set controls\n");
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, xsdirxss);
> +
> +	ret = v4l2_async_register_subdev(subdev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register subdev\n");
> +		goto error;
> +	}
> +
> +	xsdirxss->streaming = false;
> +
> +	dev_info(xsdirxss->core.dev, "Xilinx SDI Rx Subsystem device found!\n");
> +
> +	xsdirx_core_enable(core);
> +
> +	return 0;
> +error:
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +
> +clk_err:
> +	clk_disable_unprepare(core->vidout_clk);
> +vidout_clk_err:
> +	clk_disable_unprepare(core->sdirx_clk);
> +rx_clk_err:
> +	clk_disable_unprepare(core->axi_clk);
> +	return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> +	struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> +	struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> +	v4l2_async_unregister_subdev(subdev);
> +	v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> +	media_entity_cleanup(&subdev->entity);
> +	clk_disable_unprepare(xsdirxss->core.vidout_clk);
> +	clk_disable_unprepare(xsdirxss->core.sdirx_clk);
> +	clk_disable_unprepare(xsdirxss->core.axi_clk);
> +	return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> +	{ .compatible = "xlnx,v-smpte-uhdsdi-rx-ss" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, xsdirxss_of_id_table);
> +
> +static struct platform_driver xsdirxss_driver = {
> +	.driver = {
> +		.name		= "xilinx-sdirxss",
> +		.of_match_table	= xsdirxss_of_id_table,
> +	},
> +	.probe			= xsdirxss_probe,
> +	.remove			= xsdirxss_remove,
> +};
> +
> +module_platform_driver(xsdirxss_driver);
> +
> +MODULE_AUTHOR("Vishal Sagar <vsagar@xilinx.com>");
> +MODULE_DESCRIPTION("Xilinx SDI Rx Subsystem Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/uapi/linux/xilinx-sdirxss.h b/include/uapi/linux/xilinx-sdirxss.h
> new file mode 100644
> index 0000000..983a43b
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Xilinx SDI Rx Subsystem mode and flag definitions.
> + *
> + * Copyright (C) 2019 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
> + *
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * This enum is used to prepare the bitmask of modes to be detected
> + */
> +enum {
> +	XSDIRX_MODE_SD_OFFSET = 0,
> +	XSDIRX_MODE_HD_OFFSET,
> +	XSDIRX_MODE_3G_OFFSET,
> +	XSDIRX_MODE_6G_OFFSET,
> +	XSDIRX_MODE_12GI_OFFSET,
> +	XSDIRX_MODE_12GF_OFFSET,
> +	XSDIRX_MODE_NUM_SUPPORTED,
> +};
> +
> +#define XSDIRX_DETECT_ALL_MODES		(BIT(XSDIRX_MODE_SD_OFFSET) | \
> +					BIT(XSDIRX_MODE_HD_OFFSET) | \
> +					BIT(XSDIRX_MODE_3G_OFFSET) | \
> +					BIT(XSDIRX_MODE_6G_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GI_OFFSET) | \
> +					BIT(XSDIRX_MODE_12GF_OFFSET))
> +
> +/*
> + * EDH Error Types
> + * ANC - Ancillary Data Packet Errors
> + * FF - Full Field Errors
> + * AP - Active Portion Errors
> + */
> +
> +#define XSDIRX_EDH_ERRCNT_ANC_EDH_ERR		BIT(0)
> +#define XSDIRX_EDH_ERRCNT_ANC_EDA_ERR		BIT(1)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDH_ERR		BIT(2)
> +#define XSDIRX_EDH_ERRCNT_ANC_IDA_ERR		BIT(3)
> +#define XSDIRX_EDH_ERRCNT_ANC_UES_ERR		BIT(4)
> +#define XSDIRX_EDH_ERRCNT_FF_EDH_ERR		BIT(5)
> +#define XSDIRX_EDH_ERRCNT_FF_EDA_ERR		BIT(6)
> +#define XSDIRX_EDH_ERRCNT_FF_IDH_ERR		BIT(7)
> +#define XSDIRX_EDH_ERRCNT_FF_IDA_ERR		BIT(8)
> +#define XSDIRX_EDH_ERRCNT_FF_UES_ERR		BIT(9)
> +#define XSDIRX_EDH_ERRCNT_AP_EDH_ERR		BIT(10)
> +#define XSDIRX_EDH_ERRCNT_AP_EDA_ERR		BIT(11)
> +#define XSDIRX_EDH_ERRCNT_AP_IDH_ERR		BIT(12)
> +#define XSDIRX_EDH_ERRCNT_AP_IDA_ERR		BIT(13)
> +#define XSDIRX_EDH_ERRCNT_AP_UES_ERR		BIT(14)
> +#define XSDIRX_EDH_ERRCNT_PKT_CHKSUM_ERR	BIT(15)
> +
> +#define XSDIRX_EDH_ALLERR_MASK		0xFFFF
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index f023623..4c68a10 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -83,4 +83,34 @@
>  /* Reset all event counters */
>  #define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 2)
>  
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* Base ID */
> +#define V4L2_CID_XILINX_SDIRX			(V4L2_CID_USER_BASE + 0xc100)
> +
> +/* Framer Control */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER		(V4L2_CID_XILINX_SDIRX + 1)
> +/* Video Lock Window Control */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW	(V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE	(V4L2_CID_XILINX_SDIRX + 3)
> +/* Mode search Control */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES	(V4L2_CID_XILINX_SDIRX + 4)
> +/* Get Detected Mode control */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT	(V4L2_CID_XILINX_SDIRX + 5)
> +/* Get CRC error status */
> +#define V4L2_CID_XILINX_SDIRX_CRC		(V4L2_CID_XILINX_SDIRX + 6)
> +/* Get EDH error count control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT	(V4L2_CID_XILINX_SDIRX + 7)
> +/* Get EDH status control */
> +#define V4L2_CID_XILINX_SDIRX_EDH_STATUS	(V4L2_CID_XILINX_SDIRX + 8)
> +/* Get Transport Interlaced status */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED	(V4L2_CID_XILINX_SDIRX + 9)
> +/* Get Active Streams count */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS	(V4L2_CID_XILINX_SDIRX + 10)
> +/* Is Mode 3GB */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB		(V4L2_CID_XILINX_SDIRX + 11)
> +
>  #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-events.h b/include/uapi/linux/xilinx-v4l2-events.h
> index 2aa357c..7b47595 100644
> --- a/include/uapi/linux/xilinx-v4l2-events.h
> +++ b/include/uapi/linux/xilinx-v4l2-events.h
> @@ -18,4 +18,13 @@
>  /* Stream Line Buffer full */
>  #define V4L2_EVENT_XLNXCSIRX_SLBF	(V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
>  
> +/* Xilinx SMPTE UHD-SDI RX Subsystem */
> +#define V4L2_EVENT_XLNXSDIRX_CLASS	(V4L2_EVENT_PRIVATE_START | 0x200)
> +/* Video Unlock event */
> +#define V4L2_EVENT_XLNXSDIRX_VIDUNLOCK	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x1)
> +/* Video in to AXI4 Stream core underflowed */
> +#define V4L2_EVENT_XLNXSDIRX_UNDERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x2)
> +/* Video in to AXI4 Stream core overflowed */
> +#define V4L2_EVENT_XLNXSDIRX_OVERFLOW	(V4L2_EVENT_XLNXSDIRX_CLASS | 0x3)
> +
>  #endif /* __UAPI_XILINX_V4L2_EVENTS_H__ */
> -- 
> 1.8.3.1

This must be old.

-- 
Kind regards,

Sakari Ailus

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2019-10-02  8:05 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-04 13:55 [PATCH 0/2] Add support for Xilinx UHD-SDI Receiver subsystem Vishal Sagar
2019-06-04 13:55 ` Vishal Sagar
2019-06-04 13:55 ` [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem Vishal Sagar
2019-06-04 13:55   ` Vishal Sagar
2019-07-08 22:50   ` Rob Herring
2019-07-08 22:50     ` Rob Herring
2019-10-02  7:22   ` Sakari Ailus
2019-10-02  7:22     ` Sakari Ailus
2019-06-04 13:55 ` [PATCH 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver Vishal Sagar
2019-06-04 13:55   ` Vishal Sagar
2019-06-05 12:57   ` Hans Verkuil
2019-06-05 12:57     ` Hans Verkuil
2019-06-14 11:44     ` Vishal Sagar
2019-06-14 11:44       ` Vishal Sagar
2019-06-15  7:55       ` Hans Verkuil
2019-06-15  7:55         ` Hans Verkuil
2019-06-15  7:55         ` Hans Verkuil
2019-06-18 11:51         ` Vishal Sagar
2019-06-18 11:51           ` Vishal Sagar
2019-06-18 11:51           ` Vishal Sagar
2019-06-18 12:08           ` Hans Verkuil
2019-06-18 12:08             ` Hans Verkuil
2019-06-18 12:08             ` Hans Verkuil
2019-06-18 12:45             ` Vishal Sagar
2019-06-18 12:45               ` Vishal Sagar
2019-06-18 12:45               ` Vishal Sagar
2019-06-13 22:05   ` Hyun Kwon
2019-06-13 22:05     ` Hyun Kwon
2019-06-13 22:05     ` Hyun Kwon
2019-06-13 22:31     ` Joe Perches
2019-06-13 22:31       ` Joe Perches
2019-06-13 22:31       ` Joe Perches
2019-06-14 12:15       ` Vishal Sagar
2019-06-14 12:15         ` Vishal Sagar
2019-06-14 12:15         ` Vishal Sagar
2019-06-14 12:12     ` Vishal Sagar
2019-06-14 12:12       ` Vishal Sagar
2019-06-14 12:12       ` Vishal Sagar
2019-10-02  8:04   ` Sakari Ailus
2019-10-02  8:04     ` Sakari Ailus
2019-10-02  8:04     ` Sakari Ailus

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.