2020-04-29 14:21:16

by Vishal Sagar

[permalink] [raw]
Subject: [PATCH v2 0/2] Add support for Xilinx UHD-SDI Receiver subsystem

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/12 bits per component.

Though the core also supports RBG/YUV444/YUV420 10/12 bits per component,
these are 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.

v2
1/2
- Converted to yaml format
- Removed references to xlnx,video*
- Fixed as per Sakari Ailus and Rob Herring's comments

2/2
- Added DV timing support based on Hans Verkuilś feedback
- More documentation to custom v4l controls and events
- Fixed Hyunś comments
- Added macro for masking and shifting as per Joe Perches comments
- Updated to latest as per Xilinx github repo driver like
adding new DV timings not in mainline yet uptill 03/21/20

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.yaml | 132 +
drivers/media/platform/xilinx/Kconfig | 11 +
drivers/media/platform/xilinx/Makefile | 1 +
.../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
include/uapi/linux/xilinx-sdirxss.h | 179 ++
include/uapi/linux/xilinx-v4l2-controls.h | 67 +
6 files changed, 2552 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
create mode 100644 include/uapi/linux/xilinx-sdirxss.h

--
2.21.0


2020-04-29 14:21:19

by Vishal Sagar

[permalink] [raw]
Subject: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

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 10/12 bpc YUV 422 media bus format currently. 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 IP configuration.

Signed-off-by: Vishal Sagar <[email protected]>
---
v2
- Added DV timing support based on Hans Verkuilś feedback
- More documentation to custom v4l controls and events
- Fixed Hyunś comments
- Added macro for masking and shifting as per Joe Perches comments
- Updated to latest as per Xilinx github repo driver like
adding new DV timings not in mainline yet uptill 03/21/20

drivers/media/platform/xilinx/Kconfig | 11 +
drivers/media/platform/xilinx/Makefile | 1 +
.../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
include/uapi/linux/xilinx-sdirxss.h | 179 ++
include/uapi/linux/xilinx-v4l2-controls.h | 67 +
5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
--- a/drivers/media/platform/xilinx/Kconfig
+++ b/drivers/media/platform/xilinx/Kconfig
@@ -12,6 +12,17 @@ config VIDEO_XILINX

if VIDEO_XILINX

+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 4cdc0b1ec7a5..6c375f03f587 100644
--- a/drivers/media/platform/xilinx/Makefile
+++ b/drivers/media/platform/xilinx/Makefile
@@ -2,6 +2,7 @@

xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o

+obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
@@ -0,0 +1,2162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Xilinx SDI Rx Subsystem
+ *
+ * Copyright (C) 2017 - 2020 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <[email protected]>
+ *
+ */
+
+#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-dv-timings.h>
+#include <linux/v4l2-subdev.h>
+#include <linux/xilinx-sdirxss.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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
+#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12, 10)
+#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
+
+#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_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)
+
+#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_BYTE1_MASK 0xFF
+#define XST352_PAYLOAD_BYTE1_OFFSET 0
+#define XST352_PAYLOAD_BYTE2_OFFSET 8
+#define XST352_PAYLOAD_BYTE3_OFFSET 16
+#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
+#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_OFFSET 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_YUV444 0x1
+#define XST352_BYTE3_COLOR_FORMAT_420 0x3
+#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
+
+#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
+#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
+#define XST352_BYTE4_BIT_DEPTH_10 0x1
+#define XST352_BYTE4_BIT_DEPTH_12 0x2
+
+#define CLK_INT 148500000UL
+
+#define rshift_and_mask(val, type) \
+ (((val) >> type##_OFFSET) & type##_MASK)
+
+#define mask_and_rshift(val, type) \
+ (((val) & type##_MASK) >> type##_OFFSET)
+
+/**
+ * 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
+ * @clks: array of clocks
+ * @num_clks: number of clocks
+ * @bpc: Bits per component, can be 10 or 12
+ */
+struct xsdirxss_core {
+ struct device *dev;
+ void __iomem *iomem;
+ int irq;
+ bool include_edh;
+ int mode;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ u32 bpc;
+};
+
+/**
+ * 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
+ * @format: Active V4L2 format on source pad
+ * @default_format: default V4L2 media bus format
+ * @frame_interval: Captures the frame rate
+ * @pad: source media pad
+ * @vidlockwin: Video lock window value set by control
+ * @edhmask: EDH mask set by control
+ * @searchmask: Search mask set by control
+ * @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.
+ * @framer_enable: Flag for framer enabled or not set by control
+ *
+ * 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 format;
+ struct v4l2_mbus_framefmt default_format;
+ struct v4l2_fract frame_interval;
+ struct media_pad pad;
+ u32 vidlockwin;
+ u32 edhmask;
+ u16 searchmask;
+ bool streaming;
+ bool vidlocked;
+ bool ts_is_interlaced;
+ bool framer_enable;
+};
+
+/* List of clocks required by UHD-SDI Rx subsystem */
+static const char * const xsdirxss_clks[] = {
+ "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
+};
+
+/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
+static const u32 xsdirxss_10bpc_mbus_fmts[] = {
+ MEDIA_BUS_FMT_UYVY10_1X20,
+};
+
+static const u32 xsdirxss_12bpc_mbus_fmts[] = {
+ MEDIA_BUS_FMT_UYVY12_1X24,
+};
+
+static const struct v4l2_dv_timings fmt_cap[] = {
+ V4L2_DV_BT_SDI_720X487I60,
+ V4L2_DV_BT_CEA_720X576I50,
+ V4L2_DV_BT_CEA_1280X720P24,
+ V4L2_DV_BT_CEA_1280X720P25,
+ V4L2_DV_BT_CEA_1280X720P30,
+ V4L2_DV_BT_CEA_1280X720P50,
+ V4L2_DV_BT_CEA_1280X720P60,
+ V4L2_DV_BT_CEA_1920X1080P24,
+ V4L2_DV_BT_CEA_1920X1080P30,
+ V4L2_DV_BT_CEA_1920X1080I50,
+ V4L2_DV_BT_CEA_1920X1080I60,
+ V4L2_DV_BT_CEA_1920X1080P50,
+ V4L2_DV_BT_CEA_1920X1080P60,
+ V4L2_DV_BT_CEA_3840X2160P24,
+ V4L2_DV_BT_CEA_3840X2160P30,
+ V4L2_DV_BT_CEA_3840X2160P50,
+ V4L2_DV_BT_CEA_3840X2160P60,
+ V4L2_DV_BT_CEA_4096X2160P24,
+ V4L2_DV_BT_CEA_4096X2160P25,
+ V4L2_DV_BT_CEA_4096X2160P30,
+ V4L2_DV_BT_CEA_4096X2160P50,
+ V4L2_DV_BT_CEA_4096X2160P60,
+
+ XLNX_V4L2_DV_BT_2048X1080P24,
+ XLNX_V4L2_DV_BT_2048X1080P25,
+ XLNX_V4L2_DV_BT_2048X1080P30,
+ XLNX_V4L2_DV_BT_2048X1080I48,
+ XLNX_V4L2_DV_BT_2048X1080I50,
+ XLNX_V4L2_DV_BT_2048X1080I60,
+ XLNX_V4L2_DV_BT_2048X1080P48,
+ XLNX_V4L2_DV_BT_2048X1080P50,
+ XLNX_V4L2_DV_BT_2048X1080P60,
+ XLNX_V4L2_DV_BT_1920X1080P48,
+ XLNX_V4L2_DV_BT_1920X1080I48,
+ XLNX_V4L2_DV_BT_3840X2160P48,
+ XLNX_V4L2_DV_BT_4096X2160P48,
+};
+
+struct xsdirxss_dv_map {
+ u32 width;
+ u32 height;
+ u32 fps;
+ struct v4l2_dv_timings format;
+};
+
+static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
+ /* SD - 720x487i60 */
+ { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
+ /* SD - 720x576i50 */
+ { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
+ /* HD - 1280x720p23.98 */
+ /* HD - 1280x720p24 */
+ { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
+ /* HD - 1280x720p25 */
+ { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
+ /* HD - 1280x720p29.97 */
+ /* HD - 1280x720p30 */
+ { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
+ /* HD - 1280x720p50 */
+ { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
+ /* HD - 1280x720p59.94 */
+ /* HD - 1280x720p60 */
+ { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
+ /* HD - 1920x1080p23.98 */
+ /* HD - 1920x1080p24 */
+ { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
+ /* HD - 1920x1080p25 */
+ { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
+ /* HD - 1920x1080p29.97 */
+ /* HD - 1920x1080p30 */
+ { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
+
+ /* HD - 2048x1080p23.98 */
+ /* HD - 2048x1080p24 */
+ { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
+ /* HD - 2048x1080p25 */
+ { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
+ /* HD - 2048x1080p29.97 */
+ /* HD - 2048x1080p30 */
+ { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
+ /* HD - 1920x1080i47.95 */
+ /* HD - 1920x1080i48 */
+ { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
+
+ /* HD - 1920x1080i50 */
+ { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
+ /* HD - 1920x1080i59.94 */
+ /* HD - 1920x1080i60 */
+ { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
+
+ /* HD - 2048x1080i47.95 */
+ /* HD - 2048x1080i48 */
+ { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
+ /* HD - 2048x1080i50 */
+ { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
+ /* HD - 2048x1080i59.94 */
+ /* HD - 2048x1080i60 */
+ { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
+ /* 3G - 1920x1080p47.95 */
+ /* 3G - 1920x1080p48 */
+ { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
+
+ /* 3G - 1920x1080p50 148.5 */
+ { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
+ /* 3G - 1920x1080p59.94 148.5/1.001 */
+ /* 3G - 1920x1080p60 148.5 */
+ { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
+
+ /* 3G - 2048x1080p47.95 */
+ /* 3G - 2048x1080p48 */
+ { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
+ /* 3G - 2048x1080p50 */
+ { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
+ /* 3G - 2048x1080p59.94 */
+ /* 3G - 2048x1080p60 */
+ { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
+
+ /* 6G - 3840X2160p23.98 */
+ /* 6G - 3840X2160p24 */
+ { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
+ /* 6G - 3840X2160p25 */
+ { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
+ /* 6G - 3840X2160p29.97 */
+ /* 6G - 3840X2160p30 */
+ { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
+ /* 6G - 4096X2160p23.98 */
+ /* 6G - 4096X2160p24 */
+ { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
+ /* 6G - 4096X2160p25 */
+ { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
+ /* 6G - 4096X2160p29.97 */
+ /* 6G - 4096X2160p30 */
+ { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
+ /* 12G - 3840X2160p47.95 */
+ /* 12G - 3840X2160p48 */
+ { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
+
+ /* 12G - 3840X2160p50 */
+ { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
+ /* 12G - 3840X2160p59.94 */
+ /* 12G - 3840X2160p60 */
+ { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
+
+ /* 12G - 4096X2160p47.95 */
+ /* 12G - 4096X2160p48 */
+ { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
+
+ /* 12G - 4096X2160p50 */
+ { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
+ /* 12G - 4096X2160p59.94 */
+ /* 12G - 4096X2160p60 */
+ { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
+};
+
+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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
+{
+ xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
+}
+
+static inline 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 val;
+ struct device *dev = core->dev;
+
+ mask &= XSDIRX_DETECT_ALL_MODES;
+ if (!mask) {
+ dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
+ return -EINVAL;
+ }
+
+ dev_dbg(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(dev, "Detect multiple modes\n");
+
+ if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
+ val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
+ if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
+ val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
+ if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
+ val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
+ if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
+ val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
+ if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
+ val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
+ if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
+ val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
+
+ val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
+ } else {
+ /* Fixed Mode */
+ u32 forced_mode_mask;
+
+ dev_dbg(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;
+ default:
+ forced_mode_mask = 0;
+ }
+ dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
+ forced_mode_mask);
+ val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
+ }
+
+ dev_dbg(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 inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
+{
+ xsdirxss_clr(core, XSDIRX_IER_REG, mask);
+}
+
+static inline 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 inline 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)
+{
+ u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
+
+ /*
+ * TODO - Enable YUV444/RBG format in the bridge based
+ * on BYTE3 color format.
+ * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
+ */
+ if (enable)
+ xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
+ else
+ xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, 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 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;
+ }
+}
+
+static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
+{
+ struct clk *gtclk;
+ unsigned long clkrate;
+ int ret, is_frac;
+ struct xsdirxss_core *core = &state->core;
+ u32 mode;
+ static int prev_is_frac = -1;
+
+ mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
+ mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
+
+ /*
+ * TODO: For now, don't change the clock rate for any mode except 12G.
+ * In future, configure gt clock for all modes and enable clock only
+ * when needed (stream on/off).
+ */
+ if (mode != XSDIRX_MODE_12GI_MASK && mode != XSDIRX_MODE_12GF_MASK)
+ return;
+
+ is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
+
+ if (prev_is_frac == is_frac)
+ return;
+
+ xsdirx_core_disable(core);
+ xsdirx_globalintr(core, false);
+ xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
+
+ /* get sdi_rx_clk */
+ gtclk = core->clks[1].clk;
+ clkrate = clk_get_rate(gtclk);
+ is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
+
+ /* calcualte clkrate */
+ if (!is_frac)
+ clkrate = CLK_INT;
+ else
+ clkrate = (CLK_INT * 1000) / 1001;
+
+ ret = clk_set_rate(gtclk, clkrate);
+ if (ret)
+ dev_err(core->dev, "failed to set clk rate = %d\n", ret);
+
+ clkrate = clk_get_rate(gtclk);
+
+ dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
+ clkrate, is_frac);
+
+ xsdirx_framer(core, state->framer_enable);
+ xsdirx_setedherrcnttrigger(core, state->edhmask);
+ xsdirx_setvidlockwindow(core, state->vidlockwin);
+ xsdirx_set_modedetect(core, state->searchmask);
+ xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
+ xsdirx_globalintr(core, true);
+ xsdirx_core_enable(core);
+
+ prev_is_frac = is_frac;
+}
+
+/**
+ * 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;
+ struct device *dev = core->dev;
+ 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->format;
+ u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
+
+ 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
+ active_luma = mask_and_rshift(payload,
+ XST352_BYTE3_ACT_LUMA_COUNT);
+ pic_type = mask_and_rshift(payload, XST352_BYTE2_PIC_TYPE);
+ framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
+ tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
+ sampling = mask_and_rshift(payload, XST352_BYTE3_COLOR_FORMAT);
+ bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
+ } else {
+ dev_dbg(dev, "No ST352 payload available : Mode = %d\n", mode);
+ framerate = mask_and_rshift(val, XSDIRX_TS_DET_STAT_RATE);
+ tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
+ }
+
+ if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
+ (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
+ dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc = %d\n",
+ bpc, core->bpc);
+ return -EINVAL;
+ }
+
+ family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
+ state->ts_is_interlaced = tscan ? false : true;
+
+ dev_dbg(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(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(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(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(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(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(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;
+ case XST352_BYTE1_ST2081_10_2_1080L_6G:
+ format->height = 1080;
+ if (active_luma)
+ format->width = 2048;
+ else
+ format->width = 1920;
+ break;
+ default:
+ dev_dbg(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(dev, "Unknown 12G Mode SMPTE standard\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid Mode\n");
+ return -EINVAL;
+ }
+
+ if (valid) {
+ if (pic_type)
+ format->field = V4L2_FIELD_NONE;
+ else
+ format->field = V4L2_FIELD_ALTERNATE;
+
+ if (format->height == 1080 && pic_type && !tscan)
+ format->field = V4L2_FIELD_ALTERNATE;
+
+ /*
+ * In 3GB DL pSF mode the video is similar to interlaced.
+ * So though it is a progressive video, its transport is
+ * interlaced and is sent as two width x (height/2) buffers.
+ */
+ if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
+ if (state->ts_is_interlaced)
+ format->field = V4L2_FIELD_ALTERNATE;
+ else
+ format->field = V4L2_FIELD_NONE;
+ }
+ }
+
+ if (format->field == V4L2_FIELD_ALTERNATE)
+ format->height = format->height / 2;
+
+ switch (sampling) {
+ case XST352_BYTE3_COLOR_FORMAT_422:
+ if (core->bpc == 10)
+ format->code = MEDIA_BUS_FMT_UYVY10_1X20;
+ else
+ format->code = MEDIA_BUS_FMT_UYVY12_1X24;
+ break;
+ case XST352_BYTE3_COLOR_FORMAT_420:
+ case XST352_BYTE3_COLOR_FORMAT_YUV444:
+ case XST352_BYTE3_COLOR_FORMAT_GBR:
+ format->code = 0;
+ dev_dbg(dev, "No corresponding media bus formats\n");
+ break;
+ default:
+ dev_err(dev, "Unsupported color format : %d\n", sampling);
+ return -EINVAL;
+ }
+
+ xsdirxss_get_framerate(&state->frame_interval, framerate);
+
+ dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
+ state->frame_interval.numerator,
+ state->frame_interval.denominator);
+ dev_dbg(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 writing 1 to corresponding bit.
+ *
+ * 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;
+ struct device *dev = core->dev;
+ u32 status;
+
+ status = xsdirxss_read(core, XSDIRX_ISR_REG);
+ xsdirxss_write(core, XSDIRX_ISR_REG, status);
+ dev_dbg(dev, "interrupt status = 0x%08x\n", status);
+
+ if (status & XSDIRX_INTR_VIDLOCK_MASK ||
+ status & XSDIRX_INTR_VIDUNLOCK_MASK) {
+ u32 val1, val2;
+
+ dev_dbg(dev, "video lock/unlock interrupt\n");
+
+ xsdirx_streamflow_control(core, false);
+ state->streaming = false;
+
+ 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
+ dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
+
+ if (!xsdirx_get_stream_properties(state)) {
+ state->vidlocked = true;
+ xsdirxss_set_gtclk(state);
+ } else {
+ dev_err(dev, "Unable to get stream properties!\n");
+ state->vidlocked = false;
+ }
+ } else {
+ dev_dbg(dev, "video unlock interrupt\n");
+ state->vidlocked = false;
+ }
+
+ 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);
+ }
+
+ if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
+ dev_dbg(dev, "Video in to AXI4 Stream core underflow interrupt\n");
+
+ memset(&state->event, 0, sizeof(state->event));
+ state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
+ v4l2_subdev_notify_event(&state->subdev, &state->event);
+ }
+
+ if (status & XSDIRX_INTR_OVERFLOW_MASK) {
+ dev_dbg(dev, "Video in to AXI4 Stream core overflow interrupt\n");
+
+ memset(&state->event, 0, sizeof(state->event));
+ state->event.type = V4L2_EVENT_XILINX_SDIRX_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_XILINX_SDIRX_UNDERFLOW:
+ case V4L2_EVENT_XILINX_SDIRX_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;
+ struct device *dev = core->dev;
+
+ dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
+ ctrl->id, ctrl->val);
+
+ if (xsdirxss->streaming) {
+ dev_err(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);
+ xsdirxss->framer_enable = ctrl->val;
+ break;
+ case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
+ xsdirx_setvidlockwindow(core, ctrl->val);
+ xsdirxss->vidlockwin = ctrl->val;
+ break;
+ case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
+ xsdirx_setedherrcnttrigger(core, ctrl->val);
+ xsdirxss->edhmask = ctrl->val;
+ break;
+ case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
+ if (ctrl->val) {
+ if (core->mode == XSDIRXSS_SDI_STD_3G) {
+ dev_dbg(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(dev, "Upto 6G supported\n");
+ ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
+ BIT(XSDIRX_MODE_12GF_OFFSET));
+ }
+
+ ret = xsdirx_set_modedetect(core, ctrl->val);
+ if (!ret)
+ xsdirxss->searchmask = ctrl->val;
+ } else {
+ dev_err(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;
+ struct device *dev = core->dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
+ if (!xsdirxss->vidlocked) {
+ dev_err(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(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(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(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(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(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(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
+ return -EINVAL;
+ }
+ dev_dbg(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 i;
+
+ v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
+ for (i = 0; i < 0x28; i++) {
+ u32 data;
+
+ 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;
+}
+
+/**
+ * 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;
+ struct device *dev = core->dev;
+
+ if (enable) {
+ if (!xsdirxss->vidlocked) {
+ dev_dbg(dev, "Video is not locked\n");
+ return -EINVAL;
+ }
+ if (xsdirxss->streaming) {
+ dev_dbg(dev, "Already streaming\n");
+ return -EINVAL;
+ }
+
+ xsdirx_streamflow_control(core, true);
+ xsdirxss->streaming = true;
+ dev_dbg(dev, "Streaming started\n");
+ } else {
+ if (!xsdirxss->streaming) {
+ dev_dbg(dev, "Stopped streaming already\n");
+ return 0;
+ }
+
+ xsdirx_streamflow_control(core, false);
+ xsdirxss->streaming = false;
+ dev_dbg(dev, "Streaming stopped\n");
+ }
+
+ return 0;
+}
+
+/**
+ * xsdirxss_g_input_status - It is used to determine if the video signal
+ * is present / locked onto or not.
+ *
+ * @sd: V4L2 Sub device
+ * @status: status of signal locked
+ *
+ * This is used to determine if the video signal is present and locked onto
+ * by the SDI Rx core or not based on vidlocked flag.
+ *
+ * Return: zero on success
+ */
+static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+
+ if (!xsdirxss->vidlocked)
+ *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
+ else
+ *status = 0;
+
+ 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->format;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * xsdirxss_init_cfg - Initialise the pad format config to default
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ *
+ * This function is used to initialize the pad format with the default
+ * values.
+ *
+ * Return: 0 on success
+ */
+static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(sd, cfg, 0);
+ *format = xsdirxss->default_format;
+
+ return 0;
+}
+
+/**
+ * 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_enum_mbus_code - Handle pixel format enumeration
+ * @sd: pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad configuration
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ *
+ * Return: -EINVAL or zero on success
+ */
+static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
+ u32 index = code->index;
+ u32 maxindex;
+
+ if (xsdirxss->core.bpc == 10)
+ maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
+ else
+ maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
+
+ if (code->pad || index >= maxindex)
+ return -EINVAL;
+
+ if (xsdirxss->core.bpc == 10)
+ code->code = xsdirxss_10bpc_mbus_fmts[index];
+ else
+ code->code = xsdirxss_12bpc_mbus_fmts[index];
+
+ return 0;
+}
+
+/**
+ * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
+ * @sd: pointer to v4l2 subdev structure
+ * @timings: DV timings structure to be returned.
+ *
+ * Return: -EINVAL incase of invalid index and pad or zero on success
+ */
+static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ if (timings->index >= ARRAY_SIZE(fmt_cap))
+ return -EINVAL;
+
+ if (timings->pad != 0)
+ return -EINVAL;
+
+ timings->timings = fmt_cap[timings->index];
+ return 0;
+}
+
+/**
+ * xsdirxss_query_dv_timings: Query for the current DV timings
+ * @sd: pointer to v4l2 subdev structure
+ * @timings: DV timings structure to be returned.
+ *
+ * Return: -ENOLCK when video is not locked, -ERANGE when corresponding timing
+ * entry is not found or zero on success.
+ */
+static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct xsdirxss_state *state = to_xsdirxssstate(sd);
+ unsigned int i;
+
+ if (!state->vidlocked)
+ return -ENOLCK;
+
+ for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
+ if (state->format.width == xsdirxss_dv_timings[i].width &&
+ state->format.height == xsdirxss_dv_timings[i].height &&
+ state->frame_interval.denominator ==
+ (xsdirxss_dv_timings[i].fps * 1000)) {
+ *timings = xsdirxss_dv_timings[i].format;
+ return 0;
+ }
+ }
+
+ return -ERANGE;
+}
+
+/* -----------------------------------------------------------------------------
+ * 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 const 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 const 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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
+ .g_frame_interval = xsdirxss_g_frame_interval,
+ .s_stream = xsdirxss_s_stream,
+ .g_input_status = xsdirxss_g_input_status,
+ .query_dv_timings = xsdirxss_query_dv_timings,
+};
+
+static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
+ .init_cfg = xsdirxss_init_cfg,
+ .get_fmt = xsdirxss_get_format,
+ .set_fmt = xsdirxss_set_format,
+ .enum_mbus_code = xsdirxss_enum_mbus_code,
+ .enum_dv_timings = xsdirxss_enum_dv_timings,
+};
+
+static const struct v4l2_subdev_ops xsdirxss_ops = {
+ .core = &xsdirxss_core_ops,
+ .video = &xsdirxss_video_ops,
+ .pad = &xsdirxss_pad_ops
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
+{
+ struct device_node *node = xsdirxss->core.dev->of_node;
+ struct xsdirxss_core *core = &xsdirxss->core;
+ struct device *dev = core->dev;
+ struct fwnode_handle *ep, *rep;
+ int ret;
+ const char *sdi_std;
+
+ core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
+ dev_dbg(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(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(dev, "Invalid Line Rate\n");
+ return -EINVAL;
+ }
+ dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
+ core->mode);
+
+ ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
+ if (ret < 0) {
+ dev_err(dev, "failed to get xlnx,bpp\n");
+ return ret;
+ }
+
+ if (core->bpc != 10 && core->bpc != 12) {
+ dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
+ core->bpc);
+ return -EINVAL;
+ }
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep) {
+ dev_err(dev, "no source port found");
+ ret = -EINVAL;
+ goto dt_parse_done;
+ }
+
+ rep = fwnode_graph_get_remote_endpoint(ep);
+ if (!rep) {
+ dev_err(dev, "no remote sink endpoint found");
+ ret = -EINVAL;
+ }
+
+ fwnode_handle_put(rep);
+dt_parse_done:
+ fwnode_handle_put(ep);
+ return ret;
+}
+
+static int xsdirxss_probe(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev;
+ struct xsdirxss_state *xsdirxss;
+ struct xsdirxss_core *core;
+ struct device *dev;
+ 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;
+ dev = core->dev;
+
+ /* Register interrupt handler */
+ core->irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(dev, core->irq, NULL,
+ xsdirxss_irq_handler, IRQF_ONESHOT,
+ dev_name(dev), xsdirxss);
+ if (ret) {
+ dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
+ ret);
+ return ret;
+ }
+
+ core->num_clks = ARRAY_SIZE(xsdirxss_clks);
+ core->clks = devm_kcalloc(dev, core->num_clks,
+ sizeof(*core->clks), GFP_KERNEL);
+ if (!core->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < core->num_clks; i++)
+ core->clks[i].id = xsdirxss_clks[i];
+
+ ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
+ if (ret)
+ return ret;
+
+ ret = xsdirxss_parse_of(xsdirxss);
+ if (ret < 0)
+ goto clk_err;
+
+ core->iomem = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(core->iomem)) {
+ ret = PTR_ERR(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->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ /* Initialize the default format */
+ if (core->bpc == 10)
+ xsdirxss->default_format.code = MEDIA_BUS_FMT_UYVY10_1X20;
+ else
+ xsdirxss->default_format.code = MEDIA_BUS_FMT_UYVY12_1X24;
+ 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->format = xsdirxss->default_format;
+
+ /* Initialize V4L2 subdevice and media entity */
+ subdev = &xsdirxss->subdev;
+ v4l2_subdev_init(subdev, &xsdirxss_ops);
+
+ subdev->dev = &pdev->dev;
+ strscpy(subdev->name, dev_name(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->pad);
+ if (ret < 0)
+ goto error;
+
+ /* Initialise and register the controls */
+ num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
+
+ if (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(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(dev, "Failed to add %s ctrl\n",
+ xsdirxss_ctrls[i].name);
+ goto error;
+ }
+ }
+
+ if (core->include_edh) {
+ for (i = 0; i < num_edh_ctrls; i++) {
+ struct v4l2_ctrl *ctrl;
+
+ dev_dbg(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(dev, "Failed to add %s ctrl\n",
+ xsdirxss_edh_ctrls[i].name);
+ goto error;
+ }
+ }
+ }
+
+ if (xsdirxss->ctrl_handler.error) {
+ dev_err(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(dev, "failed to set controls\n");
+ goto error;
+ }
+
+ platform_set_drvdata(pdev, xsdirxss);
+
+ ret = v4l2_async_register_subdev(subdev);
+ if (ret < 0) {
+ dev_err(dev, "failed to register subdev\n");
+ goto error;
+ }
+
+ xsdirxss->streaming = false;
+
+ xsdirx_core_enable(core);
+
+ dev_info(dev, "probe success\n");
+
+ return 0;
+error:
+ v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
+ media_entity_cleanup(&subdev->entity);
+ xsdirx_globalintr(core, false);
+ xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
+clk_err:
+ clk_bulk_disable_unprepare(core->num_clks, core->clks);
+ return ret;
+}
+
+static int xsdirxss_remove(struct platform_device *pdev)
+{
+ struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
+ struct xsdirxss_core *core = &xsdirxss->core;
+ struct v4l2_subdev *subdev = &xsdirxss->subdev;
+
+ v4l2_async_unregister_subdev(subdev);
+ v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
+ media_entity_cleanup(&subdev->entity);
+
+ xsdirx_globalintr(core, false);
+ xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
+ xsdirx_core_disable(core);
+ xsdirx_streamflow_control(core, false);
+
+ clk_bulk_disable_unprepare(core->num_clks, core->clks);
+
+ return 0;
+}
+
+static const struct of_device_id xsdirxss_of_id_table[] = {
+ { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
+ { }
+};
+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 <[email protected]>");
+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 000000000000..6f2a093968d9
--- /dev/null
+++ b/include/uapi/linux/xilinx-sdirxss.h
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Xilinx SDI Rx Subsystem mode, event, custom timings
+ * and flag definitions.
+ *
+ * Copyright (C) 2019 - 2020 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <[email protected]>
+ */
+
+#ifndef __UAPI_XILINX_SDIRXSS_H__
+#define __UAPI_XILINX_SDIRXSS_H__
+
+#include <linux/types.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/videodev2.h>
+
+/*
+ * Events
+ *
+ * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core underflowed
+ * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core overflowed
+ */
+#define V4L2_EVENT_XILINX_SDIRX_CLASS (V4L2_EVENT_PRIVATE_START | 0x200)
+#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
+#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
+
+/*
+ * 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
+
+/* Xilinx DV timings not in mainline yet */
+#define XLNX_V4L2_DV_BT_2048X1080P24 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_2048X1080P25 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_2048X1080P30 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_2048X1080I48 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_2048X1080I50 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_2048X1080I60 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_1920X1080P48 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_2048X1080P48 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_2048X1080P50 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_2048X1080P60 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_3840X2160P48 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_4096X2160P48 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#define XLNX_V4L2_DV_BT_1920X1080I48 { \
+ .type = V4L2_DV_BT_656_1120, \
+ V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
+ 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
+ V4L2_DV_BT_STD_SDI) \
+}
+
+#endif /* __UAPI_XILINX_SDIRXSS_H__ */
diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
index b6441fe705c5..e9de65e82642 100644
--- a/include/uapi/linux/xilinx-v4l2-controls.h
+++ b/include/uapi/linux/xilinx-v4l2-controls.h
@@ -71,4 +71,71 @@
/* Noise level */
#define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17)

+/*
+ * Xilinx SDI Rx Subsystem
+ */
+
+/* The base for the sdi rx driver controls.
+ * We reserve 32 controls for this driver.
+ *
+ * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
+ * EDH is enabled.
+ * The controls which can be set should only be set before enabling
+ * streaming. The controls which can be got should be called while
+ * streaming to get correct values.
+ * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query dv timing
+ * returns a valid timing.
+ */
+
+#define V4L2_CID_XILINX_SDIRX (V4L2_CID_XILINX_BASE + 0x20)
+
+/* Framer Control to enable or disable the framer */
+#define V4L2_CID_XILINX_SDIRX_FRAMER (V4L2_CID_XILINX_SDIRX + 1)
+/*
+ * Video Lock Window Control to set the video lock window value
+ * This is the amount of time the mode and transport stream need
+ * to be locked before a video lock interrupt occurs.
+ */
+#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW (V4L2_CID_XILINX_SDIRX + 2)
+/* EDH Error Mask Control to enable EDH error count */
+#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE (V4L2_CID_XILINX_SDIRX + 3)
+/*
+ * Mode search Control to pass the bit mask of modes to detect.
+ *
+ * bit 0 set to detect SD mode,
+ * bit 1 set to detect HD mode,
+ * bit 2 set to detect 3G (3GA & 3GB) mode,
+ * bit 3 set to detect 6G mode,
+ * bit 4 set to detect 12G integer frame rate mode,
+ * bit 5 set to detect 12G fractional frame rate mode,
+ */
+#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES (V4L2_CID_XILINX_SDIRX + 4)
+/*
+ * Get Detected Mode control
+ *
+ * Control Value - Mode detected
+ * 0 - SD
+ * 1 - HD
+ * 2 - 3G (3GA & 3GB)
+ * 3 - 6G
+ * 4 - 12G integer frame rate
+ * 5 - 12G fractional frame rate
+ */
+#define V4L2_CID_XILINX_SDIRX_MODE_DETECT (V4L2_CID_XILINX_SDIRX + 5)
+/* Get number of CRC errors status control */
+#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 whether it is interlaced or not */
+#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED (V4L2_CID_XILINX_SDIRX + 9)
+/* Get number of Active Streams */
+#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS (V4L2_CID_XILINX_SDIRX + 10)
+/*
+ * Get if the detected mode is 3GB.
+ * Can be used to distinguished between 3GA and 3GB
+ */
+#define V4L2_CID_XILINX_SDIRX_IS_3GB (V4L2_CID_XILINX_SDIRX + 11)
+
#endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
--
2.21.0

2020-04-29 14:22:09

by Vishal Sagar

[permalink] [raw]
Subject: [PATCH v2 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem

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 <[email protected]>
---
v2
- Removed references to xlnx,video*
- Fixed as per Sakari Ailus and Rob Herring's comments
- Converted to yaml format

.../bindings/media/xilinx/xlnx,sdirxss.yaml | 132 ++++++++++++++++++
1 file changed, 132 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml

diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
new file mode 100644
index 000000000000..9133ad19df55
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/xilinx/xlnx,sdirxss.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+
+title: Xilinx SMPTE UHD-SDI Receiver Subsystem
+
+maintainers:
+ - Vishal Sagar <[email protected]>
+
+description: |
+ 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 upto 12G mode 8 data streams and output
+ a dual pixel per clock RGB/YUV444,422/420 10/12 bits per component AXI4-Stream.
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - xlnx,v-smpte-uhdsdi-rx-ss-2.0
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ description: List of clock specifiers
+ items:
+ - description: AXI4-Lite clock
+ - description: SMPTE UHD-SDI Rx core clock
+ - description: Video clock
+
+ clock-names:
+ items:
+ - const: s_axi_aclk
+ - const: sdi_rx_clk
+ - const: video_out_clk
+
+ xlnx,bpp:
+ description: Bits per pixel supported. Can be 10 or 12 bits per pixel only.
+ allOf:
+ - $ref: "/schemas/types.yaml#/definitions/uint32"
+ - enum: [10, 12]
+
+ xlnx,line-rate:
+ description: |
+ The maximum mode supported by the design. Possible values are as below
+ 12G_SDI_8DS - 12G mode with 8 data streams
+ 6G_SDI - 6G mode
+ 3G_SDI - 3G mode
+ enum:
+ - 12G_SDI_8DS
+ - 6G_SDI
+ - 3G_SDI
+
+ xlnx,include-edh:
+ type: boolean
+ description: |
+ This is present when the Error Detection and Handling processor is
+ enabled in design.
+
+ ports:
+ type: object
+ description: |
+ 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.
+ properties:
+ port@0:
+ type: object
+ description: Source port
+ properties:
+ reg:
+ const: 0
+ endpoint:
+ type: object
+ properties:
+ remote-endpoint: true
+ required:
+ - remote-endpoint
+ additionalProperties: false
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - xlnx,line-rate
+ - xlnx,bpp
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ uhdsdirxss: v-smpte-uhdsdi-rxss@80000000 {
+ compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0";
+ 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";
+ xlnx,bpp = <10>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ sdirx_out: endpoint {
+ remote-endpoint = <&vcap_sdirx_in>;
+ };
+ };
+ };
+ };
--
2.21.0

2020-05-06 09:57:35

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Hi Vishal,

Thank you for this patch.

I have some comments below:

On 29/04/2020 16:17, 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 10/12 bpc YUV 422 media bus format currently. 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 IP configuration.
>
> Signed-off-by: Vishal Sagar <[email protected]>
> ---
> v2
> - Added DV timing support based on Hans Verkuilś feedback
> - More documentation to custom v4l controls and events
> - Fixed Hyunś comments
> - Added macro for masking and shifting as per Joe Perches comments
> - Updated to latest as per Xilinx github repo driver like
> adding new DV timings not in mainline yet uptill 03/21/20
>
> drivers/media/platform/xilinx/Kconfig | 11 +
> drivers/media/platform/xilinx/Makefile | 1 +
> .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> include/uapi/linux/xilinx-sdirxss.h | 179 ++
> include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> 5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -12,6 +12,17 @@ config VIDEO_XILINX
>
> if VIDEO_XILINX
>
> +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 4cdc0b1ec7a5..6c375f03f587 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -2,6 +2,7 @@
>
> xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
>
> +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,2162 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <[email protected]>
> + *
> + */
> +
> +#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-dv-timings.h>
> +#include <linux/v4l2-subdev.h>
> +#include <linux/xilinx-sdirxss.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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12, 10)
> +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> +
> +#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_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)
> +
> +#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_BYTE1_MASK 0xFF
> +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> +#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
> +#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_OFFSET 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_YUV444 0x1
> +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> +
> +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> +
> +#define CLK_INT 148500000UL
> +
> +#define rshift_and_mask(val, type) \
> + (((val) >> type##_OFFSET) & type##_MASK)
> +
> +#define mask_and_rshift(val, type) \
> + (((val) & type##_MASK) >> type##_OFFSET)
> +
> +/**
> + * 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
> + * @clks: array of clocks
> + * @num_clks: number of clocks
> + * @bpc: Bits per component, can be 10 or 12
> + */
> +struct xsdirxss_core {
> + struct device *dev;
> + void __iomem *iomem;
> + int irq;
> + bool include_edh;
> + int mode;
> + struct clk_bulk_data *clks;
> + int num_clks;
> + u32 bpc;
> +};
> +
> +/**
> + * 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
> + * @format: Active V4L2 format on source pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @pad: source media pad
> + * @vidlockwin: Video lock window value set by control
> + * @edhmask: EDH mask set by control
> + * @searchmask: Search mask set by control
> + * @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.
> + * @framer_enable: Flag for framer enabled or not set by control
> + *
> + * 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 format;
> + struct v4l2_mbus_framefmt default_format;
> + struct v4l2_fract frame_interval;
> + struct media_pad pad;
> + u32 vidlockwin;
> + u32 edhmask;
> + u16 searchmask;
> + bool streaming;
> + bool vidlocked;
> + bool ts_is_interlaced;
> + bool framer_enable;
> +};
> +
> +/* List of clocks required by UHD-SDI Rx subsystem */
> +static const char * const xsdirxss_clks[] = {
> + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> +};
> +
> +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> + MEDIA_BUS_FMT_UYVY10_1X20,
> +};
> +
> +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> + MEDIA_BUS_FMT_UYVY12_1X24,
> +};
> +
> +static const struct v4l2_dv_timings fmt_cap[] = {
> + V4L2_DV_BT_SDI_720X487I60,
> + V4L2_DV_BT_CEA_720X576I50,
> + V4L2_DV_BT_CEA_1280X720P24,
> + V4L2_DV_BT_CEA_1280X720P25,
> + V4L2_DV_BT_CEA_1280X720P30,
> + V4L2_DV_BT_CEA_1280X720P50,
> + V4L2_DV_BT_CEA_1280X720P60,
> + V4L2_DV_BT_CEA_1920X1080P24,
> + V4L2_DV_BT_CEA_1920X1080P30,
> + V4L2_DV_BT_CEA_1920X1080I50,
> + V4L2_DV_BT_CEA_1920X1080I60,
> + V4L2_DV_BT_CEA_1920X1080P50,
> + V4L2_DV_BT_CEA_1920X1080P60,
> + V4L2_DV_BT_CEA_3840X2160P24,
> + V4L2_DV_BT_CEA_3840X2160P30,
> + V4L2_DV_BT_CEA_3840X2160P50,
> + V4L2_DV_BT_CEA_3840X2160P60,
> + V4L2_DV_BT_CEA_4096X2160P24,
> + V4L2_DV_BT_CEA_4096X2160P25,
> + V4L2_DV_BT_CEA_4096X2160P30,
> + V4L2_DV_BT_CEA_4096X2160P50,
> + V4L2_DV_BT_CEA_4096X2160P60,
> +
> + XLNX_V4L2_DV_BT_2048X1080P24,
> + XLNX_V4L2_DV_BT_2048X1080P25,
> + XLNX_V4L2_DV_BT_2048X1080P30,
> + XLNX_V4L2_DV_BT_2048X1080I48,
> + XLNX_V4L2_DV_BT_2048X1080I50,
> + XLNX_V4L2_DV_BT_2048X1080I60,
> + XLNX_V4L2_DV_BT_2048X1080P48,
> + XLNX_V4L2_DV_BT_2048X1080P50,
> + XLNX_V4L2_DV_BT_2048X1080P60,
> + XLNX_V4L2_DV_BT_1920X1080P48,
> + XLNX_V4L2_DV_BT_1920X1080I48,
> + XLNX_V4L2_DV_BT_3840X2160P48,
> + XLNX_V4L2_DV_BT_4096X2160P48,
> +};
> +
> +struct xsdirxss_dv_map {
> + u32 width;
> + u32 height;
> + u32 fps;
> + struct v4l2_dv_timings format;
> +};
> +
> +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> + /* SD - 720x487i60 */
> + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> + /* SD - 720x576i50 */
> + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> + /* HD - 1280x720p23.98 */
> + /* HD - 1280x720p24 */
> + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> + /* HD - 1280x720p25 */
> + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> + /* HD - 1280x720p29.97 */
> + /* HD - 1280x720p30 */
> + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> + /* HD - 1280x720p50 */
> + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> + /* HD - 1280x720p59.94 */
> + /* HD - 1280x720p60 */
> + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> + /* HD - 1920x1080p23.98 */
> + /* HD - 1920x1080p24 */
> + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> + /* HD - 1920x1080p25 */
> + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> + /* HD - 1920x1080p29.97 */
> + /* HD - 1920x1080p30 */
> + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> +
> + /* HD - 2048x1080p23.98 */
> + /* HD - 2048x1080p24 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> + /* HD - 2048x1080p25 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> + /* HD - 2048x1080p29.97 */
> + /* HD - 2048x1080p30 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> + /* HD - 1920x1080i47.95 */
> + /* HD - 1920x1080i48 */
> + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> +
> + /* HD - 1920x1080i50 */
> + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> + /* HD - 1920x1080i59.94 */
> + /* HD - 1920x1080i60 */
> + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> +
> + /* HD - 2048x1080i47.95 */
> + /* HD - 2048x1080i48 */
> + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> + /* HD - 2048x1080i50 */
> + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> + /* HD - 2048x1080i59.94 */
> + /* HD - 2048x1080i60 */
> + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> + /* 3G - 1920x1080p47.95 */
> + /* 3G - 1920x1080p48 */
> + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> +
> + /* 3G - 1920x1080p50 148.5 */
> + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> + /* 3G - 1920x1080p59.94 148.5/1.001 */
> + /* 3G - 1920x1080p60 148.5 */
> + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> +
> + /* 3G - 2048x1080p47.95 */
> + /* 3G - 2048x1080p48 */
> + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> + /* 3G - 2048x1080p50 */
> + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> + /* 3G - 2048x1080p59.94 */
> + /* 3G - 2048x1080p60 */
> + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> +
> + /* 6G - 3840X2160p23.98 */
> + /* 6G - 3840X2160p24 */
> + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> + /* 6G - 3840X2160p25 */
> + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> + /* 6G - 3840X2160p29.97 */
> + /* 6G - 3840X2160p30 */
> + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> + /* 6G - 4096X2160p23.98 */
> + /* 6G - 4096X2160p24 */
> + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> + /* 6G - 4096X2160p25 */
> + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> + /* 6G - 4096X2160p29.97 */
> + /* 6G - 4096X2160p30 */
> + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> + /* 12G - 3840X2160p47.95 */
> + /* 12G - 3840X2160p48 */
> + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> +
> + /* 12G - 3840X2160p50 */
> + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> + /* 12G - 3840X2160p59.94 */
> + /* 12G - 3840X2160p60 */
> + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> +
> + /* 12G - 4096X2160p47.95 */
> + /* 12G - 4096X2160p48 */
> + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> +
> + /* 12G - 4096X2160p50 */
> + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> + /* 12G - 4096X2160p59.94 */
> + /* 12G - 4096X2160p60 */
> + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> +};
> +
> +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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static inline 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 val;
> + struct device *dev = core->dev;
> +
> + mask &= XSDIRX_DETECT_ALL_MODES;
> + if (!mask) {
> + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> + return -EINVAL;
> + }
> +
> + dev_dbg(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(dev, "Detect multiple modes\n");
> +
> + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +
> + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> + } else {
> + /* Fixed Mode */
> + u32 forced_mode_mask;
> +
> + dev_dbg(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;
> + default:
> + forced_mode_mask = 0;
> + }
> + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> + forced_mode_mask);
> + val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> + }
> +
> + dev_dbg(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 inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static inline 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 inline 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)
> +{
> + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> +
> + /*
> + * TODO - Enable YUV444/RBG format in the bridge based
> + * on BYTE3 color format.
> + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> + */
> + if (enable)
> + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> + else
> + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, 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 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;
> + }
> +}
> +
> +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> +{
> + struct clk *gtclk;
> + unsigned long clkrate;
> + int ret, is_frac;
> + struct xsdirxss_core *core = &state->core;
> + u32 mode;
> + static int prev_is_frac = -1;
> +
> + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> + /*
> + * TODO: For now, don't change the clock rate for any mode except 12G.
> + * In future, configure gt clock for all modes and enable clock only
> + * when needed (stream on/off).
> + */
> + if (mode != XSDIRX_MODE_12GI_MASK && mode != XSDIRX_MODE_12GF_MASK)
> + return;
> +
> + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> +
> + if (prev_is_frac == is_frac)
> + return;
> +
> + xsdirx_core_disable(core);
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +
> + /* get sdi_rx_clk */
> + gtclk = core->clks[1].clk;
> + clkrate = clk_get_rate(gtclk);
> + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> +
> + /* calcualte clkrate */
> + if (!is_frac)
> + clkrate = CLK_INT;
> + else
> + clkrate = (CLK_INT * 1000) / 1001;
> +
> + ret = clk_set_rate(gtclk, clkrate);
> + if (ret)
> + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> +
> + clkrate = clk_get_rate(gtclk);
> +
> + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> + clkrate, is_frac);
> +
> + xsdirx_framer(core, state->framer_enable);
> + xsdirx_setedherrcnttrigger(core, state->edhmask);
> + xsdirx_setvidlockwindow(core, state->vidlockwin);
> + xsdirx_set_modedetect(core, state->searchmask);
> + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> + xsdirx_globalintr(core, true);
> + xsdirx_core_enable(core);
> +
> + prev_is_frac = is_frac;
> +}
> +
> +/**
> + * 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;
> + struct device *dev = core->dev;
> + 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->format;
> + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> +
> + 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> + active_luma = mask_and_rshift(payload,
> + XST352_BYTE3_ACT_LUMA_COUNT);
> + pic_type = mask_and_rshift(payload, XST352_BYTE2_PIC_TYPE);
> + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> + sampling = mask_and_rshift(payload, XST352_BYTE3_COLOR_FORMAT);
> + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> + } else {
> + dev_dbg(dev, "No ST352 payload available : Mode = %d\n", mode);
> + framerate = mask_and_rshift(val, XSDIRX_TS_DET_STAT_RATE);
> + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> + }
> +
> + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc = %d\n",
> + bpc, core->bpc);
> + return -EINVAL;
> + }
> +
> + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> + state->ts_is_interlaced = tscan ? false : true;
> +
> + dev_dbg(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(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(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(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(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(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(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;
> + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> + format->height = 1080;
> + if (active_luma)
> + format->width = 2048;
> + else
> + format->width = 1920;
> + break;
> + default:
> + dev_dbg(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(dev, "Unknown 12G Mode SMPTE standard\n");
> + return -EINVAL;
> + }
> + break;
> + default:
> + dev_err(dev, "Invalid Mode\n");
> + return -EINVAL;
> + }
> +
> + if (valid) {
> + if (pic_type)
> + format->field = V4L2_FIELD_NONE;
> + else
> + format->field = V4L2_FIELD_ALTERNATE;
> +
> + if (format->height == 1080 && pic_type && !tscan)
> + format->field = V4L2_FIELD_ALTERNATE;
> +
> + /*
> + * In 3GB DL pSF mode the video is similar to interlaced.
> + * So though it is a progressive video, its transport is
> + * interlaced and is sent as two width x (height/2) buffers.
> + */
> + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> + if (state->ts_is_interlaced)
> + format->field = V4L2_FIELD_ALTERNATE;
> + else
> + format->field = V4L2_FIELD_NONE;
> + }
> + }
> +
> + if (format->field == V4L2_FIELD_ALTERNATE)
> + format->height = format->height / 2;
> +
> + switch (sampling) {
> + case XST352_BYTE3_COLOR_FORMAT_422:
> + if (core->bpc == 10)
> + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> + else
> + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> + break;
> + case XST352_BYTE3_COLOR_FORMAT_420:
> + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> + case XST352_BYTE3_COLOR_FORMAT_GBR:
> + format->code = 0;
> + dev_dbg(dev, "No corresponding media bus formats\n");
> + break;
> + default:
> + dev_err(dev, "Unsupported color format : %d\n", sampling);
> + return -EINVAL;
> + }
> +
> + xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> + dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
> + state->frame_interval.numerator,
> + state->frame_interval.denominator);
> + dev_dbg(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 writing 1 to corresponding bit.
> + *
> + * 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;
> + struct device *dev = core->dev;
> + u32 status;
> +
> + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> +
> + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> + u32 val1, val2;
> +
> + dev_dbg(dev, "video lock/unlock interrupt\n");
> +
> + xsdirx_streamflow_control(core, false);
> + state->streaming = false;
> +
> + 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
> + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> +
> + if (!xsdirx_get_stream_properties(state)) {
> + state->vidlocked = true;
> + xsdirxss_set_gtclk(state);
> + } else {
> + dev_err(dev, "Unable to get stream properties!\n");
> + state->vidlocked = false;
> + }
> + } else {
> + dev_dbg(dev, "video unlock interrupt\n");
> + state->vidlocked = false;
> + }
> +
> + 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);
> + }
> +
> + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> + dev_dbg(dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }
> +
> + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> + dev_dbg(dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> + state->event.type = V4L2_EVENT_XILINX_SDIRX_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_XILINX_SDIRX_UNDERFLOW:
> + case V4L2_EVENT_XILINX_SDIRX_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;

This should call v4l2_ctrl_subscribe_event instead, otherwise control events
wouldn't be supported.

> + }
> + 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;
> + struct device *dev = core->dev;
> +
> + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> + ctrl->id, ctrl->val);
> +
> + if (xsdirxss->streaming) {
> + dev_err(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);
> + xsdirxss->framer_enable = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> + xsdirx_setvidlockwindow(core, ctrl->val);
> + xsdirxss->vidlockwin = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> + xsdirx_setedherrcnttrigger(core, ctrl->val);
> + xsdirxss->edhmask = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> + if (ctrl->val) {
> + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> + dev_dbg(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(dev, "Upto 6G supported\n");
> + ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> + BIT(XSDIRX_MODE_12GF_OFFSET));
> + }
> +
> + ret = xsdirx_set_modedetect(core, ctrl->val);
> + if (!ret)
> + xsdirxss->searchmask = ctrl->val;
> + } else {
> + dev_err(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;
> + struct device *dev = core->dev;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> + if (!xsdirxss->vidlocked) {
> + dev_err(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(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(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(dev, "Can't get values when video not locked!\n");
> + return -EINVAL;
> + }
> + ctrl->val = xsdirxss->ts_is_interlaced;

This control makes no sense: the v4l2_dv_timings struct will already tell you
if it is an interlaced format or not. Same for v4l2_mbus_framefmt.

> + break;
> + case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> + if (!xsdirxss->vidlocked) {
> + dev_err(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(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(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> + return -EINVAL;
> + }
> + dev_dbg(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 i;
> +
> + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> + for (i = 0; i < 0x28; i++) {
> + u32 data;
> +
> + 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;

I suggest calling v4l2_ctrl_subdev_log_status() as well to log all controls.

> +}
> +
> +/**
> + * 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;
> + struct device *dev = core->dev;
> +
> + if (enable) {
> + if (!xsdirxss->vidlocked) {
> + dev_dbg(dev, "Video is not locked\n");
> + return -EINVAL;
> + }
> + if (xsdirxss->streaming) {
> + dev_dbg(dev, "Already streaming\n");
> + return -EINVAL;
> + }
> +
> + xsdirx_streamflow_control(core, true);
> + xsdirxss->streaming = true;
> + dev_dbg(dev, "Streaming started\n");
> + } else {
> + if (!xsdirxss->streaming) {
> + dev_dbg(dev, "Stopped streaming already\n");
> + return 0;
> + }
> +
> + xsdirx_streamflow_control(core, false);
> + xsdirxss->streaming = false;
> + dev_dbg(dev, "Streaming stopped\n");
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_g_input_status - It is used to determine if the video signal
> + * is present / locked onto or not.
> + *
> + * @sd: V4L2 Sub device
> + * @status: status of signal locked
> + *
> + * This is used to determine if the video signal is present and locked onto
> + * by the SDI Rx core or not based on vidlocked flag.
> + *
> + * Return: zero on success
> + */
> +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> + if (!xsdirxss->vidlocked)
> + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> + else
> + *status = 0;
> +
> + 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->format;
> + default:
> + return NULL;
> + }
> +}
> +
> +/**
> + * xsdirxss_init_cfg - Initialise the pad format config to default
> + * @sd: Pointer to V4L2 Sub device structure
> + * @cfg: Pointer to sub device pad information structure
> + *
> + * This function is used to initialize the pad format with the default
> + * values.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> + struct v4l2_mbus_framefmt *format;
> +
> + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> + *format = xsdirxss->default_format;
> +
> + return 0;
> +}
> +
> +/**
> + * 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;

It should fill in the width and height based on the current DV timings.
Ditto for the field (I assume that's fixed as well based on whether this
is interlaced or not). I'm not sure how colorspace information is handled
for SDI.

> +
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> + * @sd: pointer to v4l2 subdev structure
> + * @cfg: V4L2 subdev pad configuration
> + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> + *
> + * Return: -EINVAL or zero on success
> + */
> +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> + u32 index = code->index;
> + u32 maxindex;
> +
> + if (xsdirxss->core.bpc == 10)
> + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> + else
> + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> +
> + if (code->pad || index >= maxindex)
> + return -EINVAL;
> +
> + if (xsdirxss->core.bpc == 10)
> + code->code = xsdirxss_10bpc_mbus_fmts[index];
> + else
> + code->code = xsdirxss_12bpc_mbus_fmts[index];
> +
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> + * @sd: pointer to v4l2 subdev structure
> + * @timings: DV timings structure to be returned.
> + *
> + * Return: -EINVAL incase of invalid index and pad or zero on success
> + */
> +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> + struct v4l2_enum_dv_timings *timings)
> +{
> + if (timings->index >= ARRAY_SIZE(fmt_cap))
> + return -EINVAL;
> +
> + if (timings->pad != 0)
> + return -EINVAL;
> +
> + timings->timings = fmt_cap[timings->index];
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_query_dv_timings: Query for the current DV timings
> + * @sd: pointer to v4l2 subdev structure
> + * @timings: DV timings structure to be returned.
> + *
> + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding timing
> + * entry is not found or zero on success.
> + */
> +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> + struct v4l2_dv_timings *timings)
> +{
> + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> + unsigned int i;
> +
> + if (!state->vidlocked)
> + return -ENOLCK;
> +
> + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> + if (state->format.width == xsdirxss_dv_timings[i].width &&
> + state->format.height == xsdirxss_dv_timings[i].height &&
> + state->frame_interval.denominator ==
> + (xsdirxss_dv_timings[i].fps * 1000)) {
> + *timings = xsdirxss_dv_timings[i].format;
> + return 0;
> + }
> + }
> +
> + return -ERANGE;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * 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 const 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 const 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,
> + }

All these controls need to be documented in the header. Some of these controls
may turn out to be controls that can be standardized for SDI receivers, others
might be more vendor or driver specific.

> +};
> +
> +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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> + .g_frame_interval = xsdirxss_g_frame_interval,
> + .s_stream = xsdirxss_s_stream,
> + .g_input_status = xsdirxss_g_input_status,
> + .query_dv_timings = xsdirxss_query_dv_timings,

This is missing g/s_dv_timings. You need to keep track of the current
timings in the driver state: typically userspace will query timings and
if that returns valid timings it will set them (s_dv_timings). With
g_dv_timings the last set timings are returned.

If the timings change then the driver stops streaming and reports the
SOURCE_CHANGE event, at which point userspace will query the new timings.

The reported mediabus format resolution only changes when s_dv_timings
is called: it's updated with the new width/height info.

In other words: the timings reported by g_dv_timings are controlled by
userspace, the timings reported by query_dv_timings reflect the actual
timings received on the SDI bus.

> +};
> +
> +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> + .init_cfg = xsdirxss_init_cfg,
> + .get_fmt = xsdirxss_get_format,
> + .set_fmt = xsdirxss_set_format,
> + .enum_mbus_code = xsdirxss_enum_mbus_code,
> + .enum_dv_timings = xsdirxss_enum_dv_timings,

This is missing dv_timings_cap.

> +};
> +
> +static const struct v4l2_subdev_ops xsdirxss_ops = {
> + .core = &xsdirxss_core_ops,
> + .video = &xsdirxss_video_ops,
> + .pad = &xsdirxss_pad_ops
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> + struct device_node *node = xsdirxss->core.dev->of_node;
> + struct xsdirxss_core *core = &xsdirxss->core;
> + struct device *dev = core->dev;
> + struct fwnode_handle *ep, *rep;
> + int ret;
> + const char *sdi_std;
> +
> + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> + dev_dbg(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(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(dev, "Invalid Line Rate\n");
> + return -EINVAL;
> + }
> + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> + core->mode);
> +
> + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> + if (ret < 0) {
> + dev_err(dev, "failed to get xlnx,bpp\n");
> + return ret;
> + }
> +
> + if (core->bpc != 10 && core->bpc != 12) {
> + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> + core->bpc);
> + return -EINVAL;
> + }
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> + FWNODE_GRAPH_ENDPOINT_NEXT);
> + if (!ep) {
> + dev_err(dev, "no source port found");
> + ret = -EINVAL;
> + goto dt_parse_done;
> + }
> +
> + rep = fwnode_graph_get_remote_endpoint(ep);
> + if (!rep) {
> + dev_err(dev, "no remote sink endpoint found");
> + ret = -EINVAL;
> + }
> +
> + fwnode_handle_put(rep);
> +dt_parse_done:
> + fwnode_handle_put(ep);
> + return ret;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *subdev;
> + struct xsdirxss_state *xsdirxss;
> + struct xsdirxss_core *core;
> + struct device *dev;
> + 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;
> + dev = core->dev;
> +
> + /* Register interrupt handler */
> + core->irq = platform_get_irq(pdev, 0);
> + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> + xsdirxss_irq_handler, IRQF_ONESHOT,
> + dev_name(dev), xsdirxss);
> + if (ret) {
> + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> + ret);
> + return ret;
> + }
> +
> + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> + core->clks = devm_kcalloc(dev, core->num_clks,
> + sizeof(*core->clks), GFP_KERNEL);
> + if (!core->clks)
> + return -ENOMEM;
> +
> + for (i = 0; i < core->num_clks; i++)
> + core->clks[i].id = xsdirxss_clks[i];
> +
> + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> + if (ret)
> + return ret;
> +
> + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> + if (ret)
> + return ret;
> +
> + ret = xsdirxss_parse_of(xsdirxss);
> + if (ret < 0)
> + goto clk_err;
> +
> + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(core->iomem)) {
> + ret = PTR_ERR(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->pad.flags = MEDIA_PAD_FL_SOURCE;
> +
> + /* Initialize the default format */
> + if (core->bpc == 10)
> + xsdirxss->default_format.code = MEDIA_BUS_FMT_UYVY10_1X20;
> + else
> + xsdirxss->default_format.code = MEDIA_BUS_FMT_UYVY12_1X24;
> + xsdirxss->default_format.field = V4L2_FIELD_NONE;
> + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;

This shouldn't be DEFAULT. Can you explain a bit how colorspace (or colorimetry
in general) is determined for SDI?

> + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> +
> + xsdirxss->format = xsdirxss->default_format;
> +
> + /* Initialize V4L2 subdevice and media entity */
> + subdev = &xsdirxss->subdev;
> + v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> + subdev->dev = &pdev->dev;
> + strscpy(subdev->name, dev_name(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->pad);
> + if (ret < 0)
> + goto error;
> +
> + /* Initialise and register the controls */
> + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> + if (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(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(dev, "Failed to add %s ctrl\n",
> + xsdirxss_ctrls[i].name);
> + goto error;
> + }
> + }
> +
> + if (core->include_edh) {
> + for (i = 0; i < num_edh_ctrls; i++) {
> + struct v4l2_ctrl *ctrl;
> +
> + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> + xsdirxss_edh_ctrls[i].name);
> + goto error;
> + }
> + }
> + }
> +
> + if (xsdirxss->ctrl_handler.error) {
> + dev_err(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(dev, "failed to set controls\n");
> + goto error;
> + }
> +
> + platform_set_drvdata(pdev, xsdirxss);
> +
> + ret = v4l2_async_register_subdev(subdev);
> + if (ret < 0) {
> + dev_err(dev, "failed to register subdev\n");
> + goto error;
> + }
> +
> + xsdirxss->streaming = false;
> +
> + xsdirx_core_enable(core);
> +
> + dev_info(dev, "probe success\n");
> +
> + return 0;
> +error:
> + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +clk_err:
> + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> + return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> + struct xsdirxss_core *core = &xsdirxss->core;
> + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> + v4l2_async_unregister_subdev(subdev);
> + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> +
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> + xsdirx_core_disable(core);
> + xsdirx_streamflow_control(core, false);
> +
> + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> + { }
> +};
> +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 <[email protected]>");
> +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 000000000000..6f2a093968d9
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,179 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Xilinx SDI Rx Subsystem mode, event, custom timings
> + * and flag definitions.
> + *
> + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <[email protected]>
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/v4l2-dv-timings.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * Events
> + *
> + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core underflowed
> + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core overflowed
> + */
> +#define V4L2_EVENT_XILINX_SDIRX_CLASS (V4L2_EVENT_PRIVATE_START | 0x200)
> +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> +
> +/*
> + * 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
> +
> +/* Xilinx DV timings not in mainline yet */
> +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}

Why not add these to v4l2-dv-timings.h? I do need to know on which standard
they are based.

> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index b6441fe705c5..e9de65e82642 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h

Why is this in a separate header? It seems to me that it makes more sense to have
a single public header for this driver.

> @@ -71,4 +71,71 @@
> /* Noise level */
> #define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17)
>
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* The base for the sdi rx driver controls.
> + * We reserve 32 controls for this driver.
> + *
> + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> + * EDH is enabled.
> + * The controls which can be set should only be set before enabling
> + * streaming. The controls which can be got should be called while
> + * streaming to get correct values.
> + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query dv timing
> + * returns a valid timing.
> + */
> +
> +#define V4L2_CID_XILINX_SDIRX (V4L2_CID_XILINX_BASE + 0x20)
> +
> +/* Framer Control to enable or disable the framer */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER (V4L2_CID_XILINX_SDIRX + 1)
> +/*
> + * Video Lock Window Control to set the video lock window value
> + * This is the amount of time the mode and transport stream need
> + * to be locked before a video lock interrupt occurs.
> + */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW (V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control to enable EDH error count */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE (V4L2_CID_XILINX_SDIRX + 3)
> +/*
> + * Mode search Control to pass the bit mask of modes to detect.
> + *
> + * bit 0 set to detect SD mode,
> + * bit 1 set to detect HD mode,
> + * bit 2 set to detect 3G (3GA & 3GB) mode,
> + * bit 3 set to detect 6G mode,
> + * bit 4 set to detect 12G integer frame rate mode,
> + * bit 5 set to detect 12G fractional frame rate mode,
> + */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES (V4L2_CID_XILINX_SDIRX + 4)
> +/*
> + * Get Detected Mode control
> + *
> + * Control Value - Mode detected
> + * 0 - SD
> + * 1 - HD
> + * 2 - 3G (3GA & 3GB)
> + * 3 - 6G
> + * 4 - 12G integer frame rate
> + * 5 - 12G fractional frame rate
> + */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT (V4L2_CID_XILINX_SDIRX + 5)
> +/* Get number of CRC errors status control */
> +#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 whether it is interlaced or not */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED (V4L2_CID_XILINX_SDIRX + 9)
> +/* Get number of Active Streams */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS (V4L2_CID_XILINX_SDIRX + 10)
> +/*
> + * Get if the detected mode is 3GB.
> + * Can be used to distinguished between 3GA and 3GB
> + */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB (V4L2_CID_XILINX_SDIRX + 11)
> +
> #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
>

Regards,

Hans

2020-05-06 13:05:52

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem

Hi Vishal,

Thank you for the patch.

On Wed, Apr 29, 2020 at 07:47:03PM +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 <[email protected]>
> ---
> v2
> - Removed references to xlnx,video*
> - Fixed as per Sakari Ailus and Rob Herring's comments
> - Converted to yaml format
>
> .../bindings/media/xilinx/xlnx,sdirxss.yaml | 132 ++++++++++++++++++
> 1 file changed, 132 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
>
> diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> new file mode 100644
> index 000000000000..9133ad19df55
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> @@ -0,0 +1,132 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/xilinx/xlnx,sdirxss.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +
> +title: Xilinx SMPTE UHD-SDI Receiver Subsystem
> +
> +maintainers:
> + - Vishal Sagar <[email protected]>
> +
> +description: |
> + 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 upto 12G mode 8 data streams and output

s/upto/up to/

> + a dual pixel per clock RGB/YUV444,422/420 10/12 bits per component AXI4-Stream.
> +
> +properties:
> + compatible:
> + items:
> + - enum:
> + - xlnx,v-smpte-uhdsdi-rx-ss-2.0
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + clocks:
> + description: List of clock specifiers
> + items:
> + - description: AXI4-Lite clock
> + - description: SMPTE UHD-SDI Rx core clock
> + - description: Video clock
> +
> + clock-names:
> + items:
> + - const: s_axi_aclk
> + - const: sdi_rx_clk
> + - const: video_out_clk
> +
> + xlnx,bpp:
> + description: Bits per pixel supported. Can be 10 or 12 bits per pixel only.
> + allOf:
> + - $ref: "/schemas/types.yaml#/definitions/uint32"
> + - enum: [10, 12]

I don't see this as a design parameter in the documentation (pg290,
v2.0). What does it correspond to ? All the BPC mentions in the
documentation always state that 10-bit is the only supported value.

> +
> + xlnx,line-rate:
> + description: |
> + The maximum mode supported by the design. Possible values are as below
> + 12G_SDI_8DS - 12G mode with 8 data streams
> + 6G_SDI - 6G mode
> + 3G_SDI - 3G mode
> + enum:
> + - 12G_SDI_8DS
> + - 6G_SDI
> + - 3G_SDI

How about making this an integer property, with #define in
include/dt-bindings/media/xilinx-sdi.h ? As far as I understand, the SDI
TX subsystem has the same parameter, so the #define could be shared
between the two.

> +
> + xlnx,include-edh:
> + type: boolean
> + description: |
> + This is present when the Error Detection and Handling processor is
> + enabled in design.
> +
> + ports:
> + type: object
> + description: |
> + 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.

We should still have an input port. It can be connected to a DT node for
a physical SDI connector, or any other component in the platform (I
expect the former to be the common case). There are DT bindings for
connectors in Documentation/devicetree/bindings/display/connector/, we
should add one for SDI.

> + properties:
> + port@0:
> + type: object
> + description: Source port
> + properties:
> + reg:
> + const: 0
> + endpoint:
> + type: object
> + properties:
> + remote-endpoint: true
> + required:
> + - remote-endpoint
> + additionalProperties: false
> + additionalProperties: false
> +
> +required:
> + - compatible
> + - reg
> + - interrupts
> + - clocks
> + - clock-names
> + - xlnx,line-rate
> + - xlnx,bpp
> + - ports
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + uhdsdirxss: v-smpte-uhdsdi-rxss@80000000 {
> + compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0";
> + 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";
> + xlnx,bpp = <10>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + port@0 {
> + reg = <0>;
> + sdirx_out: endpoint {
> + remote-endpoint = <&vcap_sdirx_in>;
> + };
> + };
> + };
> + };

--
Regards,

Laurent Pinchart

2020-05-06 15:13:58

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Hi Vishal,

Thank you for the patch.

There are a few questions for Hans below.

On Wed, Apr 29, 2020 at 07:47:04PM +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 10/12 bpc YUV 422 media bus format currently. 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.

As commented on patch 1/2, I don't see a mention of 12 bpc in the
documentation. Let's discuss it as part of patch 1/2.

> The driver currently supports only the AXI4-Stream IP configuration.
>
> Signed-off-by: Vishal Sagar <[email protected]>
> ---
> v2
> - Added DV timing support based on Hans Verkuilś feedback
> - More documentation to custom v4l controls and events
> - Fixed Hyunś comments
> - Added macro for masking and shifting as per Joe Perches comments
> - Updated to latest as per Xilinx github repo driver like
> adding new DV timings not in mainline yet uptill 03/21/20
>
> drivers/media/platform/xilinx/Kconfig | 11 +
> drivers/media/platform/xilinx/Makefile | 1 +
> .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> include/uapi/linux/xilinx-sdirxss.h | 179 ++
> include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> 5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -12,6 +12,17 @@ config VIDEO_XILINX
>
> if VIDEO_XILINX
>
> +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

s/comprises of/comprises/

> + 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 4cdc0b1ec7a5..6c375f03f587 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -2,6 +2,7 @@
>
> xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
>
> +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,2162 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <[email protected]>
> + *

You can remove this blank line.

> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>
> +#include <linux/clk.h>

You nearly got the alphabetical order right ;-)

> +#include <linux/delay.h>

No use of delay or sleep functions in the code, is this needed ?

> +#include <linux/device.h>

You can probably drop this one as you include platform_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>

I don't think of_irq is needed either.

> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/spinlock_types.h>

The driver doesn't seems to use spinlocks, you can remove those two
headers.

> +#include <linux/types.h>
> +#include <linux/v4l2-dv-timings.h>
> +#include <linux/v4l2-subdev.h>
> +#include <linux/xilinx-sdirxss.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

Could you use lower-case hex values ? That's the most common option in
kernel code. If you use vim,

:%s/\<0x[0-9A-F]\+\>/\L\0/

I'm sure there's an emacs equivalent :-)

> +#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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12, 10)
> +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> +
> +#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_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)
> +
> +#define XSDIRX_DEFAULT_WIDTH (1920)

No need for parentheses around integer values in macros.

> +#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

Should these three constants be placed in an enum ? The
xsdirxss_core.mode field would then become an enum.

> +
> +#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)

Do we really need such a high number ? How often are the overflow and
underflow expected ?

> +
> +/* ST352 related macros */
> +#define XST352_PAYLOAD_BYTE1_MASK 0xFF
> +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> +#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
> +#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_OFFSET 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_YUV444 0x1
> +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> +
> +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> +
> +#define CLK_INT 148500000UL
> +
> +#define rshift_and_mask(val, type) \
> + (((val) >> type##_OFFSET) & type##_MASK)
> +
> +#define mask_and_rshift(val, type) \
> + (((val) & type##_MASK) >> type##_OFFSET)

How about using FIELD_GET() from linux/bitfield.h ? It's more standard,
and will thus be less confusing to readers.

> +
> +/**
> + * 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
> +};

This should ideally go in a header file shared between the SDI RX and
TX, as it's part of the UHD-SDI IP core, but it can be done later.

> +
> +/**
> + * 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
> + * @clks: array of clocks
> + * @num_clks: number of clocks
> + * @bpc: Bits per component, can be 10 or 12
> + */
> +struct xsdirxss_core {
> + struct device *dev;
> + void __iomem *iomem;
> + int irq;
> + bool include_edh;
> + int mode;
> + struct clk_bulk_data *clks;
> + int num_clks;

num_clks is never negative, you can make it an unsigned int.

> + u32 bpc;
> +};
> +
> +/**
> + * 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
> + * @format: Active V4L2 format on source pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @pad: source media pad
> + * @vidlockwin: Video lock window value set by control
> + * @edhmask: EDH mask set by control
> + * @searchmask: Search mask set by control
> + * @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.
> + * @framer_enable: Flag for framer enabled or not set by control
> + *
> + * 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;

Is this needed, can't it be a local variable in the function where it is
used ?

> + struct v4l2_mbus_framefmt format;
> + struct v4l2_mbus_framefmt default_format;
> + struct v4l2_fract frame_interval;
> + struct media_pad pad;

You will need two pads as there should be two ports (let's discuss it as
part of patch 1/2).

> + u32 vidlockwin;
> + u32 edhmask;
> + u16 searchmask;
> + bool streaming;
> + bool vidlocked;
> + bool ts_is_interlaced;
> + bool framer_enable;
> +};

As for the CSI2-RX, splitting core and state in separate structures
isn't an established practice. I think it leads to more complex code as
you have to remember in which structure each field is located.

> +
> +/* List of clocks required by UHD-SDI Rx subsystem */
> +static const char * const xsdirxss_clks[] = {
> + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> +};
> +
> +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */

s/RBG/RGB/ ?

> +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> + MEDIA_BUS_FMT_UYVY10_1X20,
> +};
> +
> +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> + MEDIA_BUS_FMT_UYVY12_1X24,
> +};
> +
> +static const struct v4l2_dv_timings fmt_cap[] = {
> + V4L2_DV_BT_SDI_720X487I60,
> + V4L2_DV_BT_CEA_720X576I50,
> + V4L2_DV_BT_CEA_1280X720P24,
> + V4L2_DV_BT_CEA_1280X720P25,
> + V4L2_DV_BT_CEA_1280X720P30,
> + V4L2_DV_BT_CEA_1280X720P50,
> + V4L2_DV_BT_CEA_1280X720P60,
> + V4L2_DV_BT_CEA_1920X1080P24,
> + V4L2_DV_BT_CEA_1920X1080P30,
> + V4L2_DV_BT_CEA_1920X1080I50,
> + V4L2_DV_BT_CEA_1920X1080I60,
> + V4L2_DV_BT_CEA_1920X1080P50,
> + V4L2_DV_BT_CEA_1920X1080P60,
> + V4L2_DV_BT_CEA_3840X2160P24,
> + V4L2_DV_BT_CEA_3840X2160P30,
> + V4L2_DV_BT_CEA_3840X2160P50,
> + V4L2_DV_BT_CEA_3840X2160P60,
> + V4L2_DV_BT_CEA_4096X2160P24,
> + V4L2_DV_BT_CEA_4096X2160P25,
> + V4L2_DV_BT_CEA_4096X2160P30,
> + V4L2_DV_BT_CEA_4096X2160P50,
> + V4L2_DV_BT_CEA_4096X2160P60,
> +
> + XLNX_V4L2_DV_BT_2048X1080P24,
> + XLNX_V4L2_DV_BT_2048X1080P25,
> + XLNX_V4L2_DV_BT_2048X1080P30,
> + XLNX_V4L2_DV_BT_2048X1080I48,
> + XLNX_V4L2_DV_BT_2048X1080I50,
> + XLNX_V4L2_DV_BT_2048X1080I60,
> + XLNX_V4L2_DV_BT_2048X1080P48,
> + XLNX_V4L2_DV_BT_2048X1080P50,
> + XLNX_V4L2_DV_BT_2048X1080P60,
> + XLNX_V4L2_DV_BT_1920X1080P48,
> + XLNX_V4L2_DV_BT_1920X1080I48,
> + XLNX_V4L2_DV_BT_3840X2160P48,
> + XLNX_V4L2_DV_BT_4096X2160P48,
> +};
> +
> +struct xsdirxss_dv_map {
> + u32 width;
> + u32 height;
> + u32 fps;
> + struct v4l2_dv_timings format;
> +};
> +
> +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> + /* SD - 720x487i60 */
> + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> + /* SD - 720x576i50 */
> + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> + /* HD - 1280x720p23.98 */
> + /* HD - 1280x720p24 */
> + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> + /* HD - 1280x720p25 */
> + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> + /* HD - 1280x720p29.97 */
> + /* HD - 1280x720p30 */
> + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> + /* HD - 1280x720p50 */
> + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> + /* HD - 1280x720p59.94 */
> + /* HD - 1280x720p60 */
> + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> + /* HD - 1920x1080p23.98 */
> + /* HD - 1920x1080p24 */
> + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> + /* HD - 1920x1080p25 */
> + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> + /* HD - 1920x1080p29.97 */
> + /* HD - 1920x1080p30 */
> + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> +
> + /* HD - 2048x1080p23.98 */
> + /* HD - 2048x1080p24 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> + /* HD - 2048x1080p25 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> + /* HD - 2048x1080p29.97 */
> + /* HD - 2048x1080p30 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> + /* HD - 1920x1080i47.95 */
> + /* HD - 1920x1080i48 */
> + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> +
> + /* HD - 1920x1080i50 */
> + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> + /* HD - 1920x1080i59.94 */
> + /* HD - 1920x1080i60 */
> + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> +
> + /* HD - 2048x1080i47.95 */
> + /* HD - 2048x1080i48 */
> + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> + /* HD - 2048x1080i50 */
> + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> + /* HD - 2048x1080i59.94 */
> + /* HD - 2048x1080i60 */
> + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> + /* 3G - 1920x1080p47.95 */
> + /* 3G - 1920x1080p48 */
> + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> +
> + /* 3G - 1920x1080p50 148.5 */
> + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> + /* 3G - 1920x1080p59.94 148.5/1.001 */
> + /* 3G - 1920x1080p60 148.5 */
> + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> +
> + /* 3G - 2048x1080p47.95 */
> + /* 3G - 2048x1080p48 */
> + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> + /* 3G - 2048x1080p50 */
> + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> + /* 3G - 2048x1080p59.94 */
> + /* 3G - 2048x1080p60 */
> + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> +
> + /* 6G - 3840X2160p23.98 */
> + /* 6G - 3840X2160p24 */
> + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> + /* 6G - 3840X2160p25 */
> + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> + /* 6G - 3840X2160p29.97 */
> + /* 6G - 3840X2160p30 */
> + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> + /* 6G - 4096X2160p23.98 */
> + /* 6G - 4096X2160p24 */
> + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> + /* 6G - 4096X2160p25 */
> + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> + /* 6G - 4096X2160p29.97 */
> + /* 6G - 4096X2160p30 */
> + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> + /* 12G - 3840X2160p47.95 */
> + /* 12G - 3840X2160p48 */
> + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> +
> + /* 12G - 3840X2160p50 */
> + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> + /* 12G - 3840X2160p59.94 */
> + /* 12G - 3840X2160p60 */
> + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> +
> + /* 12G - 4096X2160p47.95 */
> + /* 12G - 4096X2160p48 */
> + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> +
> + /* 12G - 4096X2160p50 */
> + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> + /* 12G - 4096X2160p59.94 */
> + /* 12G - 4096X2160p60 */
> + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> +};
> +
> +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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static inline 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 val;
> + struct device *dev = core->dev;
> +
> + mask &= XSDIRX_DETECT_ALL_MODES;
> + if (!mask) {
> + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> + return -EINVAL;
> + }
> +
> + dev_dbg(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(dev, "Detect multiple modes\n");
> +
> + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +
> + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> + } else {
> + /* Fixed Mode */
> + u32 forced_mode_mask;
> +
> + dev_dbg(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;
> + default:
> + forced_mode_mask = 0;
> + }
> + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> + forced_mode_mask);
> + val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> + }
> +
> + dev_dbg(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)

That's a hard to read function name. Could you separate words with
underscores ? Same for functions below that concatenate multiple words
together.

> +{
> + 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 inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static inline 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 inline 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)
> +{
> + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> +
> + /*
> + * TODO - Enable YUV444/RBG format in the bridge based
> + * on BYTE3 color format.
> + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> + */
> + if (enable)
> + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> + else
> + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> +}

This is only used in xsdirx_streamflow_control(), inlining it manually
would be simpler I think.

> +
> +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);
> +}

Same for this one.

> +
> +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 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;
> + }
> +}
> +
> +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> +{
> + struct clk *gtclk;
> + unsigned long clkrate;
> + int ret, is_frac;
> + struct xsdirxss_core *core = &state->core;
> + u32 mode;
> + static int prev_is_frac = -1;
> +
> + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> + /*
> + * TODO: For now, don't change the clock rate for any mode except 12G.
> + * In future, configure gt clock for all modes and enable clock only
> + * when needed (stream on/off).
> + */
> + if (mode != XSDIRX_MODE_12GI_MASK && mode != XSDIRX_MODE_12GF_MASK)
> + return;
> +
> + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> +
> + if (prev_is_frac == is_frac)
> + return;
> +
> + xsdirx_core_disable(core);
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +
> + /* get sdi_rx_clk */
> + gtclk = core->clks[1].clk;
> + clkrate = clk_get_rate(gtclk);
> + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> +
> + /* calcualte clkrate */

s/calcualte/calculate/

> + if (!is_frac)
> + clkrate = CLK_INT;
> + else
> + clkrate = (CLK_INT * 1000) / 1001;

Shouldn't the clock rate also depend on the mode ? Table 3-2 lists
different clock rates for HD-SDI and 12G-SDI.

> +
> + ret = clk_set_rate(gtclk, clkrate);
> + if (ret)
> + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> +
> + clkrate = clk_get_rate(gtclk);
> +
> + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> + clkrate, is_frac);
> +
> + xsdirx_framer(core, state->framer_enable);

As this is called in both the IRQ handler and the s_ctrl handler, you
could have a race when accessing the XSDIRX_MDL_CTRL_REG register. You
will need to protect all accesses to registers that are written from
both the IRQ handler and userspace contexts with a spinlock.

> + xsdirx_setedherrcnttrigger(core, state->edhmask);
> + xsdirx_setvidlockwindow(core, state->vidlockwin);
> + xsdirx_set_modedetect(core, state->searchmask);
> + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> + xsdirx_globalintr(core, true);
> + xsdirx_core_enable(core);
> +
> + prev_is_frac = is_frac;

That's lots of work done in an interrupt handler. As this function is
called when a stream lock is detected, if the clock rate has to be
changed, do I assume correctly that the resolution may have changed too
? That may require reconfiguring the pipeline and reallocating buffers.
With SD and HD TV, I think the usual practice is to notify userspace of
video lock and let it do the reconfiguration is required, instead of
attempting to do so automatically. Hans, is that correct ? How would we
handle then the case where the pipeline is already correctly configured
with big enough buffers (I'm not sure this can actually happen, for the
buffers yes, but for the pipeline configuration, maybe the format will
always need to change) ?

And I've now seen that we do emit the V4L2_EVENT_SRC_CH_RESOLUTION event
:-) Do we thus need to reconfigure the IP core here ?

> +}
> +
> +/**
> + * 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;
> + struct device *dev = core->dev;
> + 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->format;
> + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> +
> + 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> + active_luma = mask_and_rshift(payload,
> + XST352_BYTE3_ACT_LUMA_COUNT);
> + pic_type = mask_and_rshift(payload, XST352_BYTE2_PIC_TYPE);
> + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> + sampling = mask_and_rshift(payload, XST352_BYTE3_COLOR_FORMAT);
> + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> + } else {
> + dev_dbg(dev, "No ST352 payload available : Mode = %d\n", mode);
> + framerate = mask_and_rshift(val, XSDIRX_TS_DET_STAT_RATE);
> + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> + }
> +
> + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc = %d\n",
> + bpc, core->bpc);
> + return -EINVAL;
> + }
> +
> + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> + state->ts_is_interlaced = tscan ? false : true;
> +
> + dev_dbg(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(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(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(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(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(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(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;
> + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> + format->height = 1080;
> + if (active_luma)
> + format->width = 2048;
> + else
> + format->width = 1920;
> + break;
> + default:
> + dev_dbg(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(dev, "Unknown 12G Mode SMPTE standard\n");
> + return -EINVAL;
> + }
> + break;
> + default:
> + dev_err(dev, "Invalid Mode\n");
> + return -EINVAL;
> + }
> +
> + if (valid) {
> + if (pic_type)
> + format->field = V4L2_FIELD_NONE;
> + else
> + format->field = V4L2_FIELD_ALTERNATE;
> +
> + if (format->height == 1080 && pic_type && !tscan)
> + format->field = V4L2_FIELD_ALTERNATE;
> +
> + /*
> + * In 3GB DL pSF mode the video is similar to interlaced.
> + * So though it is a progressive video, its transport is
> + * interlaced and is sent as two width x (height/2) buffers.
> + */
> + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> + if (state->ts_is_interlaced)
> + format->field = V4L2_FIELD_ALTERNATE;
> + else
> + format->field = V4L2_FIELD_NONE;
> + }
> + }
> +
> + if (format->field == V4L2_FIELD_ALTERNATE)
> + format->height = format->height / 2;
> +
> + switch (sampling) {
> + case XST352_BYTE3_COLOR_FORMAT_422:
> + if (core->bpc == 10)
> + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> + else
> + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> + break;
> + case XST352_BYTE3_COLOR_FORMAT_420:
> + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> + case XST352_BYTE3_COLOR_FORMAT_GBR:
> + format->code = 0;
> + dev_dbg(dev, "No corresponding media bus formats\n");
> + break;
> + default:
> + dev_err(dev, "Unsupported color format : %d\n", sampling);
> + return -EINVAL;
> + }
> +
> + xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> + dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
> + state->frame_interval.numerator,
> + state->frame_interval.denominator);
> + dev_dbg(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 writing 1 to corresponding bit.
> + *
> + * 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;
> + struct device *dev = core->dev;
> + u32 status;
> +
> + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> +
> + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> + u32 val1, val2;
> +
> + dev_dbg(dev, "video lock/unlock interrupt\n");
> +
> + xsdirx_streamflow_control(core, false);
> + state->streaming = false;
> +
> + 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
> + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> +
> + if (!xsdirx_get_stream_properties(state)) {
> + state->vidlocked = true;
> + xsdirxss_set_gtclk(state);
> + } else {
> + dev_err(dev, "Unable to get stream properties!\n");
> + state->vidlocked = false;
> + }
> + } else {
> + dev_dbg(dev, "video unlock interrupt\n");
> + state->vidlocked = false;
> + }
> +
> + 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);
> + }
> +
> + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> + dev_dbg(dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }
> +
> + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> + dev_dbg(dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> + state->event.type = V4L2_EVENT_XILINX_SDIRX_OVERFLOW;
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }

Can't we combine these two in a single event type, with underflow and
overflow reported in the event payload in a bitmask ?

> + 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_XILINX_SDIRX_UNDERFLOW:
> + case V4L2_EVENT_XILINX_SDIRX_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;

As Hans commented, you need to call v4l2_ctrl_subscribe_event() here.

> + }
> + 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);
> +}

I think you can drop this function and use v4l2_event_subdev_unsubscribe
as the ..unsubscribe_event() handler.

> +
> +/**
> + * 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;
> + struct device *dev = core->dev;
> +
> + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> + ctrl->id, ctrl->val);
> +
> + if (xsdirxss->streaming) {
> + dev_err(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);
> + xsdirxss->framer_enable = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> + xsdirx_setvidlockwindow(core, ctrl->val);
> + xsdirxss->vidlockwin = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:

As this is used to select which error sources to enable, how about
naming the control V4L2_CID_XILINX_SDIRX_EDH_ERROR_SOURCES or
V4L2_CID_XILINX_SDIRX_EDH_ERRORS_MASK ?

> + xsdirx_setedherrcnttrigger(core, ctrl->val);
> + xsdirxss->edhmask = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> + if (ctrl->val) {
> + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> + dev_dbg(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(dev, "Upto 6G supported\n");
> + ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> + BIT(XSDIRX_MODE_12GF_OFFSET));
> + }
> +
> + ret = xsdirx_set_modedetect(core, ctrl->val);
> + if (!ret)
> + xsdirxss->searchmask = ctrl->val;
> + } else {
> + dev_err(dev, "Select at least one mode!\n");
> + return -EINVAL;
> + }
> + break;

The traditional approach to select timing standards is to use ioctls.
Hans, do you think a custom control is fine here, or should the dv
timings ioctls be extended (or new sdi timings ioctls created) ?

If we go for a single control, I'm a bit concerned that this control
will restrict the search when multiple bits are set, but force a
specific mode when a single bit is set. I don't have enough experience
with SDI to tell whether this is the right behaviour.

> + default:
> + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> + XSDIRX_RST_CTRL_SS_EN_MASK);
> + return -EINVAL;

This looks weird.

ret = -EINVAL;
break;

would be better.

> + }
> + xsdirx_core_enable(core);

Do we really need to disable and reenable the core as we're guaranteed
not to be streaming ?

> + 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;
> + struct device *dev = core->dev;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> + if (!xsdirxss->vidlocked) {
> + dev_err(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;

Hans commented that the dv timings structure will report whether the
mode is interlaced, I wonder if reporting the SDI mode shouldn't go
through that structure too.

> + case V4L2_CID_XILINX_SDIRX_CRC:
> + ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> + break;

Are CRC errors common (and recoverable), or are they rare and fatal
errors ? In other words, does this report information that would be
classified as link quality, or fatal errors ? In the later case I wonder
if an event wouldn't make more sense. It could be reported as another
bit in the overflow/underflow event.

> + 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(dev, "%d - not in SD mode\n", ctrl->id);
> + return -EINVAL;
> + }
> + break;

If my understanding of the datasheet is correct, this will be reset for
every frame, right ? Reporting the information this way is thus quite
racy. Isn't it better to send it through an event ?

> + 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(dev, "%d - not in SD mode\n", ctrl->id);
> + return -EINVAL;
> + }
> + break;

Same here, seems quite racy.

> + case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> + if (!xsdirxss->vidlocked) {
> + dev_err(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(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(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;

Shouldn't this also go through DV timings ? If not, I think it should at
least be combined with V4L2_CID_XILINX_SDIRX_MODE_DETECT.

> + default:
> + dev_err(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> + return -EINVAL;
> + }
> + dev_dbg(dev, "Get ctrl id = 0x%08x val = 0x%08x\n", ctrl->id,
> + ctrl->val);

I would drop this line.

> + 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 i;
> +
> + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> + for (i = 0; i < 0x28; i++) {

There are registers in this range that are not documented in the
datasheet. Is it really safe to read them ? There are also registers
that are of no real interest I believe (ISR for instance), or registers
that seems to map to a FIFO (RX_ST352_DATA_DS*) which may cause issue
when read as the IRQ handler extracts data from them (I could be wrong
here though). Should this be restricted to a handful of carefully
selected registers ? You could then also print their name instead of
their address, that would be more readable.

> + u32 data;
> +
> + 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;
> +}
> +
> +/**
> + * 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;

There's a data race with the interrupt handler here.

> +
> + 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;
> + struct device *dev = core->dev;
> +

Maybe

if (enable == xsdirxss->streaming) {
dev_dbg(...);
return enable ? -EINVAL : 0;
}

? Up to you.

> + if (enable) {
> + if (!xsdirxss->vidlocked) {
> + dev_dbg(dev, "Video is not locked\n");
> + return -EINVAL;
> + }
> + if (xsdirxss->streaming) {
> + dev_dbg(dev, "Already streaming\n");
> + return -EINVAL;
> + }
> +
> + xsdirx_streamflow_control(core, true);
> + xsdirxss->streaming = true;
> + dev_dbg(dev, "Streaming started\n");
> + } else {
> + if (!xsdirxss->streaming) {
> + dev_dbg(dev, "Stopped streaming already\n");
> + return 0;
> + }
> +
> + xsdirx_streamflow_control(core, false);
> + xsdirxss->streaming = false;
> + dev_dbg(dev, "Streaming stopped\n");
> + }
> +

And possibly

xsdirxss->streaming = enable;
dev_dbg(dev, "Streaming %s\", enable ? "started" : "stopped");

> + return 0;
> +}
> +
> +/**
> + * xsdirxss_g_input_status - It is used to determine if the video signal
> + * is present / locked onto or not.
> + *
> + * @sd: V4L2 Sub device
> + * @status: status of signal locked
> + *
> + * This is used to determine if the video signal is present and locked onto
> + * by the SDI Rx core or not based on vidlocked flag.
> + *
> + * Return: zero on success
> + */
> +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> + if (!xsdirxss->vidlocked)
> + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> + else
> + *status = 0;
> +
> + 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->format;
> + default:
> + return NULL;
> + }
> +}
> +
> +/**
> + * xsdirxss_init_cfg - Initialise the pad format config to default
> + * @sd: Pointer to V4L2 Sub device structure
> + * @cfg: Pointer to sub device pad information structure
> + *
> + * This function is used to initialize the pad format with the default
> + * values.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> + struct v4l2_mbus_framefmt *format;
> +
> + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> + *format = xsdirxss->default_format;
> +
> + return 0;
> +}
> +
> +/**
> + * 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;

As Hans requested, width, height and field should be set here too, and
probably colorspace information too.

> +
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> + * @sd: pointer to v4l2 subdev structure
> + * @cfg: V4L2 subdev pad configuration
> + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> + *
> + * Return: -EINVAL or zero on success
> + */
> +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> + u32 index = code->index;
> + u32 maxindex;
> +
> + if (xsdirxss->core.bpc == 10)
> + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> + else
> + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> +
> + if (code->pad || index >= maxindex)
> + return -EINVAL;
> +
> + if (xsdirxss->core.bpc == 10)
> + code->code = xsdirxss_10bpc_mbus_fmts[index];
> + else
> + code->code = xsdirxss_12bpc_mbus_fmts[index];
> +
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> + * @sd: pointer to v4l2 subdev structure
> + * @timings: DV timings structure to be returned.
> + *
> + * Return: -EINVAL incase of invalid index and pad or zero on success
> + */
> +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> + struct v4l2_enum_dv_timings *timings)
> +{
> + if (timings->index >= ARRAY_SIZE(fmt_cap))
> + return -EINVAL;
> +
> + if (timings->pad != 0)
> + return -EINVAL;
> +
> + timings->timings = fmt_cap[timings->index];
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_query_dv_timings: Query for the current DV timings
> + * @sd: pointer to v4l2 subdev structure
> + * @timings: DV timings structure to be returned.
> + *
> + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding timing
> + * entry is not found or zero on success.
> + */
> +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> + struct v4l2_dv_timings *timings)
> +{
> + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> + unsigned int i;
> +
> + if (!state->vidlocked)
> + return -ENOLCK;
> +
> + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> + if (state->format.width == xsdirxss_dv_timings[i].width &&
> + state->format.height == xsdirxss_dv_timings[i].height &&
> + state->frame_interval.denominator ==
> + (xsdirxss_dv_timings[i].fps * 1000)) {
> + *timings = xsdirxss_dv_timings[i].format;
> + return 0;
> + }
> + }
> +
> + return -ERANGE;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * 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 const 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 const 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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> + .g_frame_interval = xsdirxss_g_frame_interval,
> + .s_stream = xsdirxss_s_stream,
> + .g_input_status = xsdirxss_g_input_status,
> + .query_dv_timings = xsdirxss_query_dv_timings,
> +};
> +
> +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> + .init_cfg = xsdirxss_init_cfg,
> + .get_fmt = xsdirxss_get_format,
> + .set_fmt = xsdirxss_set_format,
> + .enum_mbus_code = xsdirxss_enum_mbus_code,
> + .enum_dv_timings = xsdirxss_enum_dv_timings,
> +};
> +
> +static const struct v4l2_subdev_ops xsdirxss_ops = {
> + .core = &xsdirxss_core_ops,
> + .video = &xsdirxss_video_ops,
> + .pad = &xsdirxss_pad_ops
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> + struct device_node *node = xsdirxss->core.dev->of_node;
> + struct xsdirxss_core *core = &xsdirxss->core;
> + struct device *dev = core->dev;
> + struct fwnode_handle *ep, *rep;
> + int ret;
> + const char *sdi_std;
> +
> + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> + dev_dbg(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(dev, "xlnx,line-rate property not found\n");
> + return ret;
> + }
> +
> + if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {

Strings in DT are null-terminated, you can use strcmp() and drop
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(dev, "Invalid Line Rate\n");
> + return -EINVAL;
> + }
> + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> + core->mode);
> +
> + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> + if (ret < 0) {
> + dev_err(dev, "failed to get xlnx,bpp\n");
> + return ret;
> + }
> +
> + if (core->bpc != 10 && core->bpc != 12) {
> + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> + core->bpc);
> + return -EINVAL;
> + }
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> + FWNODE_GRAPH_ENDPOINT_NEXT);
> + if (!ep) {
> + dev_err(dev, "no source port found");
> + ret = -EINVAL;
> + goto dt_parse_done;
> + }
> +
> + rep = fwnode_graph_get_remote_endpoint(ep);
> + if (!rep) {
> + dev_err(dev, "no remote sink endpoint found");
> + ret = -EINVAL;
> + }

I don't think you need to check this, the subdev won't be linked
properly in the pipeline if there's no port anyway.

> +
> + fwnode_handle_put(rep);
> +dt_parse_done:
> + fwnode_handle_put(ep);
> + return ret;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *subdev;
> + struct xsdirxss_state *xsdirxss;
> + struct xsdirxss_core *core;
> + struct device *dev;
> + 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;
> + dev = core->dev;
> +
> + /* Register interrupt handler */
> + core->irq = platform_get_irq(pdev, 0);
> + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> + xsdirxss_irq_handler, IRQF_ONESHOT,
> + dev_name(dev), xsdirxss);

Why a threaded IRQ ?

> + if (ret) {
> + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> + ret);
> + return ret;
> + }

You should request the IRQ after resetting the core, otherwise
interrupts could be triggered immediately.

> +
> + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> + core->clks = devm_kcalloc(dev, core->num_clks,
> + sizeof(*core->clks), GFP_KERNEL);
> + if (!core->clks)
> + return -ENOMEM;
> +
> + for (i = 0; i < core->num_clks; i++)
> + core->clks[i].id = xsdirxss_clks[i];
> +
> + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> + if (ret)
> + return ret;
> +
> + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> + if (ret)
> + return ret;
> +
> + ret = xsdirxss_parse_of(xsdirxss);
> + if (ret < 0)
> + goto clk_err;
> +
> + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(core->iomem)) {
> + ret = PTR_ERR(core->iomem);
> + goto clk_err;
> + }

If you move these two calls above handling of the clocks, you will be
able to return immediately and avoid the goto.

> +
> + /* 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->pad.flags = MEDIA_PAD_FL_SOURCE;
> +
> + /* Initialize the default format */
> + if (core->bpc == 10)
> + xsdirxss->default_format.code = MEDIA_BUS_FMT_UYVY10_1X20;
> + else
> + xsdirxss->default_format.code = MEDIA_BUS_FMT_UYVY12_1X24;
> + 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->format = xsdirxss->default_format;
> +
> + /* Initialize V4L2 subdevice and media entity */
> + subdev = &xsdirxss->subdev;
> + v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> + subdev->dev = &pdev->dev;
> + strscpy(subdev->name, dev_name(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->pad);
> + if (ret < 0)
> + goto error;
> +
> + /* Initialise and register the controls */
> + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> + if (core->include_edh)
> + num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);

How about

num_ctrls += ARRAY_SIZE(xsdirxss_edh_ctrls);

> +
> + v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> + (num_ctrls + num_edh_ctrls));

num_ctrls);

> +
> + for (i = 0; i < num_ctrls; i++) {

for (i = 0; i < ARRAY_SIZE(xsdirxss_ctrls); i++) {

> + struct v4l2_ctrl *ctrl;
> +
> + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> + xsdirxss_ctrls[i].name);
> + goto error;
> + }

As you check for xsdirxss->ctrl_handler.error below you can drop this
check and the equivalent one for the EDH controls.

> + }
> +
> + if (core->include_edh) {
> + for (i = 0; i < num_edh_ctrls; i++) {

for (i = 0; i < ARRAY_SIZE(xsdirxss_edh_ctrls); i++) {

> + struct v4l2_ctrl *ctrl;
> +
> + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> + xsdirxss_edh_ctrls[i].name);
> + goto error;
> + }
> + }
> + }
> +
> + if (xsdirxss->ctrl_handler.error) {
> + dev_err(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(dev, "failed to set controls\n");
> + goto error;
> + }
> +
> + platform_set_drvdata(pdev, xsdirxss);
> +
> + ret = v4l2_async_register_subdev(subdev);
> + if (ret < 0) {
> + dev_err(dev, "failed to register subdev\n");
> + goto error;
> + }
> +
> + xsdirxss->streaming = false;
> +
> + xsdirx_core_enable(core);
> +
> + dev_info(dev, "probe success\n");

You can drop this line, successful driver probing should be silent.

> +
> + return 0;
> +error:
> + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +clk_err:
> + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> + return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> + struct xsdirxss_core *core = &xsdirxss->core;
> + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> + v4l2_async_unregister_subdev(subdev);
> + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> +
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> + xsdirx_core_disable(core);
> + xsdirx_streamflow_control(core, false);
> +
> + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> + { }
> +};
> +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 <[email protected]>");
> +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 000000000000..6f2a093968d9
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,179 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Xilinx SDI Rx Subsystem mode, event, custom timings
> + * and flag definitions.
> + *
> + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <[email protected]>
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/v4l2-dv-timings.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * Events
> + *
> + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core underflowed
> + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core overflowed
> + */
> +#define V4L2_EVENT_XILINX_SDIRX_CLASS (V4L2_EVENT_PRIVATE_START | 0x200)
> +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> +
> +/*
> + * 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
> +
> +/* Xilinx DV timings not in mainline yet */
> +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index b6441fe705c5..e9de65e82642 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -71,4 +71,71 @@
> /* Noise level */
> #define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17)
>
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* The base for the sdi rx driver controls.
> + * We reserve 32 controls for this driver.
> + *
> + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> + * EDH is enabled.
> + * The controls which can be set should only be set before enabling
> + * streaming. The controls which can be got should be called while
> + * streaming to get correct values.
> + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query dv timing
> + * returns a valid timing.
> + */
> +
> +#define V4L2_CID_XILINX_SDIRX (V4L2_CID_XILINX_BASE + 0x20)
> +
> +/* Framer Control to enable or disable the framer */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER (V4L2_CID_XILINX_SDIRX + 1)

This control needs more documentation. What does it do, and how should
it be used ?

> +/*
> + * Video Lock Window Control to set the video lock window value
> + * This is the amount of time the mode and transport stream need
> + * to be locked before a video lock interrupt occurs.
> + */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW (V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control to enable EDH error count */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE (V4L2_CID_XILINX_SDIRX + 3)
> +/*
> + * Mode search Control to pass the bit mask of modes to detect.
> + *
> + * bit 0 set to detect SD mode,
> + * bit 1 set to detect HD mode,
> + * bit 2 set to detect 3G (3GA & 3GB) mode,
> + * bit 3 set to detect 6G mode,
> + * bit 4 set to detect 12G integer frame rate mode,
> + * bit 5 set to detect 12G fractional frame rate mode,
> + */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES (V4L2_CID_XILINX_SDIRX + 4)
> +/*
> + * Get Detected Mode control
> + *
> + * Control Value - Mode detected
> + * 0 - SD
> + * 1 - HD
> + * 2 - 3G (3GA & 3GB)
> + * 3 - 6G
> + * 4 - 12G integer frame rate
> + * 5 - 12G fractional frame rate
> + */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT (V4L2_CID_XILINX_SDIRX + 5)
> +/* Get number of CRC errors status control */
> +#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 whether it is interlaced or not */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED (V4L2_CID_XILINX_SDIRX + 9)
> +/* Get number of Active Streams */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS (V4L2_CID_XILINX_SDIRX + 10)
> +/*
> + * Get if the detected mode is 3GB.
> + * Can be used to distinguished between 3GA and 3GB
> + */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB (V4L2_CID_XILINX_SDIRX + 11)
> +
> #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */

--
Regards,

Laurent Pinchart

2020-05-06 18:45:44

by Hyun Kwon

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Hi Vishal,

Thanks for the patch. Sorry for late reply.

On Wed, 2020-04-29 at 07:17:04 -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 10/12 bpc YUV 422 media bus format currently. 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 IP configuration.
>
> Signed-off-by: Vishal Sagar <[email protected]>
> ---
> v2
> - Added DV timing support based on Hans Verkuilś feedback
> - More documentation to custom v4l controls and events
> - Fixed Hyunś comments
> - Added macro for masking and shifting as per Joe Perches comments
> - Updated to latest as per Xilinx github repo driver like
> adding new DV timings not in mainline yet uptill 03/21/20
>
> drivers/media/platform/xilinx/Kconfig | 11 +
> drivers/media/platform/xilinx/Makefile | 1 +
> .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> include/uapi/linux/xilinx-sdirxss.h | 179 ++
> include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> 5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -12,6 +12,17 @@ config VIDEO_XILINX
>
> if VIDEO_XILINX
>
> +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 4cdc0b1ec7a5..6c375f03f587 100644
> --- a/drivers/media/platform/xilinx/Makefile
> +++ b/drivers/media/platform/xilinx/Makefile
> @@ -2,6 +2,7 @@
>
> xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
>
> +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o

This better come after CONFIG_VIDEO_XILINX.

> obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
> --- /dev/null
> +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> @@ -0,0 +1,2162 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for Xilinx SDI Rx Subsystem
> + *
> + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <[email protected]>
> + *
> + */
> +
> +#include <dt-bindings/media/xilinx-vip.h>
> +#include <linux/bitops.h>
> +#include <linux/compiler.h>

Is this needed? Maybe unused ones can be removed.

> +#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-dv-timings.h>
> +#include <linux/v4l2-subdev.h>
> +#include <linux/xilinx-sdirxss.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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12, 10)
> +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> +
> +#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_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)
> +
> +#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_BYTE1_MASK 0xFF
> +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> +#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
> +#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_OFFSET 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_YUV444 0x1
> +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> +
> +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> +
> +#define CLK_INT 148500000UL

A comment for this would be helpful.

> +
> +#define rshift_and_mask(val, type) \
> + (((val) >> type##_OFFSET) & type##_MASK)
> +
> +#define mask_and_rshift(val, type) \
> + (((val) & type##_MASK) >> type##_OFFSET)
> +
> +/**
> + * 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
> + * @clks: array of clocks
> + * @num_clks: number of clocks
> + * @bpc: Bits per component, can be 10 or 12
> + */
> +struct xsdirxss_core {
> + struct device *dev;
> + void __iomem *iomem;
> + int irq;

I don't think this has to be stored.

> + bool include_edh;
> + int mode;

unsigned type?

> + struct clk_bulk_data *clks;
> + int num_clks;

unsigned type?

> + u32 bpc;
> +};
> +
> +/**
> + * 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
> + * @format: Active V4L2 format on source pad
> + * @default_format: default V4L2 media bus format
> + * @frame_interval: Captures the frame rate
> + * @pad: source media pad
> + * @vidlockwin: Video lock window value set by control
> + * @edhmask: EDH mask set by control
> + * @searchmask: Search mask set by control
> + * @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.
> + * @framer_enable: Flag for framer enabled or not set by control
> + *
> + * 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 format;
> + struct v4l2_mbus_framefmt default_format;
> + struct v4l2_fract frame_interval;
> + struct media_pad pad;
> + u32 vidlockwin;
> + u32 edhmask;
> + u16 searchmask;
> + bool streaming;
> + bool vidlocked;
> + bool ts_is_interlaced;
> + bool framer_enable;
> +};
> +
> +/* List of clocks required by UHD-SDI Rx subsystem */
> +static const char * const xsdirxss_clks[] = {
> + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> +};
> +
> +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> + MEDIA_BUS_FMT_UYVY10_1X20,
> +};
> +
> +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> + MEDIA_BUS_FMT_UYVY12_1X24,
> +};
> +
> +static const struct v4l2_dv_timings fmt_cap[] = {
> + V4L2_DV_BT_SDI_720X487I60,
> + V4L2_DV_BT_CEA_720X576I50,
> + V4L2_DV_BT_CEA_1280X720P24,
> + V4L2_DV_BT_CEA_1280X720P25,
> + V4L2_DV_BT_CEA_1280X720P30,
> + V4L2_DV_BT_CEA_1280X720P50,
> + V4L2_DV_BT_CEA_1280X720P60,
> + V4L2_DV_BT_CEA_1920X1080P24,
> + V4L2_DV_BT_CEA_1920X1080P30,
> + V4L2_DV_BT_CEA_1920X1080I50,
> + V4L2_DV_BT_CEA_1920X1080I60,
> + V4L2_DV_BT_CEA_1920X1080P50,
> + V4L2_DV_BT_CEA_1920X1080P60,
> + V4L2_DV_BT_CEA_3840X2160P24,
> + V4L2_DV_BT_CEA_3840X2160P30,
> + V4L2_DV_BT_CEA_3840X2160P50,
> + V4L2_DV_BT_CEA_3840X2160P60,
> + V4L2_DV_BT_CEA_4096X2160P24,
> + V4L2_DV_BT_CEA_4096X2160P25,
> + V4L2_DV_BT_CEA_4096X2160P30,
> + V4L2_DV_BT_CEA_4096X2160P50,
> + V4L2_DV_BT_CEA_4096X2160P60,
> +
> + XLNX_V4L2_DV_BT_2048X1080P24,
> + XLNX_V4L2_DV_BT_2048X1080P25,
> + XLNX_V4L2_DV_BT_2048X1080P30,
> + XLNX_V4L2_DV_BT_2048X1080I48,
> + XLNX_V4L2_DV_BT_2048X1080I50,
> + XLNX_V4L2_DV_BT_2048X1080I60,
> + XLNX_V4L2_DV_BT_2048X1080P48,
> + XLNX_V4L2_DV_BT_2048X1080P50,
> + XLNX_V4L2_DV_BT_2048X1080P60,
> + XLNX_V4L2_DV_BT_1920X1080P48,
> + XLNX_V4L2_DV_BT_1920X1080I48,
> + XLNX_V4L2_DV_BT_3840X2160P48,
> + XLNX_V4L2_DV_BT_4096X2160P48,
> +};
> +
> +struct xsdirxss_dv_map {
> + u32 width;
> + u32 height;
> + u32 fps;
> + struct v4l2_dv_timings format;
> +};
> +
> +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> + /* SD - 720x487i60 */
> + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> + /* SD - 720x576i50 */
> + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> + /* HD - 1280x720p23.98 */
> + /* HD - 1280x720p24 */
> + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> + /* HD - 1280x720p25 */
> + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> + /* HD - 1280x720p29.97 */
> + /* HD - 1280x720p30 */
> + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> + /* HD - 1280x720p50 */
> + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> + /* HD - 1280x720p59.94 */
> + /* HD - 1280x720p60 */
> + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> + /* HD - 1920x1080p23.98 */
> + /* HD - 1920x1080p24 */
> + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> + /* HD - 1920x1080p25 */
> + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> + /* HD - 1920x1080p29.97 */
> + /* HD - 1920x1080p30 */
> + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> +
> + /* HD - 2048x1080p23.98 */
> + /* HD - 2048x1080p24 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> + /* HD - 2048x1080p25 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> + /* HD - 2048x1080p29.97 */
> + /* HD - 2048x1080p30 */
> + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> + /* HD - 1920x1080i47.95 */
> + /* HD - 1920x1080i48 */
> + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> +
> + /* HD - 1920x1080i50 */
> + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> + /* HD - 1920x1080i59.94 */
> + /* HD - 1920x1080i60 */
> + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> +
> + /* HD - 2048x1080i47.95 */
> + /* HD - 2048x1080i48 */
> + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> + /* HD - 2048x1080i50 */
> + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> + /* HD - 2048x1080i59.94 */
> + /* HD - 2048x1080i60 */
> + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> + /* 3G - 1920x1080p47.95 */
> + /* 3G - 1920x1080p48 */
> + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> +
> + /* 3G - 1920x1080p50 148.5 */
> + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> + /* 3G - 1920x1080p59.94 148.5/1.001 */
> + /* 3G - 1920x1080p60 148.5 */
> + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> +
> + /* 3G - 2048x1080p47.95 */
> + /* 3G - 2048x1080p48 */
> + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> + /* 3G - 2048x1080p50 */
> + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> + /* 3G - 2048x1080p59.94 */
> + /* 3G - 2048x1080p60 */
> + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> +
> + /* 6G - 3840X2160p23.98 */
> + /* 6G - 3840X2160p24 */
> + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> + /* 6G - 3840X2160p25 */
> + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> + /* 6G - 3840X2160p29.97 */
> + /* 6G - 3840X2160p30 */
> + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> + /* 6G - 4096X2160p23.98 */
> + /* 6G - 4096X2160p24 */
> + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> + /* 6G - 4096X2160p25 */
> + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> + /* 6G - 4096X2160p29.97 */
> + /* 6G - 4096X2160p30 */
> + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> + /* 12G - 3840X2160p47.95 */
> + /* 12G - 3840X2160p48 */
> + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> +
> + /* 12G - 3840X2160p50 */
> + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> + /* 12G - 3840X2160p59.94 */
> + /* 12G - 3840X2160p60 */
> + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> +
> + /* 12G - 4096X2160p47.95 */
> + /* 12G - 4096X2160p48 */
> + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> +
> + /* 12G - 4096X2160p50 */
> + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> + /* 12G - 4096X2160p59.94 */
> + /* 12G - 4096X2160p60 */
> + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> +};
> +
> +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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
> +{
> + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, XSDIRX_RST_CTRL_SS_EN_MASK);
> +}
> +
> +static inline 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 val;
> + struct device *dev = core->dev;
> +
> + mask &= XSDIRX_DETECT_ALL_MODES;
> + if (!mask) {
> + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> + return -EINVAL;
> + }
> +
> + dev_dbg(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(dev, "Detect multiple modes\n");
> +
> + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> +
> + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> + } else {
> + /* Fixed Mode */
> + u32 forced_mode_mask;
> +
> + dev_dbg(dev, "Detect fixed mode\n");
> +
> + /* Find offset of first bit set */
> + switch (__ffs(mask)) {

Isn't it single bit at this point, so __ffs() is not needed?

> + 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;
> + default:
> + forced_mode_mask = 0;

Shouldn't this be an error?

> + }
> + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> + forced_mode_mask);
> + val |= forced_mode_mask << XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> + }
> +
> + dev_dbg(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);

I'd use {}, but probably up to you.

> +}
> +
> +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32 enable)

Can this and similar names have some '_'? Maybe the name gets too long. I know
how difficult naming can be. :-)

> +{
> + u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> +
> + val = enable & XSDIRX_EDH_ALLERR_MASK;
> +

Nit. I'd remove this empty line, but up to you.

> + xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> +}
> +
> +static inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> +{
> + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> +}
> +
> +static inline 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 inline void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> +{
> + xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> +}

Not sure if these single line wrappers are needed. Inlining would be simpler.

> +
> +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core,
> + bool enable)
> +{
> + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> +
> + /*
> + * TODO - Enable YUV444/RBG format in the bridge based
> + * on BYTE3 color format.
> + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> + */
> + if (enable)
> + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> + else
> + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, 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);
> + }
> +}

Even these wrappers. The if check for enable can be removed when these are
inlined, hence less operations.

> +
> +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;
> + }
> +}
> +
> +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> +{
> + struct clk *gtclk;
> + unsigned long clkrate;
> + int ret, is_frac;
> + struct xsdirxss_core *core = &state->core;
> + u32 mode;
> + static int prev_is_frac = -1;

This would prevent multiple instances. Can it be part of xsdirxss_state?

But I wonder why this previous setting and also being fractional fps
really matter. Instead, this always has to set to the required rate.

> +
> + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> +
> + /*
> + * TODO: For now, don't change the clock rate for any mode except 12G.
> + * In future, configure gt clock for all modes and enable clock only
> + * when needed (stream on/off).
> + */
> + if (mode != XSDIRX_MODE_12GI_MASK && mode != XSDIRX_MODE_12GF_MASK)
> + return;
> +
> + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;

This is related to how frame intervals are defined in xsdirxss_get_framerate(),
correct? Maybe some comments on both sides would be helpful, saying numerator
== 1001 is treated as fractional fps?

> +
> + if (prev_is_frac == is_frac)
> + return;
> +
> + xsdirx_core_disable(core);
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +
> + /* get sdi_rx_clk */
> + gtclk = core->clks[1].clk;
> + clkrate = clk_get_rate(gtclk);

This is not needed.

> + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;

Not needed.

> +
> + /* calcualte clkrate */
> + if (!is_frac)
> + clkrate = CLK_INT;
> + else
> + clkrate = (CLK_INT * 1000) / 1001;
> +
> + ret = clk_set_rate(gtclk, clkrate);
> + if (ret)
> + dev_err(core->dev, "failed to set clk rate = %d\n", ret);

If this puts the controller in non-functional state, better to be an error,
and stop.

> +
> + clkrate = clk_get_rate(gtclk);

Shouldn't this rate be checked as set_rate() doesn't guarantee the requested
rate?

> +
> + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> + clkrate, is_frac);
> +
> + xsdirx_framer(core, state->framer_enable);
> + xsdirx_setedherrcnttrigger(core, state->edhmask);
> + xsdirx_setvidlockwindow(core, state->vidlockwin);
> + xsdirx_set_modedetect(core, state->searchmask);
> + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> + xsdirx_globalintr(core, true);
> + xsdirx_core_enable(core);
> +
> + prev_is_frac = is_frac;
> +}
> +
> +/**
> + * 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;
> + struct device *dev = core->dev;
> + 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->format;
> + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> +
> + 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> + active_luma = mask_and_rshift(payload,
> + XST352_BYTE3_ACT_LUMA_COUNT);
> + pic_type = mask_and_rshift(payload, XST352_BYTE2_PIC_TYPE);
> + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> + sampling = mask_and_rshift(payload, XST352_BYTE3_COLOR_FORMAT);
> + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> + } else {
> + dev_dbg(dev, "No ST352 payload available : Mode = %d\n", mode);
> + framerate = mask_and_rshift(val, XSDIRX_TS_DET_STAT_RATE);
> + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> + }
> +
> + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc = %d\n",
> + bpc, core->bpc);
> + return -EINVAL;
> + }
> +
> + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> + state->ts_is_interlaced = tscan ? false : true;
> +
> + dev_dbg(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(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(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) {

This seems like a little too much nested switch - case and if statements in
single function. Do you think this can be cleaned up using some table,
ex using XSDIRX_MODE_*, XSDIRX_TS_DET_STAT_RATE_*, adjusted XST352_BYTE1_*,,,
as indexes to it? Or with separate functions Or is it overkill? I let you
decide though.

> + 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(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(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(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(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;
> + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> + format->height = 1080;
> + if (active_luma)
> + format->width = 2048;
> + else
> + format->width = 1920;
> + break;
> + default:
> + dev_dbg(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(dev, "Unknown 12G Mode SMPTE standard\n");
> + return -EINVAL;
> + }
> + break;
> + default:
> + dev_err(dev, "Invalid Mode\n");
> + return -EINVAL;
> + }
> +
> + if (valid) {
> + if (pic_type)
> + format->field = V4L2_FIELD_NONE;
> + else
> + format->field = V4L2_FIELD_ALTERNATE;
> +
> + if (format->height == 1080 && pic_type && !tscan)
> + format->field = V4L2_FIELD_ALTERNATE;
> +
> + /*
> + * In 3GB DL pSF mode the video is similar to interlaced.
> + * So though it is a progressive video, its transport is
> + * interlaced and is sent as two width x (height/2) buffers.
> + */
> + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> + if (state->ts_is_interlaced)
> + format->field = V4L2_FIELD_ALTERNATE;
> + else
> + format->field = V4L2_FIELD_NONE;
> + }
> + }
> +
> + if (format->field == V4L2_FIELD_ALTERNATE)
> + format->height = format->height / 2;
> +
> + switch (sampling) {
> + case XST352_BYTE3_COLOR_FORMAT_422:
> + if (core->bpc == 10)
> + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> + else
> + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> + break;
> + case XST352_BYTE3_COLOR_FORMAT_420:
> + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> + case XST352_BYTE3_COLOR_FORMAT_GBR:
> + format->code = 0;
> + dev_dbg(dev, "No corresponding media bus formats\n");
> + break;
> + default:
> + dev_err(dev, "Unsupported color format : %d\n", sampling);
> + return -EINVAL;
> + }
> +
> + xsdirxss_get_framerate(&state->frame_interval, framerate);
> +
> + dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
> + state->frame_interval.numerator,
> + state->frame_interval.denominator);
> + dev_dbg(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 writing 1 to corresponding bit.
> + *
> + * 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;
> + struct device *dev = core->dev;
> + u32 status;
> +
> + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> + dev_dbg(dev, "interrupt status = 0x%08x\n", status);

maybe,
if (!status)
return IRQ_NONE;

> +
> + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> + u32 val1, val2;
> +
> + dev_dbg(dev, "video lock/unlock interrupt\n");
> +
> + xsdirx_streamflow_control(core, false);
> + state->streaming = false;
> +
> + 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
> + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> +
> + if (!xsdirx_get_stream_properties(state)) {
> + state->vidlocked = true;
> + xsdirxss_set_gtclk(state);
> + } else {
> + dev_err(dev, "Unable to get stream properties!\n");
> + state->vidlocked = false;
> + }
> + } else {
> + dev_dbg(dev, "video unlock interrupt\n");
> + state->vidlocked = false;
> + }
> +
> + 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);
> + }
> +
> + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> + dev_dbg(dev, "Video in to AXI4 Stream core underflow interrupt\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> + v4l2_subdev_notify_event(&state->subdev, &state->event);
> + }
> +
> + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> + dev_dbg(dev, "Video in to AXI4 Stream core overflow interrupt\n");
> +
> + memset(&state->event, 0, sizeof(state->event));
> + state->event.type = V4L2_EVENT_XILINX_SDIRX_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_XILINX_SDIRX_UNDERFLOW:
> + case V4L2_EVENT_XILINX_SDIRX_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);

Having this in the beginning may help catch invalid subcription type too?

> + 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;
> + struct device *dev = core->dev;
> +
> + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> + ctrl->id, ctrl->val);
> +
> + if (xsdirxss->streaming) {
> + dev_err(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);
> + xsdirxss->framer_enable = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> + xsdirx_setvidlockwindow(core, ctrl->val);
> + xsdirxss->vidlockwin = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> + xsdirx_setedherrcnttrigger(core, ctrl->val);
> + xsdirxss->edhmask = ctrl->val;
> + break;
> + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> + if (ctrl->val) {
> + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> + dev_dbg(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(dev, "Upto 6G supported\n");
> + ctrl->val &= ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> + BIT(XSDIRX_MODE_12GF_OFFSET));
> + }
> +
> + ret = xsdirx_set_modedetect(core, ctrl->val);
> + if (!ret)
> + xsdirxss->searchmask = ctrl->val;
> + } else {

This can be done in te begining.

if (!ctrl->val) {
dev_err();
return -EINVAL;
}

if (core->mode ...)

> + dev_err(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);

I realized that the core is enabled all the time. Can it be enabled / disabled
per stream on / off? Or is it tricky?

> + 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;
> + struct device *dev = core->dev;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> + if (!xsdirxss->vidlocked) {
> + dev_err(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(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(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(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(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(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(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> + return -EINVAL;
> + }
> + dev_dbg(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 i;
> +
> + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> + for (i = 0; i < 0x28; i++) {
> + u32 data;
> +
> + 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;
> +}
> +
> +/**
> + * 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;
> + struct device *dev = core->dev;
> +
> + if (enable) {
> + if (!xsdirxss->vidlocked) {
> + dev_dbg(dev, "Video is not locked\n");

dev_err()?

> + return -EINVAL;
> + }
> + if (xsdirxss->streaming) {
> + dev_dbg(dev, "Already streaming\n");
> + return -EINVAL;

Is this an error? I think it can just return 0 instead.

> + }
> +
> + xsdirx_streamflow_control(core, true);
> + xsdirxss->streaming = true;
> + dev_dbg(dev, "Streaming started\n");
> + } else {
> + if (!xsdirxss->streaming) {
> + dev_dbg(dev, "Stopped streaming already\n");
> + return 0;
> + }
> +
> + xsdirx_streamflow_control(core, false);
> + xsdirxss->streaming = false;
> + dev_dbg(dev, "Streaming stopped\n");
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_g_input_status - It is used to determine if the video signal
> + * is present / locked onto or not.
> + *
> + * @sd: V4L2 Sub device
> + * @status: status of signal locked
> + *
> + * This is used to determine if the video signal is present and locked onto
> + * by the SDI Rx core or not based on vidlocked flag.
> + *
> + * Return: zero on success
> + */
> +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> +
> + if (!xsdirxss->vidlocked)
> + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> + else
> + *status = 0;
> +
> + 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->format;
> + default:
> + return NULL;
> + }
> +}
> +
> +/**
> + * xsdirxss_init_cfg - Initialise the pad format config to default
> + * @sd: Pointer to V4L2 Sub device structure
> + * @cfg: Pointer to sub device pad information structure
> + *
> + * This function is used to initialize the pad format with the default
> + * values.
> + *
> + * Return: 0 on success
> + */
> +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> + struct v4l2_mbus_framefmt *format;
> +
> + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> + *format = xsdirxss->default_format;
> +
> + return 0;
> +}
> +
> +/**
> + * 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 */

Ah, what blocks to implement this? :-)

> + fmt->format.code = __format->code;
> +
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> + * @sd: pointer to v4l2 subdev structure
> + * @cfg: V4L2 subdev pad configuration
> + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> + *
> + * Return: -EINVAL or zero on success
> + */
> +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> + u32 index = code->index;
> + u32 maxindex;
> +
> + if (xsdirxss->core.bpc == 10)
> + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> + else
> + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> +
> + if (code->pad || index >= maxindex)
> + return -EINVAL;
> +
> + if (xsdirxss->core.bpc == 10)
> + code->code = xsdirxss_10bpc_mbus_fmts[index];
> + else
> + code->code = xsdirxss_12bpc_mbus_fmts[index];
> +
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> + * @sd: pointer to v4l2 subdev structure
> + * @timings: DV timings structure to be returned.
> + *
> + * Return: -EINVAL incase of invalid index and pad or zero on success
> + */
> +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> + struct v4l2_enum_dv_timings *timings)
> +{
> + if (timings->index >= ARRAY_SIZE(fmt_cap))
> + return -EINVAL;
> +
> + if (timings->pad != 0)
> + return -EINVAL;
> +
> + timings->timings = fmt_cap[timings->index];
> + return 0;
> +}
> +
> +/**
> + * xsdirxss_query_dv_timings: Query for the current DV timings
> + * @sd: pointer to v4l2 subdev structure
> + * @timings: DV timings structure to be returned.
> + *
> + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding timing
> + * entry is not found or zero on success.
> + */
> +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> + struct v4l2_dv_timings *timings)
> +{
> + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> + unsigned int i;
> +
> + if (!state->vidlocked)
> + return -ENOLCK;
> +
> + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> + if (state->format.width == xsdirxss_dv_timings[i].width &&
> + state->format.height == xsdirxss_dv_timings[i].height &&
> + state->frame_interval.denominator ==
> + (xsdirxss_dv_timings[i].fps * 1000)) {
> + *timings = xsdirxss_dv_timings[i].format;
> + return 0;
> + }
> + }
> +
> + return -ERANGE;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * 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 const 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 const 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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> + .g_frame_interval = xsdirxss_g_frame_interval,
> + .s_stream = xsdirxss_s_stream,
> + .g_input_status = xsdirxss_g_input_status,
> + .query_dv_timings = xsdirxss_query_dv_timings,
> +};
> +
> +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> + .init_cfg = xsdirxss_init_cfg,
> + .get_fmt = xsdirxss_get_format,
> + .set_fmt = xsdirxss_set_format,
> + .enum_mbus_code = xsdirxss_enum_mbus_code,
> + .enum_dv_timings = xsdirxss_enum_dv_timings,
> +};
> +
> +static const struct v4l2_subdev_ops xsdirxss_ops = {
> + .core = &xsdirxss_core_ops,
> + .video = &xsdirxss_video_ops,
> + .pad = &xsdirxss_pad_ops
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Platform Device Driver
> + */
> +
> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> +{
> + struct device_node *node = xsdirxss->core.dev->of_node;
> + struct xsdirxss_core *core = &xsdirxss->core;
> + struct device *dev = core->dev;
> + struct fwnode_handle *ep, *rep;
> + int ret;
> + const char *sdi_std;
> +
> + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> + dev_dbg(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(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(dev, "Invalid Line Rate\n");
> + return -EINVAL;
> + }
> + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> + core->mode);
> +
> + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);

But, the bpp and bpc are quite different. Shouldn't it be bpc?

> + if (ret < 0) {
> + dev_err(dev, "failed to get xlnx,bpp\n");
> + return ret;
> + }
> +
> + if (core->bpc != 10 && core->bpc != 12) {
> + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> + core->bpc);
> + return -EINVAL;
> + }
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> + FWNODE_GRAPH_ENDPOINT_NEXT);
> + if (!ep) {
> + dev_err(dev, "no source port found");
> + ret = -EINVAL;
> + goto dt_parse_done;
> + }
> +
> + rep = fwnode_graph_get_remote_endpoint(ep);
> + if (!rep) {
> + dev_err(dev, "no remote sink endpoint found");
> + ret = -EINVAL;
> + }
> +
> + fwnode_handle_put(rep);
> +dt_parse_done:
> + fwnode_handle_put(ep);
> + return ret;
> +}
> +
> +static int xsdirxss_probe(struct platform_device *pdev)
> +{
> + struct v4l2_subdev *subdev;
> + struct xsdirxss_state *xsdirxss;
> + struct xsdirxss_core *core;
> + struct device *dev;
> + 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;
> + dev = core->dev;
> +
> + /* Register interrupt handler */
> + core->irq = platform_get_irq(pdev, 0);

As mentioned, this doesn't have to be stored.

> + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> + xsdirxss_irq_handler, IRQF_ONESHOT,
> + dev_name(dev), xsdirxss);
> + if (ret) {
> + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> + ret);
> + return ret;
> + }
> +
> + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> + core->clks = devm_kcalloc(dev, core->num_clks,
> + sizeof(*core->clks), GFP_KERNEL);
> + if (!core->clks)
> + return -ENOMEM;
> +
> + for (i = 0; i < core->num_clks; i++)
> + core->clks[i].id = xsdirxss_clks[i];
> +
> + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> + if (ret)
> + return ret;
> +
> + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> + if (ret)
> + return ret;
> +
> + ret = xsdirxss_parse_of(xsdirxss);
> + if (ret < 0)
> + goto clk_err;
> +
> + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(core->iomem)) {
> + ret = PTR_ERR(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->pad.flags = MEDIA_PAD_FL_SOURCE;
> +
> + /* Initialize the default format */
> + if (core->bpc == 10)
> + xsdirxss->default_format.code = MEDIA_BUS_FMT_UYVY10_1X20;
> + else
> + xsdirxss->default_format.code = MEDIA_BUS_FMT_UYVY12_1X24;
> + 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->format = xsdirxss->default_format;
> +
> + /* Initialize V4L2 subdevice and media entity */
> + subdev = &xsdirxss->subdev;
> + v4l2_subdev_init(subdev, &xsdirxss_ops);
> +
> + subdev->dev = &pdev->dev;
> + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name));
> +
> + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;

I guess it doesn't matter, but this can be assigned directly to be clearer, not
ORed.

> +
> + subdev->entity.ops = &xsdirxss_media_ops;
> +
> + v4l2_set_subdevdata(subdev, xsdirxss);
> +
> + ret = media_entity_pads_init(&subdev->entity, 1, &xsdirxss->pad);
> + if (ret < 0)
> + goto error;
> +
> + /* Initialise and register the controls */
> + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> +
> + if (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(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(dev, "Failed to add %s ctrl\n",
> + xsdirxss_ctrls[i].name);
> + goto error;
> + }
> + }
> +
> + if (core->include_edh) {
> + for (i = 0; i < num_edh_ctrls; i++) {
> + struct v4l2_ctrl *ctrl;
> +
> + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> + xsdirxss_edh_ctrls[i].name);
> + goto error;
> + }
> + }
> + }
> +
> + if (xsdirxss->ctrl_handler.error) {
> + dev_err(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(dev, "failed to set controls\n");
> + goto error;
> + }
> +
> + platform_set_drvdata(pdev, xsdirxss);
> +
> + ret = v4l2_async_register_subdev(subdev);
> + if (ret < 0) {
> + dev_err(dev, "failed to register subdev\n");
> + goto error;
> + }
> +
> + xsdirxss->streaming = false;

Is this needed?

> +
> + xsdirx_core_enable(core);
> +
> + dev_info(dev, "probe success\n");
> +
> + return 0;
> +error:
> + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> +clk_err:
> + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> + return ret;
> +}
> +
> +static int xsdirxss_remove(struct platform_device *pdev)
> +{
> + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> + struct xsdirxss_core *core = &xsdirxss->core;
> + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> +
> + v4l2_async_unregister_subdev(subdev);
> + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> + media_entity_cleanup(&subdev->entity);
> +
> + xsdirx_globalintr(core, false);
> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> + xsdirx_core_disable(core);

I believe the core has to be disabled first.

> + xsdirx_streamflow_control(core, false);

But to be safer side, maybe all these can be done first? Similar for enable,
so enabling all after v4l/media registrations?

Thanks,
-hyun

> +
> + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id xsdirxss_of_id_table[] = {
> + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> + { }
> +};
> +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 <[email protected]>");
> +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 000000000000..6f2a093968d9
> --- /dev/null
> +++ b/include/uapi/linux/xilinx-sdirxss.h
> @@ -0,0 +1,179 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Xilinx SDI Rx Subsystem mode, event, custom timings

Nit. ',' at the end.

> + * and flag definitions.
> + *
> + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> + *
> + * Contacts: Vishal Sagar <[email protected]>
> + */
> +
> +#ifndef __UAPI_XILINX_SDIRXSS_H__
> +#define __UAPI_XILINX_SDIRXSS_H__
> +
> +#include <linux/types.h>
> +#include <linux/v4l2-dv-timings.h>
> +#include <linux/videodev2.h>
> +
> +/*
> + * Events
> + *
> + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core underflowed
> + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core overflowed
> + */
> +#define V4L2_EVENT_XILINX_SDIRX_CLASS (V4L2_EVENT_PRIVATE_START | 0x200)
> +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)

Since this is many definitions for a single subdev, do you think some of these
can be minimized? For example, if the action for underflow/overflow isn't
different, resetting the device, then these can fold into one event.


Could you double-check some below too? Unless each is associated with different
handling, it'd be good to minimize the user interface, ex by merging or even
removing.

Thanks,
-hyun

> +
> +/*
> + * 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
> +
> +/* Xilinx DV timings not in mainline yet */
> +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> + .type = V4L2_DV_BT_656_1120, \
> + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> + V4L2_DV_BT_STD_SDI) \
> +}
> +
> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
> index b6441fe705c5..e9de65e82642 100644
> --- a/include/uapi/linux/xilinx-v4l2-controls.h
> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> @@ -71,4 +71,71 @@
> /* Noise level */
> #define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17)
>
> +/*
> + * Xilinx SDI Rx Subsystem
> + */
> +
> +/* The base for the sdi rx driver controls.
> + * We reserve 32 controls for this driver.
> + *
> + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> + * EDH is enabled.
> + * The controls which can be set should only be set before enabling
> + * streaming. The controls which can be got should be called while
> + * streaming to get correct values.
> + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query dv timing
> + * returns a valid timing.
> + */
> +
> +#define V4L2_CID_XILINX_SDIRX (V4L2_CID_XILINX_BASE + 0x20)
> +
> +/* Framer Control to enable or disable the framer */
> +#define V4L2_CID_XILINX_SDIRX_FRAMER (V4L2_CID_XILINX_SDIRX + 1)
> +/*
> + * Video Lock Window Control to set the video lock window value
> + * This is the amount of time the mode and transport stream need
> + * to be locked before a video lock interrupt occurs.
> + */
> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW (V4L2_CID_XILINX_SDIRX + 2)
> +/* EDH Error Mask Control to enable EDH error count */
> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE (V4L2_CID_XILINX_SDIRX + 3)
> +/*
> + * Mode search Control to pass the bit mask of modes to detect.
> + *
> + * bit 0 set to detect SD mode,
> + * bit 1 set to detect HD mode,
> + * bit 2 set to detect 3G (3GA & 3GB) mode,
> + * bit 3 set to detect 6G mode,
> + * bit 4 set to detect 12G integer frame rate mode,
> + * bit 5 set to detect 12G fractional frame rate mode,
> + */
> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES (V4L2_CID_XILINX_SDIRX + 4)
> +/*
> + * Get Detected Mode control
> + *
> + * Control Value - Mode detected
> + * 0 - SD
> + * 1 - HD
> + * 2 - 3G (3GA & 3GB)
> + * 3 - 6G
> + * 4 - 12G integer frame rate
> + * 5 - 12G fractional frame rate
> + */
> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT (V4L2_CID_XILINX_SDIRX + 5)
> +/* Get number of CRC errors status control */
> +#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 whether it is interlaced or not */
> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED (V4L2_CID_XILINX_SDIRX + 9)
> +/* Get number of Active Streams */
> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS (V4L2_CID_XILINX_SDIRX + 10)
> +/*
> + * Get if the detected mode is 3GB.
> + * Can be used to distinguished between 3GA and 3GB
> + */
> +#define V4L2_CID_XILINX_SDIRX_IS_3GB (V4L2_CID_XILINX_SDIRX + 11)
> +
> #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> --
> 2.21.0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

2020-06-01 14:47:05

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Hi Hans,

Thanks for reviewing!

> -----Original Message-----
> From: Hans Verkuil <[email protected]>
> Sent: Wednesday, May 6, 2020 3:25 PM
> To: Vishal Sagar <[email protected]>; Hyun Kwon <[email protected]>;
> [email protected]; [email protected];
> [email protected]; [email protected]; Michal Simek
> <[email protected]>; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]; Dinesh Kumar
> <[email protected]>; Sandip Kothari <[email protected]>; Joe Perches
> <[email protected]>
> Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
>
> Hi Vishal,
>
> Thank you for this patch.
>
> I have some comments below:
>
> On 29/04/2020 16:17, 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 10/12 bpc YUV 422 media bus format currently. 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 IP configuration.
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > v2
> > - Added DV timing support based on Hans Verkuilś feedback
> > - More documentation to custom v4l controls and events
> > - Fixed Hyunś comments
> > - Added macro for masking and shifting as per Joe Perches comments
> > - Updated to latest as per Xilinx github repo driver like
> > adding new DV timings not in mainline yet uptill 03/21/20
> >
> > drivers/media/platform/xilinx/Kconfig | 11 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > 5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -12,6 +12,17 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +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 4cdc0b1ec7a5..6c375f03f587 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -2,6 +2,7 @@
> >
> > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> >
> > +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,2162 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + *
> > + */
> > +
> > +#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-dv-timings.h>
> > +#include <linux/v4l2-subdev.h>
> > +#include <linux/xilinx-sdirxss.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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12,
> 10)
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> > +
> > +#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_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)
> > +
> > +#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_BYTE1_MASK 0xFF
> > +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> > +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> > +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> > +#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
> > +#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_OFFSET 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_YUV444 0x1
> > +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> > +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> > +
> > +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> > +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> > +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> > +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> > +
> > +#define CLK_INT 148500000UL
> > +
> > +#define rshift_and_mask(val, type) \
> > + (((val) >> type##_OFFSET) & type##_MASK)
> > +
> > +#define mask_and_rshift(val, type) \
> > + (((val) & type##_MASK) >> type##_OFFSET)
> > +
> > +/**
> > + * 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
> > + * @clks: array of clocks
> > + * @num_clks: number of clocks
> > + * @bpc: Bits per component, can be 10 or 12
> > + */
> > +struct xsdirxss_core {
> > + struct device *dev;
> > + void __iomem *iomem;
> > + int irq;
> > + bool include_edh;
> > + int mode;
> > + struct clk_bulk_data *clks;
> > + int num_clks;
> > + u32 bpc;
> > +};
> > +
> > +/**
> > + * 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
> > + * @format: Active V4L2 format on source pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @pad: source media pad
> > + * @vidlockwin: Video lock window value set by control
> > + * @edhmask: EDH mask set by control
> > + * @searchmask: Search mask set by control
> > + * @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.
> > + * @framer_enable: Flag for framer enabled or not set by control
> > + *
> > + * 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 format;
> > + struct v4l2_mbus_framefmt default_format;
> > + struct v4l2_fract frame_interval;
> > + struct media_pad pad;
> > + u32 vidlockwin;
> > + u32 edhmask;
> > + u16 searchmask;
> > + bool streaming;
> > + bool vidlocked;
> > + bool ts_is_interlaced;
> > + bool framer_enable;
> > +};
> > +
> > +/* List of clocks required by UHD-SDI Rx subsystem */
> > +static const char * const xsdirxss_clks[] = {
> > + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> > +};
> > +
> > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> > +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY10_1X20,
> > +};
> > +
> > +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY12_1X24,
> > +};
> > +
> > +static const struct v4l2_dv_timings fmt_cap[] = {
> > + V4L2_DV_BT_SDI_720X487I60,
> > + V4L2_DV_BT_CEA_720X576I50,
> > + V4L2_DV_BT_CEA_1280X720P24,
> > + V4L2_DV_BT_CEA_1280X720P25,
> > + V4L2_DV_BT_CEA_1280X720P30,
> > + V4L2_DV_BT_CEA_1280X720P50,
> > + V4L2_DV_BT_CEA_1280X720P60,
> > + V4L2_DV_BT_CEA_1920X1080P24,
> > + V4L2_DV_BT_CEA_1920X1080P30,
> > + V4L2_DV_BT_CEA_1920X1080I50,
> > + V4L2_DV_BT_CEA_1920X1080I60,
> > + V4L2_DV_BT_CEA_1920X1080P50,
> > + V4L2_DV_BT_CEA_1920X1080P60,
> > + V4L2_DV_BT_CEA_3840X2160P24,
> > + V4L2_DV_BT_CEA_3840X2160P30,
> > + V4L2_DV_BT_CEA_3840X2160P50,
> > + V4L2_DV_BT_CEA_3840X2160P60,
> > + V4L2_DV_BT_CEA_4096X2160P24,
> > + V4L2_DV_BT_CEA_4096X2160P25,
> > + V4L2_DV_BT_CEA_4096X2160P30,
> > + V4L2_DV_BT_CEA_4096X2160P50,
> > + V4L2_DV_BT_CEA_4096X2160P60,
> > +
> > + XLNX_V4L2_DV_BT_2048X1080P24,
> > + XLNX_V4L2_DV_BT_2048X1080P25,
> > + XLNX_V4L2_DV_BT_2048X1080P30,
> > + XLNX_V4L2_DV_BT_2048X1080I48,
> > + XLNX_V4L2_DV_BT_2048X1080I50,
> > + XLNX_V4L2_DV_BT_2048X1080I60,
> > + XLNX_V4L2_DV_BT_2048X1080P48,
> > + XLNX_V4L2_DV_BT_2048X1080P50,
> > + XLNX_V4L2_DV_BT_2048X1080P60,
> > + XLNX_V4L2_DV_BT_1920X1080P48,
> > + XLNX_V4L2_DV_BT_1920X1080I48,
> > + XLNX_V4L2_DV_BT_3840X2160P48,
> > + XLNX_V4L2_DV_BT_4096X2160P48,
> > +};
> > +
> > +struct xsdirxss_dv_map {
> > + u32 width;
> > + u32 height;
> > + u32 fps;
> > + struct v4l2_dv_timings format;
> > +};
> > +
> > +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> > + /* SD - 720x487i60 */
> > + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> > + /* SD - 720x576i50 */
> > + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> > + /* HD - 1280x720p23.98 */
> > + /* HD - 1280x720p24 */
> > + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> > + /* HD - 1280x720p25 */
> > + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> > + /* HD - 1280x720p29.97 */
> > + /* HD - 1280x720p30 */
> > + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> > + /* HD - 1280x720p50 */
> > + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> > + /* HD - 1280x720p59.94 */
> > + /* HD - 1280x720p60 */
> > + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> > + /* HD - 1920x1080p23.98 */
> > + /* HD - 1920x1080p24 */
> > + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> > + /* HD - 1920x1080p25 */
> > + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> > + /* HD - 1920x1080p29.97 */
> > + /* HD - 1920x1080p30 */
> > + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> > +
> > + /* HD - 2048x1080p23.98 */
> > + /* HD - 2048x1080p24 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> > + /* HD - 2048x1080p25 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> > + /* HD - 2048x1080p29.97 */
> > + /* HD - 2048x1080p30 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> > + /* HD - 1920x1080i47.95 */
> > + /* HD - 1920x1080i48 */
> > + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> > +
> > + /* HD - 1920x1080i50 */
> > + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> > + /* HD - 1920x1080i59.94 */
> > + /* HD - 1920x1080i60 */
> > + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> > +
> > + /* HD - 2048x1080i47.95 */
> > + /* HD - 2048x1080i48 */
> > + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> > + /* HD - 2048x1080i50 */
> > + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> > + /* HD - 2048x1080i59.94 */
> > + /* HD - 2048x1080i60 */
> > + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> > + /* 3G - 1920x1080p47.95 */
> > + /* 3G - 1920x1080p48 */
> > + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> > +
> > + /* 3G - 1920x1080p50 148.5 */
> > + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> > + /* 3G - 1920x1080p59.94 148.5/1.001 */
> > + /* 3G - 1920x1080p60 148.5 */
> > + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> > +
> > + /* 3G - 2048x1080p47.95 */
> > + /* 3G - 2048x1080p48 */
> > + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> > + /* 3G - 2048x1080p50 */
> > + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> > + /* 3G - 2048x1080p59.94 */
> > + /* 3G - 2048x1080p60 */
> > + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> > +
> > + /* 6G - 3840X2160p23.98 */
> > + /* 6G - 3840X2160p24 */
> > + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> > + /* 6G - 3840X2160p25 */
> > + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> > + /* 6G - 3840X2160p29.97 */
> > + /* 6G - 3840X2160p30 */
> > + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> > + /* 6G - 4096X2160p23.98 */
> > + /* 6G - 4096X2160p24 */
> > + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> > + /* 6G - 4096X2160p25 */
> > + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> > + /* 6G - 4096X2160p29.97 */
> > + /* 6G - 4096X2160p30 */
> > + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> > + /* 12G - 3840X2160p47.95 */
> > + /* 12G - 3840X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> > +
> > + /* 12G - 3840X2160p50 */
> > + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> > + /* 12G - 3840X2160p59.94 */
> > + /* 12G - 3840X2160p60 */
> > + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> > +
> > + /* 12G - 4096X2160p47.95 */
> > + /* 12G - 4096X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> > +
> > + /* 12G - 4096X2160p50 */
> > + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> > + /* 12G - 4096X2160p59.94 */
> > + /* 12G - 4096X2160p60 */
> > + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> > +};
> > +
> > +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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
> > +{
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static inline 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 val;
> > + struct device *dev = core->dev;
> > +
> > + mask &= XSDIRX_DETECT_ALL_MODES;
> > + if (!mask) {
> > + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> > + return -EINVAL;
> > + }
> > +
> > + dev_dbg(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(dev, "Detect multiple modes\n");
> > +
> > + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > +
> > + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > + } else {
> > + /* Fixed Mode */
> > + u32 forced_mode_mask;
> > +
> > + dev_dbg(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;
> > + default:
> > + forced_mode_mask = 0;
> > + }
> > + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> > + forced_mode_mask);
> > + val |= forced_mode_mask <<
> XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > + }
> > +
> > + dev_dbg(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 inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static inline 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 inline 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)
> > +{
> > + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> > +
> > + /*
> > + * TODO - Enable YUV444/RBG format in the bridge based
> > + * on BYTE3 color format.
> > + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> > + */
> > + if (enable)
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > + else
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, 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 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;
> > + }
> > +}
> > +
> > +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> > +{
> > + struct clk *gtclk;
> > + unsigned long clkrate;
> > + int ret, is_frac;
> > + struct xsdirxss_core *core = &state->core;
> > + u32 mode;
> > + static int prev_is_frac = -1;
> > +
> > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + /*
> > + * TODO: For now, don't change the clock rate for any mode except
> 12G.
> > + * In future, configure gt clock for all modes and enable clock only
> > + * when needed (stream on/off).
> > + */
> > + if (mode != XSDIRX_MODE_12GI_MASK && mode !=
> XSDIRX_MODE_12GF_MASK)
> > + return;
> > +
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + if (prev_is_frac == is_frac)
> > + return;
> > +
> > + xsdirx_core_disable(core);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +
> > + /* get sdi_rx_clk */
> > + gtclk = core->clks[1].clk;
> > + clkrate = clk_get_rate(gtclk);
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + /* calcualte clkrate */
> > + if (!is_frac)
> > + clkrate = CLK_INT;
> > + else
> > + clkrate = (CLK_INT * 1000) / 1001;
> > +
> > + ret = clk_set_rate(gtclk, clkrate);
> > + if (ret)
> > + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> > +
> > + clkrate = clk_get_rate(gtclk);
> > +
> > + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> > + clkrate, is_frac);
> > +
> > + xsdirx_framer(core, state->framer_enable);
> > + xsdirx_setedherrcnttrigger(core, state->edhmask);
> > + xsdirx_setvidlockwindow(core, state->vidlockwin);
> > + xsdirx_set_modedetect(core, state->searchmask);
> > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_globalintr(core, true);
> > + xsdirx_core_enable(core);
> > +
> > + prev_is_frac = is_frac;
> > +}
> > +
> > +/**
> > + * 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;
> > + struct device *dev = core->dev;
> > + 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->format;
> > + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> > +
> > + 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> > + active_luma = mask_and_rshift(payload,
> > +
> XST352_BYTE3_ACT_LUMA_COUNT);
> > + pic_type = mask_and_rshift(payload,
> XST352_BYTE2_PIC_TYPE);
> > + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> > + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> > + sampling = mask_and_rshift(payload,
> XST352_BYTE3_COLOR_FORMAT);
> > + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> > + } else {
> > + dev_dbg(dev, "No ST352 payload available : Mode = %d\n",
> mode);
> > + framerate = mask_and_rshift(val,
> XSDIRX_TS_DET_STAT_RATE);
> > + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> > + }
> > +
> > + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> > + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> > + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc =
> %d\n",
> > + bpc, core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> > + state->ts_is_interlaced = tscan ? false : true;
> > +
> > + dev_dbg(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(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(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(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(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(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(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;
> > + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(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(dev, "Unknown 12G Mode SMPTE
> standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + dev_err(dev, "Invalid Mode\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (valid) {
> > + if (pic_type)
> > + format->field = V4L2_FIELD_NONE;
> > + else
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + if (format->height == 1080 && pic_type && !tscan)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + /*
> > + * In 3GB DL pSF mode the video is similar to interlaced.
> > + * So though it is a progressive video, its transport is
> > + * interlaced and is sent as two width x (height/2) buffers.
> > + */
> > + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> > + if (state->ts_is_interlaced)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > + else
> > + format->field = V4L2_FIELD_NONE;
> > + }
> > + }
> > +
> > + if (format->field == V4L2_FIELD_ALTERNATE)
> > + format->height = format->height / 2;
> > +
> > + switch (sampling) {
> > + case XST352_BYTE3_COLOR_FORMAT_422:
> > + if (core->bpc == 10)
> > + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> > + break;
> > + case XST352_BYTE3_COLOR_FORMAT_420:
> > + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> > + case XST352_BYTE3_COLOR_FORMAT_GBR:
> > + format->code = 0;
> > + dev_dbg(dev, "No corresponding media bus formats\n");
> > + break;
> > + default:
> > + dev_err(dev, "Unsupported color format : %d\n", sampling);
> > + return -EINVAL;
> > + }
> > +
> > + xsdirxss_get_framerate(&state->frame_interval, framerate);
> > +
> > + dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
> > + state->frame_interval.numerator,
> > + state->frame_interval.denominator);
> > + dev_dbg(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 writing 1 to corresponding bit.
> > + *
> > + * 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;
> > + struct device *dev = core->dev;
> > + u32 status;
> > +
> > + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> > + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> > +
> > + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> > + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > + u32 val1, val2;
> > +
> > + dev_dbg(dev, "video lock/unlock interrupt\n");
> > +
> > + xsdirx_streamflow_control(core, false);
> > + state->streaming = false;
> > +
> > + 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
> > + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> > +
> > + if (!xsdirx_get_stream_properties(state)) {
> > + state->vidlocked = true;
> > + xsdirxss_set_gtclk(state);
> > + } else {
> > + dev_err(dev, "Unable to get stream
> properties!\n");
> > + state->vidlocked = false;
> > + }
> > + } else {
> > + dev_dbg(dev, "video unlock interrupt\n");
> > + state->vidlocked = false;
> > + }
> > +
> > + 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);
> > + }
> > +
> > + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core underflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core overflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_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_XILINX_SDIRX_UNDERFLOW:
> > + case V4L2_EVENT_XILINX_SDIRX_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;
>
> This should call v4l2_ctrl_subscribe_event instead, otherwise control events
> wouldn't be supported.
>

Ok I will add v4l2_ctrl_subscribe_event() here.

> > + }
> > + 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;
> > + struct device *dev = core->dev;
> > +
> > + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > + ctrl->id, ctrl->val);
> > +
> > + if (xsdirxss->streaming) {
> > + dev_err(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);
> > + xsdirxss->framer_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > + xsdirx_setvidlockwindow(core, ctrl->val);
> > + xsdirxss->vidlockwin = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > + xsdirx_setedherrcnttrigger(core, ctrl->val);
> > + xsdirxss->edhmask = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > + if (ctrl->val) {
> > + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > + dev_dbg(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(dev, "Upto 6G supported\n");
> > + ctrl->val &=
> ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +
> BIT(XSDIRX_MODE_12GF_OFFSET));
> > + }
> > +
> > + ret = xsdirx_set_modedetect(core, ctrl->val);
> > + if (!ret)
> > + xsdirxss->searchmask = ctrl->val;
> > + } else {
> > + dev_err(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;
> > + struct device *dev = core->dev;
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(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(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(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(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + ctrl->val = xsdirxss->ts_is_interlaced;
>
> This control makes no sense: the v4l2_dv_timings struct will already tell you
> if it is an interlaced format or not. Same for v4l2_mbus_framefmt.
>

The SDI has a concept of supporting progressive, interlaced (both as we know normally) and a progressive segmented frames(psf).
The progressive segmented frames have their video content in progressive format but the transport stream is interlaced.
This is distinguished using the bit 6 and 7 of Byte 2 in the 4 byte ST352 payload.
Refer to sec 5.3 in SMPTE ST 352:2010.

This control can be used by the application to distinguish normal interlaced and progressive segmented frames.

> > + break;
> > + case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(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(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(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + dev_dbg(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 i;
> > +
> > + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > + for (i = 0; i < 0x28; i++) {
> > + u32 data;
> > +
> > + 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;
>
> I suggest calling v4l2_ctrl_subdev_log_status() as well to log all controls.
>

Ok. I will add this in the next version.

> > +}
> > +
> > +/**
> > + * 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;
> > + struct device *dev = core->dev;
> > +
> > + if (enable) {
> > + if (!xsdirxss->vidlocked) {
> > + dev_dbg(dev, "Video is not locked\n");
> > + return -EINVAL;
> > + }
> > + if (xsdirxss->streaming) {
> > + dev_dbg(dev, "Already streaming\n");
> > + return -EINVAL;
> > + }
> > +
> > + xsdirx_streamflow_control(core, true);
> > + xsdirxss->streaming = true;
> > + dev_dbg(dev, "Streaming started\n");
> > + } else {
> > + if (!xsdirxss->streaming) {
> > + dev_dbg(dev, "Stopped streaming already\n");
> > + return 0;
> > + }
> > +
> > + xsdirx_streamflow_control(core, false);
> > + xsdirxss->streaming = false;
> > + dev_dbg(dev, "Streaming stopped\n");
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_input_status - It is used to determine if the video signal
> > + * is present / locked onto or not.
> > + *
> > + * @sd: V4L2 Sub device
> > + * @status: status of signal locked
> > + *
> > + * This is used to determine if the video signal is present and locked onto
> > + * by the SDI Rx core or not based on vidlocked flag.
> > + *
> > + * Return: zero on success
> > + */
> > +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > + if (!xsdirxss->vidlocked)
> > + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> > + else
> > + *status = 0;
> > +
> > + 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->format;
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +/**
> > + * xsdirxss_init_cfg - Initialise the pad format config to default
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + *
> > + * This function is used to initialize the pad format with the default
> > + * values.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct v4l2_mbus_framefmt *format;
> > +
> > + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> > + *format = xsdirxss->default_format;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * 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;
>
> It should fill in the width and height based on the current DV timings.
> Ditto for the field (I assume that's fixed as well based on whether this
> is interlaced or not). I'm not sure how colorspace information is handled
> for SDI.
>

Yes. I will update this logic to update the width, height and field based on current DV timings.
Please have a look at this in the next version.
The current IP supports BT709 colorimetry.
The colorimetry information of incoming stream is present in the ST 352 payload Byte 3 (bit 5 and bit 4) as per SMPTE ST 2081-10:2018.
B5:4 = 0 -> BT 709 as per SMPTE ST 274

> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> > + * @sd: pointer to v4l2 subdev structure
> > + * @cfg: V4L2 subdev pad configuration
> > + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> > + *
> > + * Return: -EINVAL or zero on success
> > + */
> > +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_mbus_code_enum
> *code)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + u32 index = code->index;
> > + u32 maxindex;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> > + else
> > + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> > +
> > + if (code->pad || index >= maxindex)
> > + return -EINVAL;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + code->code = xsdirxss_10bpc_mbus_fmts[index];
> > + else
> > + code->code = xsdirxss_12bpc_mbus_fmts[index];
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -EINVAL incase of invalid index and pad or zero on success
> > + */
> > +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_enum_dv_timings *timings)
> > +{
> > + if (timings->index >= ARRAY_SIZE(fmt_cap))
> > + return -EINVAL;
> > +
> > + if (timings->pad != 0)
> > + return -EINVAL;
> > +
> > + timings->timings = fmt_cap[timings->index];
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_query_dv_timings: Query for the current DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding
> timing
> > + * entry is not found or zero on success.
> > + */
> > +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_dv_timings *timings)
> > +{
> > + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> > + unsigned int i;
> > +
> > + if (!state->vidlocked)
> > + return -ENOLCK;
> > +
> > + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> > + if (state->format.width == xsdirxss_dv_timings[i].width &&
> > + state->format.height == xsdirxss_dv_timings[i].height &&
> > + state->frame_interval.denominator ==
> > + (xsdirxss_dv_timings[i].fps * 1000)) {
> > + *timings = xsdirxss_dv_timings[i].format;
> > + return 0;
> > + }
> > + }
> > +
> > + return -ERANGE;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * 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 const 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 const 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,
> > + }
>
> All these controls need to be documented in the header. Some of these controls
> may turn out to be controls that can be standardized for SDI receivers, others
> might be more vendor or driver specific.
>

I have documented these in the header. But it seems insufficient. Let me add more info in it.
Most of this is IP specific. Probably Mode detected and Active streams can be standardized.

> > +};
> > +
> > +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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > + .g_frame_interval = xsdirxss_g_frame_interval,
> > + .s_stream = xsdirxss_s_stream,
> > + .g_input_status = xsdirxss_g_input_status,
> > + .query_dv_timings = xsdirxss_query_dv_timings,
>
> This is missing g/s_dv_timings. You need to keep track of the current
> timings in the driver state: typically userspace will query timings and
> if that returns valid timings it will set them (s_dv_timings). With
> g_dv_timings the last set timings are returned.
>

Thanks for sharing this information. I didn't get the sequence from the current documentation.
Let me know if there is a standard application that I may refer to for this.

> If the timings change then the driver stops streaming and reports the
> SOURCE_CHANGE event, at which point userspace will query the new timings.
>

The SOURCE_CHANGE event will be generated only after stopping the streaming to the downstream by
disabling the bridges. The core will always be enabled to detect any other incoming stream.
Once a new incoming stream is detected for video_lock_window amount of time, a video lock interrupt occurs.

> The reported mediabus format resolution only changes when s_dv_timings
> is called: it's updated with the new width/height info.
>

I will create another v4l2_mbus_framefmt member in the state structure which will be updated with the detected formats in the s_dv_timings.
Then application may call get/set_fmt().

> In other words: the timings reported by g_dv_timings are controlled by
> userspace, the timings reported by query_dv_timings reflect the actual
> timings received on the SDI bus.
>

Thanks for this detailed explanation. ????

> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > + .init_cfg = xsdirxss_init_cfg,
> > + .get_fmt = xsdirxss_get_format,
> > + .set_fmt = xsdirxss_set_format,
> > + .enum_mbus_code = xsdirxss_enum_mbus_code,
> > + .enum_dv_timings = xsdirxss_enum_dv_timings,
>
> This is missing dv_timings_cap.
>

I will add this in the next version.

> > +};
> > +
> > +static const struct v4l2_subdev_ops xsdirxss_ops = {
> > + .core = &xsdirxss_core_ops,
> > + .video = &xsdirxss_video_ops,
> > + .pad = &xsdirxss_pad_ops
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > +{
> > + struct device_node *node = xsdirxss->core.dev->of_node;
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > + struct fwnode_handle *ep, *rep;
> > + int ret;
> > + const char *sdi_std;
> > +
> > + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > + dev_dbg(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(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(dev, "Invalid Line Rate\n");
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > + core->mode);
> > +
> > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to get xlnx,bpp\n");
> > + return ret;
> > + }
> > +
> > + if (core->bpc != 10 && core->bpc != 12) {
> > + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> > + core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > +
> FWNODE_GRAPH_ENDPOINT_NEXT);
> > + if (!ep) {
> > + dev_err(dev, "no source port found");
> > + ret = -EINVAL;
> > + goto dt_parse_done;
> > + }
> > +
> > + rep = fwnode_graph_get_remote_endpoint(ep);
> > + if (!rep) {
> > + dev_err(dev, "no remote sink endpoint found");
> > + ret = -EINVAL;
> > + }
> > +
> > + fwnode_handle_put(rep);
> > +dt_parse_done:
> > + fwnode_handle_put(ep);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_probe(struct platform_device *pdev)
> > +{
> > + struct v4l2_subdev *subdev;
> > + struct xsdirxss_state *xsdirxss;
> > + struct xsdirxss_core *core;
> > + struct device *dev;
> > + 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;
> > + dev = core->dev;
> > +
> > + /* Register interrupt handler */
> > + core->irq = platform_get_irq(pdev, 0);
> > + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> > + xsdirxss_irq_handler, IRQF_ONESHOT,
> > + dev_name(dev), xsdirxss);
> > + if (ret) {
> > + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> > + core->clks = devm_kcalloc(dev, core->num_clks,
> > + sizeof(*core->clks), GFP_KERNEL);
> > + if (!core->clks)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < core->num_clks; i++)
> > + core->clks[i].id = xsdirxss_clks[i];
> > +
> > + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = xsdirxss_parse_of(xsdirxss);
> > + if (ret < 0)
> > + goto clk_err;
> > +
> > + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(core->iomem)) {
> > + ret = PTR_ERR(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->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +
> > + /* Initialize the default format */
> > + if (core->bpc == 10)
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY12_1X24;
> > + xsdirxss->default_format.field = V4L2_FIELD_NONE;
> > + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
>
> This shouldn't be DEFAULT. Can you explain a bit how colorspace (or
> colorimetry
> in general) is determined for SDI?
>

That is correct. As mentioned earlier the colorspace in SDI is determined by bit 5 and 4 of byte 3 of the 4 byte ST 352 payload as per SMPTE 2081-10:2018

> > + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> > + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> > +
> > + xsdirxss->format = xsdirxss->default_format;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + subdev = &xsdirxss->subdev;
> > + v4l2_subdev_init(subdev, &xsdirxss_ops);
> > +
> > + subdev->dev = &pdev->dev;
> > + strscpy(subdev->name, dev_name(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->pad);
> > + if (ret < 0)
> > + goto error;
> > +
> > + /* Initialise and register the controls */
> > + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > +
> > + if (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(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(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > +
> > + if (core->include_edh) {
> > + for (i = 0; i < num_edh_ctrls; i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_edh_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > + }
> > +
> > + if (xsdirxss->ctrl_handler.error) {
> > + dev_err(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(dev, "failed to set controls\n");
> > + goto error;
> > + }
> > +
> > + platform_set_drvdata(pdev, xsdirxss);
> > +
> > + ret = v4l2_async_register_subdev(subdev);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to register subdev\n");
> > + goto error;
> > + }
> > +
> > + xsdirxss->streaming = false;
> > +
> > + xsdirx_core_enable(core);
> > +
> > + dev_info(dev, "probe success\n");
> > +
> > + return 0;
> > +error:
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +clk_err:
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > + v4l2_async_unregister_subdev(subdev);
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > +
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_core_disable(core);
> > + xsdirx_streamflow_control(core, false);
> > +
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> > + { }
> > +};
> > +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 <[email protected]>");
> > +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 000000000000..6f2a093968d9
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,179 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode, event, custom timings
> > + * and flag definitions.
> > + *
> > + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * Events
> > + *
> > + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
> underflowed
> > + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
> overflowed
> > + */
> > +#define V4L2_EVENT_XILINX_SDIRX_CLASS
> (V4L2_EVENT_PRIVATE_START | 0x200)
> > +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> > +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> > +
> > +/*
> > + * 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
> > +
> > +/* Xilinx DV timings not in mainline yet */
> > +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
>
> Why not add these to v4l2-dv-timings.h? I do need to know on which standard
> they are based.
>
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index b6441fe705c5..e9de65e82642 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
>
> Why is this in a separate header? It seems to me that it makes more sense to
> have
> a single public header for this driver.
>
> > @@ -71,4 +71,71 @@
> > /* Noise level */
> > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> (V4L2_CID_XILINX_TPG + 17)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* The base for the sdi rx driver controls.
> > + * We reserve 32 controls for this driver.
> > + *
> > + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> > + * EDH is enabled.
> > + * The controls which can be set should only be set before enabling
> > + * streaming. The controls which can be got should be called while
> > + * streaming to get correct values.
> > + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
> dv timing
> > + * returns a valid timing.
> > + */
> > +
> > +#define V4L2_CID_XILINX_SDIRX
> (V4L2_CID_XILINX_BASE + 0x20)
> > +
> > +/* Framer Control to enable or disable the framer */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER
> (V4L2_CID_XILINX_SDIRX + 1)
> > +/*
> > + * Video Lock Window Control to set the video lock window value
> > + * This is the amount of time the mode and transport stream need
> > + * to be locked before a video lock interrupt occurs.
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> (V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control to enable EDH error count */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> (V4L2_CID_XILINX_SDIRX + 3)
> > +/*
> > + * Mode search Control to pass the bit mask of modes to detect.
> > + *
> > + * bit 0 set to detect SD mode,
> > + * bit 1 set to detect HD mode,
> > + * bit 2 set to detect 3G (3GA & 3GB) mode,
> > + * bit 3 set to detect 6G mode,
> > + * bit 4 set to detect 12G integer frame rate mode,
> > + * bit 5 set to detect 12G fractional frame rate mode,
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> (V4L2_CID_XILINX_SDIRX + 4)
> > +/*
> > + * Get Detected Mode control
> > + *
> > + * Control Value - Mode detected
> > + * 0 - SD
> > + * 1 - HD
> > + * 2 - 3G (3GA & 3GB)
> > + * 3 - 6G
> > + * 4 - 12G integer frame rate
> > + * 5 - 12G fractional frame rate
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
> (V4L2_CID_XILINX_SDIRX + 5)
> > +/* Get number of CRC errors status control */
> > +#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 whether it is interlaced or not */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> (V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get number of Active Streams */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> (V4L2_CID_XILINX_SDIRX + 10)
> > +/*
> > + * Get if the detected mode is 3GB.
> > + * Can be used to distinguished between 3GA and 3GB
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB
> (V4L2_CID_XILINX_SDIRX + 11)
> > +
> > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> >
>
> Regards,
>
> Hans

2020-06-01 14:49:20

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Please ignore this email ..

> -----Original Message-----
> From: Vishal Sagar
> Sent: Monday, June 1, 2020 8:12 PM
> To: Hans Verkuil <[email protected]>; Hyun Kwon <[email protected]>;
> [email protected]; [email protected];
> [email protected]; [email protected]; Michal Simek
> <[email protected]>; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]; Dinesh Kumar
> <[email protected]>; Sandip Kothari <[email protected]>; Joe Perches
> <[email protected]>
> Subject: RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
>
> Hi Hans,
>
> Thanks for reviewing!
>
> > -----Original Message-----
> > From: Hans Verkuil <[email protected]>
> > Sent: Wednesday, May 6, 2020 3:25 PM
> > To: Vishal Sagar <[email protected]>; Hyun Kwon <[email protected]>;
> > [email protected]; [email protected];
> > [email protected]; [email protected]; Michal Simek
> > <[email protected]>; [email protected];
> > [email protected]; [email protected]; linux-arm-
> > [email protected]; [email protected]; Dinesh Kumar
> > <[email protected]>; Sandip Kothari <[email protected]>; Joe Perches
> > <[email protected]>
> > Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx
> Subsystem
> > driver
> >
> > Hi Vishal,
> >
> > Thank you for this patch.
> >
> > I have some comments below:
> >
> > On 29/04/2020 16:17, 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 10/12 bpc YUV 422 media bus format currently. 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 IP configuration.
> > >
> > > Signed-off-by: Vishal Sagar <[email protected]>
> > > ---
> > > v2
> > > - Added DV timing support based on Hans Verkuilś feedback
> > > - More documentation to custom v4l controls and events
> > > - Fixed Hyunś comments
> > > - Added macro for masking and shifting as per Joe Perches comments
> > > - Updated to latest as per Xilinx github repo driver like
> > > adding new DV timings not in mainline yet uptill 03/21/20
> > >
> > > drivers/media/platform/xilinx/Kconfig | 11 +
> > > drivers/media/platform/xilinx/Makefile | 1 +
> > > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > > 5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
> > > --- a/drivers/media/platform/xilinx/Kconfig
> > > +++ b/drivers/media/platform/xilinx/Kconfig
> > > @@ -12,6 +12,17 @@ config VIDEO_XILINX
> > >
> > > if VIDEO_XILINX
> > >
> > > +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 4cdc0b1ec7a5..6c375f03f587 100644
> > > --- a/drivers/media/platform/xilinx/Makefile
> > > +++ b/drivers/media/platform/xilinx/Makefile
> > > @@ -2,6 +2,7 @@
> > >
> > > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> > >
> > > +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> > > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
> > > --- /dev/null
> > > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > > @@ -0,0 +1,2162 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Driver for Xilinx SDI Rx Subsystem
> > > + *
> > > + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> > > + *
> > > + * Contacts: Vishal Sagar <[email protected]>
> > > + *
> > > + */
> > > +
> > > +#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-dv-timings.h>
> > > +#include <linux/v4l2-subdev.h>
> > > +#include <linux/xilinx-sdirxss.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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> > > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK
> GENMASK(12,
> > 10)
> > > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> > > +
> > > +#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_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)
> > > +
> > > +#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_BYTE1_MASK 0xFF
> > > +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> > > +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> > > +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> > > +#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
> > > +#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_OFFSET 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_YUV444 0x1
> > > +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> > > +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> > > +
> > > +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> > > +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> > > +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> > > +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> > > +
> > > +#define CLK_INT 148500000UL
> > > +
> > > +#define rshift_and_mask(val, type) \
> > > + (((val) >> type##_OFFSET) & type##_MASK)
> > > +
> > > +#define mask_and_rshift(val, type) \
> > > + (((val) & type##_MASK) >> type##_OFFSET)
> > > +
> > > +/**
> > > + * 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
> > > + * @clks: array of clocks
> > > + * @num_clks: number of clocks
> > > + * @bpc: Bits per component, can be 10 or 12
> > > + */
> > > +struct xsdirxss_core {
> > > + struct device *dev;
> > > + void __iomem *iomem;
> > > + int irq;
> > > + bool include_edh;
> > > + int mode;
> > > + struct clk_bulk_data *clks;
> > > + int num_clks;
> > > + u32 bpc;
> > > +};
> > > +
> > > +/**
> > > + * 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
> > > + * @format: Active V4L2 format on source pad
> > > + * @default_format: default V4L2 media bus format
> > > + * @frame_interval: Captures the frame rate
> > > + * @pad: source media pad
> > > + * @vidlockwin: Video lock window value set by control
> > > + * @edhmask: EDH mask set by control
> > > + * @searchmask: Search mask set by control
> > > + * @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.
> > > + * @framer_enable: Flag for framer enabled or not set by control
> > > + *
> > > + * 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 format;
> > > + struct v4l2_mbus_framefmt default_format;
> > > + struct v4l2_fract frame_interval;
> > > + struct media_pad pad;
> > > + u32 vidlockwin;
> > > + u32 edhmask;
> > > + u16 searchmask;
> > > + bool streaming;
> > > + bool vidlocked;
> > > + bool ts_is_interlaced;
> > > + bool framer_enable;
> > > +};
> > > +
> > > +/* List of clocks required by UHD-SDI Rx subsystem */
> > > +static const char * const xsdirxss_clks[] = {
> > > + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> > > +};
> > > +
> > > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> > > +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> > > + MEDIA_BUS_FMT_UYVY10_1X20,
> > > +};
> > > +
> > > +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> > > + MEDIA_BUS_FMT_UYVY12_1X24,
> > > +};
> > > +
> > > +static const struct v4l2_dv_timings fmt_cap[] = {
> > > + V4L2_DV_BT_SDI_720X487I60,
> > > + V4L2_DV_BT_CEA_720X576I50,
> > > + V4L2_DV_BT_CEA_1280X720P24,
> > > + V4L2_DV_BT_CEA_1280X720P25,
> > > + V4L2_DV_BT_CEA_1280X720P30,
> > > + V4L2_DV_BT_CEA_1280X720P50,
> > > + V4L2_DV_BT_CEA_1280X720P60,
> > > + V4L2_DV_BT_CEA_1920X1080P24,
> > > + V4L2_DV_BT_CEA_1920X1080P30,
> > > + V4L2_DV_BT_CEA_1920X1080I50,
> > > + V4L2_DV_BT_CEA_1920X1080I60,
> > > + V4L2_DV_BT_CEA_1920X1080P50,
> > > + V4L2_DV_BT_CEA_1920X1080P60,
> > > + V4L2_DV_BT_CEA_3840X2160P24,
> > > + V4L2_DV_BT_CEA_3840X2160P30,
> > > + V4L2_DV_BT_CEA_3840X2160P50,
> > > + V4L2_DV_BT_CEA_3840X2160P60,
> > > + V4L2_DV_BT_CEA_4096X2160P24,
> > > + V4L2_DV_BT_CEA_4096X2160P25,
> > > + V4L2_DV_BT_CEA_4096X2160P30,
> > > + V4L2_DV_BT_CEA_4096X2160P50,
> > > + V4L2_DV_BT_CEA_4096X2160P60,
> > > +
> > > + XLNX_V4L2_DV_BT_2048X1080P24,
> > > + XLNX_V4L2_DV_BT_2048X1080P25,
> > > + XLNX_V4L2_DV_BT_2048X1080P30,
> > > + XLNX_V4L2_DV_BT_2048X1080I48,
> > > + XLNX_V4L2_DV_BT_2048X1080I50,
> > > + XLNX_V4L2_DV_BT_2048X1080I60,
> > > + XLNX_V4L2_DV_BT_2048X1080P48,
> > > + XLNX_V4L2_DV_BT_2048X1080P50,
> > > + XLNX_V4L2_DV_BT_2048X1080P60,
> > > + XLNX_V4L2_DV_BT_1920X1080P48,
> > > + XLNX_V4L2_DV_BT_1920X1080I48,
> > > + XLNX_V4L2_DV_BT_3840X2160P48,
> > > + XLNX_V4L2_DV_BT_4096X2160P48,
> > > +};
> > > +
> > > +struct xsdirxss_dv_map {
> > > + u32 width;
> > > + u32 height;
> > > + u32 fps;
> > > + struct v4l2_dv_timings format;
> > > +};
> > > +
> > > +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> > > + /* SD - 720x487i60 */
> > > + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> > > + /* SD - 720x576i50 */
> > > + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> > > + /* HD - 1280x720p23.98 */
> > > + /* HD - 1280x720p24 */
> > > + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> > > + /* HD - 1280x720p25 */
> > > + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> > > + /* HD - 1280x720p29.97 */
> > > + /* HD - 1280x720p30 */
> > > + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> > > + /* HD - 1280x720p50 */
> > > + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> > > + /* HD - 1280x720p59.94 */
> > > + /* HD - 1280x720p60 */
> > > + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> > > + /* HD - 1920x1080p23.98 */
> > > + /* HD - 1920x1080p24 */
> > > + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> > > + /* HD - 1920x1080p25 */
> > > + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> > > + /* HD - 1920x1080p29.97 */
> > > + /* HD - 1920x1080p30 */
> > > + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> > > +
> > > + /* HD - 2048x1080p23.98 */
> > > + /* HD - 2048x1080p24 */
> > > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> > > + /* HD - 2048x1080p25 */
> > > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> > > + /* HD - 2048x1080p29.97 */
> > > + /* HD - 2048x1080p30 */
> > > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> > > + /* HD - 1920x1080i47.95 */
> > > + /* HD - 1920x1080i48 */
> > > + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> > > +
> > > + /* HD - 1920x1080i50 */
> > > + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> > > + /* HD - 1920x1080i59.94 */
> > > + /* HD - 1920x1080i60 */
> > > + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> > > +
> > > + /* HD - 2048x1080i47.95 */
> > > + /* HD - 2048x1080i48 */
> > > + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> > > + /* HD - 2048x1080i50 */
> > > + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> > > + /* HD - 2048x1080i59.94 */
> > > + /* HD - 2048x1080i60 */
> > > + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> > > + /* 3G - 1920x1080p47.95 */
> > > + /* 3G - 1920x1080p48 */
> > > + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> > > +
> > > + /* 3G - 1920x1080p50 148.5 */
> > > + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> > > + /* 3G - 1920x1080p59.94 148.5/1.001 */
> > > + /* 3G - 1920x1080p60 148.5 */
> > > + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> > > +
> > > + /* 3G - 2048x1080p47.95 */
> > > + /* 3G - 2048x1080p48 */
> > > + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> > > + /* 3G - 2048x1080p50 */
> > > + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> > > + /* 3G - 2048x1080p59.94 */
> > > + /* 3G - 2048x1080p60 */
> > > + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> > > +
> > > + /* 6G - 3840X2160p23.98 */
> > > + /* 6G - 3840X2160p24 */
> > > + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> > > + /* 6G - 3840X2160p25 */
> > > + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> > > + /* 6G - 3840X2160p29.97 */
> > > + /* 6G - 3840X2160p30 */
> > > + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> > > + /* 6G - 4096X2160p23.98 */
> > > + /* 6G - 4096X2160p24 */
> > > + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> > > + /* 6G - 4096X2160p25 */
> > > + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> > > + /* 6G - 4096X2160p29.97 */
> > > + /* 6G - 4096X2160p30 */
> > > + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> > > + /* 12G - 3840X2160p47.95 */
> > > + /* 12G - 3840X2160p48 */
> > > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> > > +
> > > + /* 12G - 3840X2160p50 */
> > > + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> > > + /* 12G - 3840X2160p59.94 */
> > > + /* 12G - 3840X2160p60 */
> > > + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> > > +
> > > + /* 12G - 4096X2160p47.95 */
> > > + /* 12G - 4096X2160p48 */
> > > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> > > +
> > > + /* 12G - 4096X2160p50 */
> > > + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> > > + /* 12G - 4096X2160p59.94 */
> > > + /* 12G - 4096X2160p60 */
> > > + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> > > +};
> > > +
> > > +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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
> > > +{
> > > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> > XSDIRX_RST_CTRL_SS_EN_MASK);
> > > +}
> > > +
> > > +static inline 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 val;
> > > + struct device *dev = core->dev;
> > > +
> > > + mask &= XSDIRX_DETECT_ALL_MODES;
> > > + if (!mask) {
> > > + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + dev_dbg(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(dev, "Detect multiple modes\n");
> > > +
> > > + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > > + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> > > + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > > +
> > > + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > > + } else {
> > > + /* Fixed Mode */
> > > + u32 forced_mode_mask;
> > > +
> > > + dev_dbg(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;
> > > + default:
> > > + forced_mode_mask = 0;
> > > + }
> > > + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> > > + forced_mode_mask);
> > > + val |= forced_mode_mask <<
> > XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > > + }
> > > +
> > > + dev_dbg(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 inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > > +{
> > > + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > > +}
> > > +
> > > +static inline 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 inline 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)
> > > +{
> > > + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> > > +
> > > + /*
> > > + * TODO - Enable YUV444/RBG format in the bridge based
> > > + * on BYTE3 color format.
> > > + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> > > + */
> > > + if (enable)
> > > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > > + else
> > > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, 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 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;
> > > + }
> > > +}
> > > +
> > > +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> > > +{
> > > + struct clk *gtclk;
> > > + unsigned long clkrate;
> > > + int ret, is_frac;
> > > + struct xsdirxss_core *core = &state->core;
> > > + u32 mode;
> > > + static int prev_is_frac = -1;
> > > +
> > > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > > +
> > > + /*
> > > + * TODO: For now, don't change the clock rate for any mode except
> > 12G.
> > > + * In future, configure gt clock for all modes and enable clock only
> > > + * when needed (stream on/off).
> > > + */
> > > + if (mode != XSDIRX_MODE_12GI_MASK && mode !=
> > XSDIRX_MODE_12GF_MASK)
> > > + return;
> > > +
> > > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > > +
> > > + if (prev_is_frac == is_frac)
> > > + return;
> > > +
> > > + xsdirx_core_disable(core);
> > > + xsdirx_globalintr(core, false);
> > > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > > +
> > > + /* get sdi_rx_clk */
> > > + gtclk = core->clks[1].clk;
> > > + clkrate = clk_get_rate(gtclk);
> > > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > > +
> > > + /* calcualte clkrate */
> > > + if (!is_frac)
> > > + clkrate = CLK_INT;
> > > + else
> > > + clkrate = (CLK_INT * 1000) / 1001;
> > > +
> > > + ret = clk_set_rate(gtclk, clkrate);
> > > + if (ret)
> > > + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> > > +
> > > + clkrate = clk_get_rate(gtclk);
> > > +
> > > + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> > > + clkrate, is_frac);
> > > +
> > > + xsdirx_framer(core, state->framer_enable);
> > > + xsdirx_setedherrcnttrigger(core, state->edhmask);
> > > + xsdirx_setvidlockwindow(core, state->vidlockwin);
> > > + xsdirx_set_modedetect(core, state->searchmask);
> > > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > > + xsdirx_globalintr(core, true);
> > > + xsdirx_core_enable(core);
> > > +
> > > + prev_is_frac = is_frac;
> > > +}
> > > +
> > > +/**
> > > + * 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;
> > > + struct device *dev = core->dev;
> > > + 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->format;
> > > + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> > > +
> > > + 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> > > + active_luma = mask_and_rshift(payload,
> > > +
> > XST352_BYTE3_ACT_LUMA_COUNT);
> > > + pic_type = mask_and_rshift(payload,
> > XST352_BYTE2_PIC_TYPE);
> > > + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> > > + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> > > + sampling = mask_and_rshift(payload,
> > XST352_BYTE3_COLOR_FORMAT);
> > > + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> > > + } else {
> > > + dev_dbg(dev, "No ST352 payload available : Mode = %d\n",
> > mode);
> > > + framerate = mask_and_rshift(val,
> > XSDIRX_TS_DET_STAT_RATE);
> > > + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> > > + }
> > > +
> > > + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> > > + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> > > + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc =
> > %d\n",
> > > + bpc, core->bpc);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> > > + state->ts_is_interlaced = tscan ? false : true;
> > > +
> > > + dev_dbg(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(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(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(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(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(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(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;
> > > + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> > > + format->height = 1080;
> > > + if (active_luma)
> > > + format->width = 2048;
> > > + else
> > > + format->width = 1920;
> > > + break;
> > > + default:
> > > + dev_dbg(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(dev, "Unknown 12G Mode SMPTE
> > standard\n");
> > > + return -EINVAL;
> > > + }
> > > + break;
> > > + default:
> > > + dev_err(dev, "Invalid Mode\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + if (valid) {
> > > + if (pic_type)
> > > + format->field = V4L2_FIELD_NONE;
> > > + else
> > > + format->field = V4L2_FIELD_ALTERNATE;
> > > +
> > > + if (format->height == 1080 && pic_type && !tscan)
> > > + format->field = V4L2_FIELD_ALTERNATE;
> > > +
> > > + /*
> > > + * In 3GB DL pSF mode the video is similar to interlaced.
> > > + * So though it is a progressive video, its transport is
> > > + * interlaced and is sent as two width x (height/2) buffers.
> > > + */
> > > + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> > > + if (state->ts_is_interlaced)
> > > + format->field = V4L2_FIELD_ALTERNATE;
> > > + else
> > > + format->field = V4L2_FIELD_NONE;
> > > + }
> > > + }
> > > +
> > > + if (format->field == V4L2_FIELD_ALTERNATE)
> > > + format->height = format->height / 2;
> > > +
> > > + switch (sampling) {
> > > + case XST352_BYTE3_COLOR_FORMAT_422:
> > > + if (core->bpc == 10)
> > > + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > > + else
> > > + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> > > + break;
> > > + case XST352_BYTE3_COLOR_FORMAT_420:
> > > + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> > > + case XST352_BYTE3_COLOR_FORMAT_GBR:
> > > + format->code = 0;
> > > + dev_dbg(dev, "No corresponding media bus formats\n");
> > > + break;
> > > + default:
> > > + dev_err(dev, "Unsupported color format : %d\n", sampling);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + xsdirxss_get_framerate(&state->frame_interval, framerate);
> > > +
> > > + dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
> > > + state->frame_interval.numerator,
> > > + state->frame_interval.denominator);
> > > + dev_dbg(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 writing 1 to corresponding bit.
> > > + *
> > > + * 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;
> > > + struct device *dev = core->dev;
> > > + u32 status;
> > > +
> > > + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > > + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> > > + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> > > +
> > > + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> > > + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > > + u32 val1, val2;
> > > +
> > > + dev_dbg(dev, "video lock/unlock interrupt\n");
> > > +
> > > + xsdirx_streamflow_control(core, false);
> > > + state->streaming = false;
> > > +
> > > + 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
> > > + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> > > +
> > > + if (!xsdirx_get_stream_properties(state)) {
> > > + state->vidlocked = true;
> > > + xsdirxss_set_gtclk(state);
> > > + } else {
> > > + dev_err(dev, "Unable to get stream
> > properties!\n");
> > > + state->vidlocked = false;
> > > + }
> > > + } else {
> > > + dev_dbg(dev, "video unlock interrupt\n");
> > > + state->vidlocked = false;
> > > + }
> > > +
> > > + 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);
> > > + }
> > > +
> > > + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > > + dev_dbg(dev, "Video in to AXI4 Stream core underflow
> > interrupt\n");
> > > +
> > > + memset(&state->event, 0, sizeof(state->event));
> > > + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> > > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > > + }
> > > +
> > > + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > > + dev_dbg(dev, "Video in to AXI4 Stream core overflow
> > interrupt\n");
> > > +
> > > + memset(&state->event, 0, sizeof(state->event));
> > > + state->event.type = V4L2_EVENT_XILINX_SDIRX_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_XILINX_SDIRX_UNDERFLOW:
> > > + case V4L2_EVENT_XILINX_SDIRX_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;
> >
> > This should call v4l2_ctrl_subscribe_event instead, otherwise control events
> > wouldn't be supported.
> >
>
> Ok I will add v4l2_ctrl_subscribe_event() here.
>
> > > + }
> > > + 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;
> > > + struct device *dev = core->dev;
> > > +
> > > + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > > + ctrl->id, ctrl->val);
> > > +
> > > + if (xsdirxss->streaming) {
> > > + dev_err(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);
> > > + xsdirxss->framer_enable = ctrl->val;
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > > + xsdirx_setvidlockwindow(core, ctrl->val);
> > > + xsdirxss->vidlockwin = ctrl->val;
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > > + xsdirx_setedherrcnttrigger(core, ctrl->val);
> > > + xsdirxss->edhmask = ctrl->val;
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > > + if (ctrl->val) {
> > > + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > > + dev_dbg(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(dev, "Upto 6G supported\n");
> > > + ctrl->val &=
> > ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > > +
> > BIT(XSDIRX_MODE_12GF_OFFSET));
> > > + }
> > > +
> > > + ret = xsdirx_set_modedetect(core, ctrl->val);
> > > + if (!ret)
> > > + xsdirxss->searchmask = ctrl->val;
> > > + } else {
> > > + dev_err(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;
> > > + struct device *dev = core->dev;
> > > +
> > > + switch (ctrl->id) {
> > > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(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(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(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(dev, "Can't get values when video not
> > locked!\n");
> > > + return -EINVAL;
> > > + }
> > > + ctrl->val = xsdirxss->ts_is_interlaced;
> >
> > This control makes no sense: the v4l2_dv_timings struct will already tell you
> > if it is an interlaced format or not. Same for v4l2_mbus_framefmt.
> >
>
> The SDI has a concept of supporting progressive, interlaced (both as we know
> normally) and a progressive segmented frames(psf).
> The progressive segmented frames have their video content in progressive
> format but the transport stream is interlaced.
> This is distinguished using the bit 6 and 7 of Byte 2 in the 4 byte ST352 payload.
> Refer to sec 5.3 in SMPTE ST 352:2010.
>
> This control can be used by the application to distinguish normal interlaced and
> progressive segmented frames.
>
> > > + break;
> > > + case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(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(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(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > > + return -EINVAL;
> > > + }
> > > + dev_dbg(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 i;
> > > +
> > > + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > > + for (i = 0; i < 0x28; i++) {
> > > + u32 data;
> > > +
> > > + 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;
> >
> > I suggest calling v4l2_ctrl_subdev_log_status() as well to log all controls.
> >
>
> Ok. I will add this in the next version.
>
> > > +}
> > > +
> > > +/**
> > > + * 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;
> > > + struct device *dev = core->dev;
> > > +
> > > + if (enable) {
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_dbg(dev, "Video is not locked\n");
> > > + return -EINVAL;
> > > + }
> > > + if (xsdirxss->streaming) {
> > > + dev_dbg(dev, "Already streaming\n");
> > > + return -EINVAL;
> > > + }
> > > +
> > > + xsdirx_streamflow_control(core, true);
> > > + xsdirxss->streaming = true;
> > > + dev_dbg(dev, "Streaming started\n");
> > > + } else {
> > > + if (!xsdirxss->streaming) {
> > > + dev_dbg(dev, "Stopped streaming already\n");
> > > + return 0;
> > > + }
> > > +
> > > + xsdirx_streamflow_control(core, false);
> > > + xsdirxss->streaming = false;
> > > + dev_dbg(dev, "Streaming stopped\n");
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_g_input_status - It is used to determine if the video signal
> > > + * is present / locked onto or not.
> > > + *
> > > + * @sd: V4L2 Sub device
> > > + * @status: status of signal locked
> > > + *
> > > + * This is used to determine if the video signal is present and locked onto
> > > + * by the SDI Rx core or not based on vidlocked flag.
> > > + *
> > > + * Return: zero on success
> > > + */
> > > +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > +
> > > + if (!xsdirxss->vidlocked)
> > > + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> > > + else
> > > + *status = 0;
> > > +
> > > + 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->format;
> > > + default:
> > > + return NULL;
> > > + }
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_init_cfg - Initialise the pad format config to default
> > > + * @sd: Pointer to V4L2 Sub device structure
> > > + * @cfg: Pointer to sub device pad information structure
> > > + *
> > > + * This function is used to initialize the pad format with the default
> > > + * values.
> > > + *
> > > + * Return: 0 on success
> > > + */
> > > +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> > > + struct v4l2_subdev_pad_config *cfg)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + struct v4l2_mbus_framefmt *format;
> > > +
> > > + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> > > + *format = xsdirxss->default_format;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * 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;
> >
> > It should fill in the width and height based on the current DV timings.
> > Ditto for the field (I assume that's fixed as well based on whether this
> > is interlaced or not). I'm not sure how colorspace information is handled
> > for SDI.
> >
>
> Yes. I will update this logic to update the width, height and field based on
> current DV timings.
> Please have a look at this in the next version.
> The current IP supports BT709 colorimetry.
> The colorimetry information of incoming stream is present in the ST 352
> payload Byte 3 (bit 5 and bit 4) as per SMPTE ST 2081-10:2018.
> B5:4 = 0 -> BT 709 as per SMPTE ST 274
>
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> > > + * @sd: pointer to v4l2 subdev structure
> > > + * @cfg: V4L2 subdev pad configuration
> > > + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> > > + *
> > > + * Return: -EINVAL or zero on success
> > > + */
> > > +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> > > + struct v4l2_subdev_pad_config *cfg,
> > > + struct v4l2_subdev_mbus_code_enum
> > *code)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > > + u32 index = code->index;
> > > + u32 maxindex;
> > > +
> > > + if (xsdirxss->core.bpc == 10)
> > > + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> > > + else
> > > + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> > > +
> > > + if (code->pad || index >= maxindex)
> > > + return -EINVAL;
> > > +
> > > + if (xsdirxss->core.bpc == 10)
> > > + code->code = xsdirxss_10bpc_mbus_fmts[index];
> > > + else
> > > + code->code = xsdirxss_12bpc_mbus_fmts[index];
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> > > + * @sd: pointer to v4l2 subdev structure
> > > + * @timings: DV timings structure to be returned.
> > > + *
> > > + * Return: -EINVAL incase of invalid index and pad or zero on success
> > > + */
> > > +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> > > + struct v4l2_enum_dv_timings *timings)
> > > +{
> > > + if (timings->index >= ARRAY_SIZE(fmt_cap))
> > > + return -EINVAL;
> > > +
> > > + if (timings->pad != 0)
> > > + return -EINVAL;
> > > +
> > > + timings->timings = fmt_cap[timings->index];
> > > + return 0;
> > > +}
> > > +
> > > +/**
> > > + * xsdirxss_query_dv_timings: Query for the current DV timings
> > > + * @sd: pointer to v4l2 subdev structure
> > > + * @timings: DV timings structure to be returned.
> > > + *
> > > + * Return: -ENOLCK when video is not locked, -ERANGE when
> corresponding
> > timing
> > > + * entry is not found or zero on success.
> > > + */
> > > +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> > > + struct v4l2_dv_timings *timings)
> > > +{
> > > + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> > > + unsigned int i;
> > > +
> > > + if (!state->vidlocked)
> > > + return -ENOLCK;
> > > +
> > > + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> > > + if (state->format.width == xsdirxss_dv_timings[i].width &&
> > > + state->format.height == xsdirxss_dv_timings[i].height &&
> > > + state->frame_interval.denominator ==
> > > + (xsdirxss_dv_timings[i].fps * 1000)) {
> > > + *timings = xsdirxss_dv_timings[i].format;
> > > + return 0;
> > > + }
> > > + }
> > > +
> > > + return -ERANGE;
> > > +}
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * 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 const 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 const 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,
> > > + }
> >
> > All these controls need to be documented in the header. Some of these
> controls
> > may turn out to be controls that can be standardized for SDI receivers, others
> > might be more vendor or driver specific.
> >
>
> I have documented these in the header. But it seems insufficient. Let me add
> more info in it.
> Most of this is IP specific. Probably Mode detected and Active streams can be
> standardized.
>
> > > +};
> > > +
> > > +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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > > + .g_frame_interval = xsdirxss_g_frame_interval,
> > > + .s_stream = xsdirxss_s_stream,
> > > + .g_input_status = xsdirxss_g_input_status,
> > > + .query_dv_timings = xsdirxss_query_dv_timings,
> >
> > This is missing g/s_dv_timings. You need to keep track of the current
> > timings in the driver state: typically userspace will query timings and
> > if that returns valid timings it will set them (s_dv_timings). With
> > g_dv_timings the last set timings are returned.
> >
>
> Thanks for sharing this information. I didn't get the sequence from the current
> documentation.
> Let me know if there is a standard application that I may refer to for this.
>
> > If the timings change then the driver stops streaming and reports the
> > SOURCE_CHANGE event, at which point userspace will query the new timings.
> >
>
> The SOURCE_CHANGE event will be generated only after stopping the
> streaming to the downstream by
> disabling the bridges. The core will always be enabled to detect any other
> incoming stream.
> Once a new incoming stream is detected for video_lock_window amount of
> time, a video lock interrupt occurs.
>
> > The reported mediabus format resolution only changes when s_dv_timings
> > is called: it's updated with the new width/height info.
> >
>
> I will create another v4l2_mbus_framefmt member in the state structure which
> will be updated with the detected formats in the s_dv_timings.
> Then application may call get/set_fmt().
>
> > In other words: the timings reported by g_dv_timings are controlled by
> > userspace, the timings reported by query_dv_timings reflect the actual
> > timings received on the SDI bus.
> >
>
> Thanks for this detailed explanation. ????
>
> > > +};
> > > +
> > > +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > > + .init_cfg = xsdirxss_init_cfg,
> > > + .get_fmt = xsdirxss_get_format,
> > > + .set_fmt = xsdirxss_set_format,
> > > + .enum_mbus_code = xsdirxss_enum_mbus_code,
> > > + .enum_dv_timings = xsdirxss_enum_dv_timings,
> >
> > This is missing dv_timings_cap.
> >
>
> I will add this in the next version.
>
> > > +};
> > > +
> > > +static const struct v4l2_subdev_ops xsdirxss_ops = {
> > > + .core = &xsdirxss_core_ops,
> > > + .video = &xsdirxss_video_ops,
> > > + .pad = &xsdirxss_pad_ops
> > > +};
> > > +
> > > +/* -----------------------------------------------------------------------------
> > > + * Platform Device Driver
> > > + */
> > > +
> > > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > > +{
> > > + struct device_node *node = xsdirxss->core.dev->of_node;
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + struct device *dev = core->dev;
> > > + struct fwnode_handle *ep, *rep;
> > > + int ret;
> > > + const char *sdi_std;
> > > +
> > > + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > > + dev_dbg(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(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(dev, "Invalid Line Rate\n");
> > > + return -EINVAL;
> > > + }
> > > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > > + core->mode);
> > > +
> > > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> > > + if (ret < 0) {
> > > + dev_err(dev, "failed to get xlnx,bpp\n");
> > > + return ret;
> > > + }
> > > +
> > > + if (core->bpc != 10 && core->bpc != 12) {
> > > + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> > > + core->bpc);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > > +
> > FWNODE_GRAPH_ENDPOINT_NEXT);
> > > + if (!ep) {
> > > + dev_err(dev, "no source port found");
> > > + ret = -EINVAL;
> > > + goto dt_parse_done;
> > > + }
> > > +
> > > + rep = fwnode_graph_get_remote_endpoint(ep);
> > > + if (!rep) {
> > > + dev_err(dev, "no remote sink endpoint found");
> > > + ret = -EINVAL;
> > > + }
> > > +
> > > + fwnode_handle_put(rep);
> > > +dt_parse_done:
> > > + fwnode_handle_put(ep);
> > > + return ret;
> > > +}
> > > +
> > > +static int xsdirxss_probe(struct platform_device *pdev)
> > > +{
> > > + struct v4l2_subdev *subdev;
> > > + struct xsdirxss_state *xsdirxss;
> > > + struct xsdirxss_core *core;
> > > + struct device *dev;
> > > + 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;
> > > + dev = core->dev;
> > > +
> > > + /* Register interrupt handler */
> > > + core->irq = platform_get_irq(pdev, 0);
> > > + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> > > + xsdirxss_irq_handler, IRQF_ONESHOT,
> > > + dev_name(dev), xsdirxss);
> > > + if (ret) {
> > > + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> > > + ret);
> > > + return ret;
> > > + }
> > > +
> > > + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> > > + core->clks = devm_kcalloc(dev, core->num_clks,
> > > + sizeof(*core->clks), GFP_KERNEL);
> > > + if (!core->clks)
> > > + return -ENOMEM;
> > > +
> > > + for (i = 0; i < core->num_clks; i++)
> > > + core->clks[i].id = xsdirxss_clks[i];
> > > +
> > > + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + ret = xsdirxss_parse_of(xsdirxss);
> > > + if (ret < 0)
> > > + goto clk_err;
> > > +
> > > + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> > > + if (IS_ERR(core->iomem)) {
> > > + ret = PTR_ERR(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->pad.flags = MEDIA_PAD_FL_SOURCE;
> > > +
> > > + /* Initialize the default format */
> > > + if (core->bpc == 10)
> > > + xsdirxss->default_format.code =
> > MEDIA_BUS_FMT_UYVY10_1X20;
> > > + else
> > > + xsdirxss->default_format.code =
> > MEDIA_BUS_FMT_UYVY12_1X24;
> > > + xsdirxss->default_format.field = V4L2_FIELD_NONE;
> > > + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
> >
> > This shouldn't be DEFAULT. Can you explain a bit how colorspace (or
> > colorimetry
> > in general) is determined for SDI?
> >
>
> That is correct. As mentioned earlier the colorspace in SDI is determined by bit
> 5 and 4 of byte 3 of the 4 byte ST 352 payload as per SMPTE 2081-10:2018
>
> > > + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> > > + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> > > +
> > > + xsdirxss->format = xsdirxss->default_format;
> > > +
> > > + /* Initialize V4L2 subdevice and media entity */
> > > + subdev = &xsdirxss->subdev;
> > > + v4l2_subdev_init(subdev, &xsdirxss_ops);
> > > +
> > > + subdev->dev = &pdev->dev;
> > > + strscpy(subdev->name, dev_name(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->pad);
> > > + if (ret < 0)
> > > + goto error;
> > > +
> > > + /* Initialise and register the controls */
> > > + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > > +
> > > + if (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(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(dev, "Failed to add %s ctrl\n",
> > > + xsdirxss_ctrls[i].name);
> > > + goto error;
> > > + }
> > > + }
> > > +
> > > + if (core->include_edh) {
> > > + for (i = 0; i < num_edh_ctrls; i++) {
> > > + struct v4l2_ctrl *ctrl;
> > > +
> > > + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> > > + xsdirxss_edh_ctrls[i].name);
> > > + goto error;
> > > + }
> > > + }
> > > + }
> > > +
> > > + if (xsdirxss->ctrl_handler.error) {
> > > + dev_err(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(dev, "failed to set controls\n");
> > > + goto error;
> > > + }
> > > +
> > > + platform_set_drvdata(pdev, xsdirxss);
> > > +
> > > + ret = v4l2_async_register_subdev(subdev);
> > > + if (ret < 0) {
> > > + dev_err(dev, "failed to register subdev\n");
> > > + goto error;
> > > + }
> > > +
> > > + xsdirxss->streaming = false;
> > > +
> > > + xsdirx_core_enable(core);
> > > +
> > > + dev_info(dev, "probe success\n");
> > > +
> > > + return 0;
> > > +error:
> > > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > > + media_entity_cleanup(&subdev->entity);
> > > + xsdirx_globalintr(core, false);
> > > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > > +clk_err:
> > > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > > + return ret;
> > > +}
> > > +
> > > +static int xsdirxss_remove(struct platform_device *pdev)
> > > +{
> > > + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > > +
> > > + v4l2_async_unregister_subdev(subdev);
> > > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > > + media_entity_cleanup(&subdev->entity);
> > > +
> > > + xsdirx_globalintr(core, false);
> > > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > > + xsdirx_core_disable(core);
> > > + xsdirx_streamflow_control(core, false);
> > > +
> > > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > > + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> > > + { }
> > > +};
> > > +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 <[email protected]>");
> > > +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 000000000000..6f2a093968d9
> > > --- /dev/null
> > > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > > @@ -0,0 +1,179 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > > +/*
> > > + * Xilinx SDI Rx Subsystem mode, event, custom timings
> > > + * and flag definitions.
> > > + *
> > > + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> > > + *
> > > + * Contacts: Vishal Sagar <[email protected]>
> > > + */
> > > +
> > > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > > +#define __UAPI_XILINX_SDIRXSS_H__
> > > +
> > > +#include <linux/types.h>
> > > +#include <linux/v4l2-dv-timings.h>
> > > +#include <linux/videodev2.h>
> > > +
> > > +/*
> > > + * Events
> > > + *
> > > + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
> > underflowed
> > > + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
> > overflowed
> > > + */
> > > +#define V4L2_EVENT_XILINX_SDIRX_CLASS
> > (V4L2_EVENT_PRIVATE_START | 0x200)
> > > +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
> > (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> > > +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
> > (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> > > +
> > > +/*
> > > + * 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
> > > +
> > > +/* Xilinx DV timings not in mainline yet */
> > > +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> > > +
> > > +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> > > + .type = V4L2_DV_BT_656_1120, \
> > > + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> > > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > > + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> > > + V4L2_DV_BT_STD_SDI) \
> > > +}
> >
> > Why not add these to v4l2-dv-timings.h? I do need to know on which standard
> > they are based.
> >
> > > +
> > > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> > b/include/uapi/linux/xilinx-v4l2-controls.h
> > > index b6441fe705c5..e9de65e82642 100644
> > > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> >
> > Why is this in a separate header? It seems to me that it makes more sense to
> > have
> > a single public header for this driver.
> >
> > > @@ -71,4 +71,71 @@
> > > /* Noise level */
> > > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> > (V4L2_CID_XILINX_TPG + 17)
> > >
> > > +/*
> > > + * Xilinx SDI Rx Subsystem
> > > + */
> > > +
> > > +/* The base for the sdi rx driver controls.
> > > + * We reserve 32 controls for this driver.
> > > + *
> > > + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> > > + * EDH is enabled.
> > > + * The controls which can be set should only be set before enabling
> > > + * streaming. The controls which can be got should be called while
> > > + * streaming to get correct values.
> > > + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
> > dv timing
> > > + * returns a valid timing.
> > > + */
> > > +
> > > +#define V4L2_CID_XILINX_SDIRX
> > (V4L2_CID_XILINX_BASE + 0x20)
> > > +
> > > +/* Framer Control to enable or disable the framer */
> > > +#define V4L2_CID_XILINX_SDIRX_FRAMER
> > (V4L2_CID_XILINX_SDIRX + 1)
> > > +/*
> > > + * Video Lock Window Control to set the video lock window value
> > > + * This is the amount of time the mode and transport stream need
> > > + * to be locked before a video lock interrupt occurs.
> > > + */
> > > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> > (V4L2_CID_XILINX_SDIRX + 2)
> > > +/* EDH Error Mask Control to enable EDH error count */
> > > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> > (V4L2_CID_XILINX_SDIRX + 3)
> > > +/*
> > > + * Mode search Control to pass the bit mask of modes to detect.
> > > + *
> > > + * bit 0 set to detect SD mode,
> > > + * bit 1 set to detect HD mode,
> > > + * bit 2 set to detect 3G (3GA & 3GB) mode,
> > > + * bit 3 set to detect 6G mode,
> > > + * bit 4 set to detect 12G integer frame rate mode,
> > > + * bit 5 set to detect 12G fractional frame rate mode,
> > > + */
> > > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> > (V4L2_CID_XILINX_SDIRX + 4)
> > > +/*
> > > + * Get Detected Mode control
> > > + *
> > > + * Control Value - Mode detected
> > > + * 0 - SD
> > > + * 1 - HD
> > > + * 2 - 3G (3GA & 3GB)
> > > + * 3 - 6G
> > > + * 4 - 12G integer frame rate
> > > + * 5 - 12G fractional frame rate
> > > + */
> > > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
> > (V4L2_CID_XILINX_SDIRX + 5)
> > > +/* Get number of CRC errors status control */
> > > +#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 whether it is interlaced or not */
> > > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> > (V4L2_CID_XILINX_SDIRX + 9)
> > > +/* Get number of Active Streams */
> > > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> > (V4L2_CID_XILINX_SDIRX + 10)
> > > +/*
> > > + * Get if the detected mode is 3GB.
> > > + * Can be used to distinguished between 3GA and 3GB
> > > + */
> > > +#define V4L2_CID_XILINX_SDIRX_IS_3GB
> > (V4L2_CID_XILINX_SDIRX + 11)
> > > +
> > > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > >
> >
> > Regards,
> >
> > Hans

2020-06-01 15:04:22

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Hi Hans,

Thanks for reviewing!

> -----Original Message-----
> From: Hans Verkuil <[email protected]>
> Sent: Wednesday, May 6, 2020 3:25 PM
> To: Vishal Sagar <[email protected]>; Hyun Kwon <[email protected]>;
> [email protected]; [email protected];
> [email protected]; [email protected]; Michal Simek
> <[email protected]>; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]; Dinesh Kumar
> <[email protected]>; Sandip Kothari <[email protected]>; Joe Perches
> <[email protected]>
> Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
>
> Hi Vishal,
>
> Thank you for this patch.
>
> I have some comments below:
>
> On 29/04/2020 16:17, 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 10/12 bpc YUV 422 media bus format currently. 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 IP configuration.
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > v2
> > - Added DV timing support based on Hans Verkuilś feedback
> > - More documentation to custom v4l controls and events
> > - Fixed Hyunś comments
> > - Added macro for masking and shifting as per Joe Perches comments
> > - Updated to latest as per Xilinx github repo driver like
> > adding new DV timings not in mainline yet uptill 03/21/20
> >
> > drivers/media/platform/xilinx/Kconfig | 11 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > 5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -12,6 +12,17 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +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 4cdc0b1ec7a5..6c375f03f587 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -2,6 +2,7 @@
> >
> > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> >
> > +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,2162 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + *
> > + */
> > +
> > +#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-dv-timings.h>
> > +#include <linux/v4l2-subdev.h>
> > +#include <linux/xilinx-sdirxss.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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12,
> 10)
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> > +
> > +#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_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)
> > +
> > +#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_BYTE1_MASK 0xFF
> > +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> > +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> > +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> > +#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
> > +#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_OFFSET 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_YUV444 0x1
> > +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> > +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> > +
> > +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> > +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> > +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> > +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> > +
> > +#define CLK_INT 148500000UL
> > +
> > +#define rshift_and_mask(val, type) \
> > + (((val) >> type##_OFFSET) & type##_MASK)
> > +
> > +#define mask_and_rshift(val, type) \
> > + (((val) & type##_MASK) >> type##_OFFSET)
> > +
> > +/**
> > + * 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
> > + * @clks: array of clocks
> > + * @num_clks: number of clocks
> > + * @bpc: Bits per component, can be 10 or 12
> > + */
> > +struct xsdirxss_core {
> > + struct device *dev;
> > + void __iomem *iomem;
> > + int irq;
> > + bool include_edh;
> > + int mode;
> > + struct clk_bulk_data *clks;
> > + int num_clks;
> > + u32 bpc;
> > +};
> > +
> > +/**
> > + * 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
> > + * @format: Active V4L2 format on source pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @pad: source media pad
> > + * @vidlockwin: Video lock window value set by control
> > + * @edhmask: EDH mask set by control
> > + * @searchmask: Search mask set by control
> > + * @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.
> > + * @framer_enable: Flag for framer enabled or not set by control
> > + *
> > + * 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 format;
> > + struct v4l2_mbus_framefmt default_format;
> > + struct v4l2_fract frame_interval;
> > + struct media_pad pad;
> > + u32 vidlockwin;
> > + u32 edhmask;
> > + u16 searchmask;
> > + bool streaming;
> > + bool vidlocked;
> > + bool ts_is_interlaced;
> > + bool framer_enable;
> > +};
> > +
> > +/* List of clocks required by UHD-SDI Rx subsystem */
> > +static const char * const xsdirxss_clks[] = {
> > + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> > +};
> > +
> > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> > +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY10_1X20,
> > +};
> > +
> > +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY12_1X24,
> > +};
> > +
> > +static const struct v4l2_dv_timings fmt_cap[] = {
> > + V4L2_DV_BT_SDI_720X487I60,
> > + V4L2_DV_BT_CEA_720X576I50,
> > + V4L2_DV_BT_CEA_1280X720P24,
> > + V4L2_DV_BT_CEA_1280X720P25,
> > + V4L2_DV_BT_CEA_1280X720P30,
> > + V4L2_DV_BT_CEA_1280X720P50,
> > + V4L2_DV_BT_CEA_1280X720P60,
> > + V4L2_DV_BT_CEA_1920X1080P24,
> > + V4L2_DV_BT_CEA_1920X1080P30,
> > + V4L2_DV_BT_CEA_1920X1080I50,
> > + V4L2_DV_BT_CEA_1920X1080I60,
> > + V4L2_DV_BT_CEA_1920X1080P50,
> > + V4L2_DV_BT_CEA_1920X1080P60,
> > + V4L2_DV_BT_CEA_3840X2160P24,
> > + V4L2_DV_BT_CEA_3840X2160P30,
> > + V4L2_DV_BT_CEA_3840X2160P50,
> > + V4L2_DV_BT_CEA_3840X2160P60,
> > + V4L2_DV_BT_CEA_4096X2160P24,
> > + V4L2_DV_BT_CEA_4096X2160P25,
> > + V4L2_DV_BT_CEA_4096X2160P30,
> > + V4L2_DV_BT_CEA_4096X2160P50,
> > + V4L2_DV_BT_CEA_4096X2160P60,
> > +
> > + XLNX_V4L2_DV_BT_2048X1080P24,
> > + XLNX_V4L2_DV_BT_2048X1080P25,
> > + XLNX_V4L2_DV_BT_2048X1080P30,
> > + XLNX_V4L2_DV_BT_2048X1080I48,
> > + XLNX_V4L2_DV_BT_2048X1080I50,
> > + XLNX_V4L2_DV_BT_2048X1080I60,
> > + XLNX_V4L2_DV_BT_2048X1080P48,
> > + XLNX_V4L2_DV_BT_2048X1080P50,
> > + XLNX_V4L2_DV_BT_2048X1080P60,
> > + XLNX_V4L2_DV_BT_1920X1080P48,
> > + XLNX_V4L2_DV_BT_1920X1080I48,
> > + XLNX_V4L2_DV_BT_3840X2160P48,
> > + XLNX_V4L2_DV_BT_4096X2160P48,
> > +};
> > +
> > +struct xsdirxss_dv_map {
> > + u32 width;
> > + u32 height;
> > + u32 fps;
> > + struct v4l2_dv_timings format;
> > +};
> > +
> > +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> > + /* SD - 720x487i60 */
> > + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> > + /* SD - 720x576i50 */
> > + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> > + /* HD - 1280x720p23.98 */
> > + /* HD - 1280x720p24 */
> > + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> > + /* HD - 1280x720p25 */
> > + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> > + /* HD - 1280x720p29.97 */
> > + /* HD - 1280x720p30 */
> > + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> > + /* HD - 1280x720p50 */
> > + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> > + /* HD - 1280x720p59.94 */
> > + /* HD - 1280x720p60 */
> > + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> > + /* HD - 1920x1080p23.98 */
> > + /* HD - 1920x1080p24 */
> > + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> > + /* HD - 1920x1080p25 */
> > + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> > + /* HD - 1920x1080p29.97 */
> > + /* HD - 1920x1080p30 */
> > + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> > +
> > + /* HD - 2048x1080p23.98 */
> > + /* HD - 2048x1080p24 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> > + /* HD - 2048x1080p25 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> > + /* HD - 2048x1080p29.97 */
> > + /* HD - 2048x1080p30 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> > + /* HD - 1920x1080i47.95 */
> > + /* HD - 1920x1080i48 */
> > + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> > +
> > + /* HD - 1920x1080i50 */
> > + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> > + /* HD - 1920x1080i59.94 */
> > + /* HD - 1920x1080i60 */
> > + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> > +
> > + /* HD - 2048x1080i47.95 */
> > + /* HD - 2048x1080i48 */
> > + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> > + /* HD - 2048x1080i50 */
> > + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> > + /* HD - 2048x1080i59.94 */
> > + /* HD - 2048x1080i60 */
> > + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> > + /* 3G - 1920x1080p47.95 */
> > + /* 3G - 1920x1080p48 */
> > + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> > +
> > + /* 3G - 1920x1080p50 148.5 */
> > + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> > + /* 3G - 1920x1080p59.94 148.5/1.001 */
> > + /* 3G - 1920x1080p60 148.5 */
> > + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> > +
> > + /* 3G - 2048x1080p47.95 */
> > + /* 3G - 2048x1080p48 */
> > + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> > + /* 3G - 2048x1080p50 */
> > + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> > + /* 3G - 2048x1080p59.94 */
> > + /* 3G - 2048x1080p60 */
> > + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> > +
> > + /* 6G - 3840X2160p23.98 */
> > + /* 6G - 3840X2160p24 */
> > + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> > + /* 6G - 3840X2160p25 */
> > + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> > + /* 6G - 3840X2160p29.97 */
> > + /* 6G - 3840X2160p30 */
> > + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> > + /* 6G - 4096X2160p23.98 */
> > + /* 6G - 4096X2160p24 */
> > + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> > + /* 6G - 4096X2160p25 */
> > + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> > + /* 6G - 4096X2160p29.97 */
> > + /* 6G - 4096X2160p30 */
> > + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> > + /* 12G - 3840X2160p47.95 */
> > + /* 12G - 3840X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> > +
> > + /* 12G - 3840X2160p50 */
> > + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> > + /* 12G - 3840X2160p59.94 */
> > + /* 12G - 3840X2160p60 */
> > + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> > +
> > + /* 12G - 4096X2160p47.95 */
> > + /* 12G - 4096X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> > +
> > + /* 12G - 4096X2160p50 */
> > + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> > + /* 12G - 4096X2160p59.94 */
> > + /* 12G - 4096X2160p60 */
> > + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> > +};
> > +
> > +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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
> > +{
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static inline 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 val;
> > + struct device *dev = core->dev;
> > +
> > + mask &= XSDIRX_DETECT_ALL_MODES;
> > + if (!mask) {
> > + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> > + return -EINVAL;
> > + }
> > +
> > + dev_dbg(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(dev, "Detect multiple modes\n");
> > +
> > + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > +
> > + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > + } else {
> > + /* Fixed Mode */
> > + u32 forced_mode_mask;
> > +
> > + dev_dbg(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;
> > + default:
> > + forced_mode_mask = 0;
> > + }
> > + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> > + forced_mode_mask);
> > + val |= forced_mode_mask <<
> XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > + }
> > +
> > + dev_dbg(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 inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static inline 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 inline 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)
> > +{
> > + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> > +
> > + /*
> > + * TODO - Enable YUV444/RBG format in the bridge based
> > + * on BYTE3 color format.
> > + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> > + */
> > + if (enable)
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > + else
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, 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 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;
> > + }
> > +}
> > +
> > +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> > +{
> > + struct clk *gtclk;
> > + unsigned long clkrate;
> > + int ret, is_frac;
> > + struct xsdirxss_core *core = &state->core;
> > + u32 mode;
> > + static int prev_is_frac = -1;
> > +
> > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + /*
> > + * TODO: For now, don't change the clock rate for any mode except
> 12G.
> > + * In future, configure gt clock for all modes and enable clock only
> > + * when needed (stream on/off).
> > + */
> > + if (mode != XSDIRX_MODE_12GI_MASK && mode !=
> XSDIRX_MODE_12GF_MASK)
> > + return;
> > +
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + if (prev_is_frac == is_frac)
> > + return;
> > +
> > + xsdirx_core_disable(core);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +
> > + /* get sdi_rx_clk */
> > + gtclk = core->clks[1].clk;
> > + clkrate = clk_get_rate(gtclk);
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + /* calcualte clkrate */
> > + if (!is_frac)
> > + clkrate = CLK_INT;
> > + else
> > + clkrate = (CLK_INT * 1000) / 1001;
> > +
> > + ret = clk_set_rate(gtclk, clkrate);
> > + if (ret)
> > + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> > +
> > + clkrate = clk_get_rate(gtclk);
> > +
> > + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> > + clkrate, is_frac);
> > +
> > + xsdirx_framer(core, state->framer_enable);
> > + xsdirx_setedherrcnttrigger(core, state->edhmask);
> > + xsdirx_setvidlockwindow(core, state->vidlockwin);
> > + xsdirx_set_modedetect(core, state->searchmask);
> > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_globalintr(core, true);
> > + xsdirx_core_enable(core);
> > +
> > + prev_is_frac = is_frac;
> > +}
> > +
> > +/**
> > + * 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;
> > + struct device *dev = core->dev;
> > + 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->format;
> > + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> > +
> > + 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> > + active_luma = mask_and_rshift(payload,
> > +
> XST352_BYTE3_ACT_LUMA_COUNT);
> > + pic_type = mask_and_rshift(payload,
> XST352_BYTE2_PIC_TYPE);
> > + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> > + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> > + sampling = mask_and_rshift(payload,
> XST352_BYTE3_COLOR_FORMAT);
> > + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> > + } else {
> > + dev_dbg(dev, "No ST352 payload available : Mode = %d\n",
> mode);
> > + framerate = mask_and_rshift(val,
> XSDIRX_TS_DET_STAT_RATE);
> > + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> > + }
> > +
> > + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> > + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> > + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc =
> %d\n",
> > + bpc, core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> > + state->ts_is_interlaced = tscan ? false : true;
> > +
> > + dev_dbg(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(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(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(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(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(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(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;
> > + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(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(dev, "Unknown 12G Mode SMPTE
> standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + dev_err(dev, "Invalid Mode\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (valid) {
> > + if (pic_type)
> > + format->field = V4L2_FIELD_NONE;
> > + else
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + if (format->height == 1080 && pic_type && !tscan)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + /*
> > + * In 3GB DL pSF mode the video is similar to interlaced.
> > + * So though it is a progressive video, its transport is
> > + * interlaced and is sent as two width x (height/2) buffers.
> > + */
> > + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> > + if (state->ts_is_interlaced)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > + else
> > + format->field = V4L2_FIELD_NONE;
> > + }
> > + }
> > +
> > + if (format->field == V4L2_FIELD_ALTERNATE)
> > + format->height = format->height / 2;
> > +
> > + switch (sampling) {
> > + case XST352_BYTE3_COLOR_FORMAT_422:
> > + if (core->bpc == 10)
> > + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> > + break;
> > + case XST352_BYTE3_COLOR_FORMAT_420:
> > + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> > + case XST352_BYTE3_COLOR_FORMAT_GBR:
> > + format->code = 0;
> > + dev_dbg(dev, "No corresponding media bus formats\n");
> > + break;
> > + default:
> > + dev_err(dev, "Unsupported color format : %d\n", sampling);
> > + return -EINVAL;
> > + }
> > +
> > + xsdirxss_get_framerate(&state->frame_interval, framerate);
> > +
> > + dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
> > + state->frame_interval.numerator,
> > + state->frame_interval.denominator);
> > + dev_dbg(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 writing 1 to corresponding bit.
> > + *
> > + * 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;
> > + struct device *dev = core->dev;
> > + u32 status;
> > +
> > + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> > + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> > +
> > + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> > + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > + u32 val1, val2;
> > +
> > + dev_dbg(dev, "video lock/unlock interrupt\n");
> > +
> > + xsdirx_streamflow_control(core, false);
> > + state->streaming = false;
> > +
> > + 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
> > + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> > +
> > + if (!xsdirx_get_stream_properties(state)) {
> > + state->vidlocked = true;
> > + xsdirxss_set_gtclk(state);
> > + } else {
> > + dev_err(dev, "Unable to get stream
> properties!\n");
> > + state->vidlocked = false;
> > + }
> > + } else {
> > + dev_dbg(dev, "video unlock interrupt\n");
> > + state->vidlocked = false;
> > + }
> > +
> > + 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);
> > + }
> > +
> > + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core underflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core overflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_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_XILINX_SDIRX_UNDERFLOW:
> > + case V4L2_EVENT_XILINX_SDIRX_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;
>
> This should call v4l2_ctrl_subscribe_event instead, otherwise control events
> wouldn't be supported.
>

Ok I will add v4l2_ctrl_subscribe_event() here.

> > + }
> > + 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;
> > + struct device *dev = core->dev;
> > +
> > + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > + ctrl->id, ctrl->val);
> > +
> > + if (xsdirxss->streaming) {
> > + dev_err(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);
> > + xsdirxss->framer_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > + xsdirx_setvidlockwindow(core, ctrl->val);
> > + xsdirxss->vidlockwin = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > + xsdirx_setedherrcnttrigger(core, ctrl->val);
> > + xsdirxss->edhmask = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > + if (ctrl->val) {
> > + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > + dev_dbg(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(dev, "Upto 6G supported\n");
> > + ctrl->val &=
> ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +
> BIT(XSDIRX_MODE_12GF_OFFSET));
> > + }
> > +
> > + ret = xsdirx_set_modedetect(core, ctrl->val);
> > + if (!ret)
> > + xsdirxss->searchmask = ctrl->val;
> > + } else {
> > + dev_err(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;
> > + struct device *dev = core->dev;
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(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(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(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(dev, "Can't get values when video not
> locked!\n");
> > + return -EINVAL;
> > + }
> > + ctrl->val = xsdirxss->ts_is_interlaced;
>
> This control makes no sense: the v4l2_dv_timings struct will already tell you
> if it is an interlaced format or not. Same for v4l2_mbus_framefmt.
>

The SDI has a concept of supporting progressive, interlaced (both as we know normally) and a progressive segmented frames(psf).
The progressive segmented frames have their video content in progressive format but the transport stream is interlaced.
This is distinguished using the bit 6 and 7 of Byte 2 in the 4 byte ST352 payload.
Refer to sec 5.3 in SMPTE ST 352:2010.

This control can be used by the application to distinguish normal interlaced and progressive segmented frames.

> > + break;
> > + case V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(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(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(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + dev_dbg(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 i;
> > +
> > + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > + for (i = 0; i < 0x28; i++) {
> > + u32 data;
> > +
> > + 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;
>
> I suggest calling v4l2_ctrl_subdev_log_status() as well to log all controls.
>

Ok. I will add this in the next version.

> > +}
> > +
> > +/**
> > + * 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;
> > + struct device *dev = core->dev;
> > +
> > + if (enable) {
> > + if (!xsdirxss->vidlocked) {
> > + dev_dbg(dev, "Video is not locked\n");
> > + return -EINVAL;
> > + }
> > + if (xsdirxss->streaming) {
> > + dev_dbg(dev, "Already streaming\n");
> > + return -EINVAL;
> > + }
> > +
> > + xsdirx_streamflow_control(core, true);
> > + xsdirxss->streaming = true;
> > + dev_dbg(dev, "Streaming started\n");
> > + } else {
> > + if (!xsdirxss->streaming) {
> > + dev_dbg(dev, "Stopped streaming already\n");
> > + return 0;
> > + }
> > +
> > + xsdirx_streamflow_control(core, false);
> > + xsdirxss->streaming = false;
> > + dev_dbg(dev, "Streaming stopped\n");
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_input_status - It is used to determine if the video signal
> > + * is present / locked onto or not.
> > + *
> > + * @sd: V4L2 Sub device
> > + * @status: status of signal locked
> > + *
> > + * This is used to determine if the video signal is present and locked onto
> > + * by the SDI Rx core or not based on vidlocked flag.
> > + *
> > + * Return: zero on success
> > + */
> > +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > + if (!xsdirxss->vidlocked)
> > + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> > + else
> > + *status = 0;
> > +
> > + 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->format;
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +/**
> > + * xsdirxss_init_cfg - Initialise the pad format config to default
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + *
> > + * This function is used to initialize the pad format with the default
> > + * values.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct v4l2_mbus_framefmt *format;
> > +
> > + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> > + *format = xsdirxss->default_format;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * 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;
>
> It should fill in the width and height based on the current DV timings.
> Ditto for the field (I assume that's fixed as well based on whether this
> is interlaced or not). I'm not sure how colorspace information is handled
> for SDI.
>

Yes. I will update this logic to update the width, height and field based on current DV timings.
Please have a look at this in the next version.
The current IP supports BT709 colorimetry.
The colorimetry information of incoming stream is present in the ST 352 payload Byte 3 (bit 5 and bit 4) as per SMPTE ST 2081-10:2018.
B5:4 = 0 -> BT 709 as per SMPTE ST 274

> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> > + * @sd: pointer to v4l2 subdev structure
> > + * @cfg: V4L2 subdev pad configuration
> > + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> > + *
> > + * Return: -EINVAL or zero on success
> > + */
> > +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_mbus_code_enum
> *code)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + u32 index = code->index;
> > + u32 maxindex;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> > + else
> > + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> > +
> > + if (code->pad || index >= maxindex)
> > + return -EINVAL;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + code->code = xsdirxss_10bpc_mbus_fmts[index];
> > + else
> > + code->code = xsdirxss_12bpc_mbus_fmts[index];
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -EINVAL incase of invalid index and pad or zero on success
> > + */
> > +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_enum_dv_timings *timings)
> > +{
> > + if (timings->index >= ARRAY_SIZE(fmt_cap))
> > + return -EINVAL;
> > +
> > + if (timings->pad != 0)
> > + return -EINVAL;
> > +
> > + timings->timings = fmt_cap[timings->index];
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_query_dv_timings: Query for the current DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding
> timing
> > + * entry is not found or zero on success.
> > + */
> > +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_dv_timings *timings)
> > +{
> > + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> > + unsigned int i;
> > +
> > + if (!state->vidlocked)
> > + return -ENOLCK;
> > +
> > + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> > + if (state->format.width == xsdirxss_dv_timings[i].width &&
> > + state->format.height == xsdirxss_dv_timings[i].height &&
> > + state->frame_interval.denominator ==
> > + (xsdirxss_dv_timings[i].fps * 1000)) {
> > + *timings = xsdirxss_dv_timings[i].format;
> > + return 0;
> > + }
> > + }
> > +
> > + return -ERANGE;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * 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 const 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 const 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,
> > + }
>
> All these controls need to be documented in the header. Some of these controls
> may turn out to be controls that can be standardized for SDI receivers, others
> might be more vendor or driver specific.
>

I have documented these in the header. But it seems insufficient. Let me add more info in it.
Most of this is IP specific. Probably Mode detected and Active streams can be standardized.

> > +};
> > +
> > +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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > + .g_frame_interval = xsdirxss_g_frame_interval,
> > + .s_stream = xsdirxss_s_stream,
> > + .g_input_status = xsdirxss_g_input_status,
> > + .query_dv_timings = xsdirxss_query_dv_timings,
>
> This is missing g/s_dv_timings. You need to keep track of the current
> timings in the driver state: typically userspace will query timings and
> if that returns valid timings it will set them (s_dv_timings). With
> g_dv_timings the last set timings are returned.
>

Thanks for sharing this information. I didn't get the sequence from the current documentation.
Let me know if there is a standard application that I may refer to for this.

> If the timings change then the driver stops streaming and reports the
> SOURCE_CHANGE event, at which point userspace will query the new timings.
>

The SOURCE_CHANGE event will be generated only after stopping the streaming to the downstream by
disabling the bridges. The core will always be enabled to detect any other incoming stream.
Once a new incoming stream is detected for video_lock_window amount of time, a video lock interrupt occurs.

> The reported mediabus format resolution only changes when s_dv_timings
> is called: it's updated with the new width/height info.
>

I will create another v4l2_mbus_framefmt member in the state structure which will be updated with the detected formats in the s_dv_timings.
Then application may call get/set_fmt().

> In other words: the timings reported by g_dv_timings are controlled by
> userspace, the timings reported by query_dv_timings reflect the actual
> timings received on the SDI bus.
>

Thanks for this detailed explanation. ????

> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > + .init_cfg = xsdirxss_init_cfg,
> > + .get_fmt = xsdirxss_get_format,
> > + .set_fmt = xsdirxss_set_format,
> > + .enum_mbus_code = xsdirxss_enum_mbus_code,
> > + .enum_dv_timings = xsdirxss_enum_dv_timings,
>
> This is missing dv_timings_cap.
>

I will add this in the next version.

> > +};
> > +
> > +static const struct v4l2_subdev_ops xsdirxss_ops = {
> > + .core = &xsdirxss_core_ops,
> > + .video = &xsdirxss_video_ops,
> > + .pad = &xsdirxss_pad_ops
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > +{
> > + struct device_node *node = xsdirxss->core.dev->of_node;
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > + struct fwnode_handle *ep, *rep;
> > + int ret;
> > + const char *sdi_std;
> > +
> > + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > + dev_dbg(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(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(dev, "Invalid Line Rate\n");
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > + core->mode);
> > +
> > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to get xlnx,bpp\n");
> > + return ret;
> > + }
> > +
> > + if (core->bpc != 10 && core->bpc != 12) {
> > + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> > + core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > +
> FWNODE_GRAPH_ENDPOINT_NEXT);
> > + if (!ep) {
> > + dev_err(dev, "no source port found");
> > + ret = -EINVAL;
> > + goto dt_parse_done;
> > + }
> > +
> > + rep = fwnode_graph_get_remote_endpoint(ep);
> > + if (!rep) {
> > + dev_err(dev, "no remote sink endpoint found");
> > + ret = -EINVAL;
> > + }
> > +
> > + fwnode_handle_put(rep);
> > +dt_parse_done:
> > + fwnode_handle_put(ep);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_probe(struct platform_device *pdev)
> > +{
> > + struct v4l2_subdev *subdev;
> > + struct xsdirxss_state *xsdirxss;
> > + struct xsdirxss_core *core;
> > + struct device *dev;
> > + 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;
> > + dev = core->dev;
> > +
> > + /* Register interrupt handler */
> > + core->irq = platform_get_irq(pdev, 0);
> > + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> > + xsdirxss_irq_handler, IRQF_ONESHOT,
> > + dev_name(dev), xsdirxss);
> > + if (ret) {
> > + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> > + core->clks = devm_kcalloc(dev, core->num_clks,
> > + sizeof(*core->clks), GFP_KERNEL);
> > + if (!core->clks)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < core->num_clks; i++)
> > + core->clks[i].id = xsdirxss_clks[i];
> > +
> > + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = xsdirxss_parse_of(xsdirxss);
> > + if (ret < 0)
> > + goto clk_err;
> > +
> > + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(core->iomem)) {
> > + ret = PTR_ERR(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->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +
> > + /* Initialize the default format */
> > + if (core->bpc == 10)
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY12_1X24;
> > + xsdirxss->default_format.field = V4L2_FIELD_NONE;
> > + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
>
> This shouldn't be DEFAULT. Can you explain a bit how colorspace (or
> colorimetry
> in general) is determined for SDI?
>

That is correct. As mentioned earlier the colorspace in SDI is determined by bit 5 and 4 of byte 3
in the 4 byte ST 352 payload as per SMPTE 2081-10:2018

b5:b4 = 0h identifies Rec 709 colorimetry in accordance with Recommendation ITU-R BT.709 as referenced by SMPTE ST 274
b5:b4 = 1h identifies that the colorimetry is defined in the Color VANC packet as defined in SMPTE ST 2048-1
b5:b4 = 2h identifies UHDTV colorimetry in accordance with the reference primaries and reference white as defined in SMPTE ST 2036-1. See Note 2 to Table 3.
b5:b4 = 3h identifies unknown colorimetry

> > + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
> > + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
> > +
> > + xsdirxss->format = xsdirxss->default_format;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + subdev = &xsdirxss->subdev;
> > + v4l2_subdev_init(subdev, &xsdirxss_ops);
> > +
> > + subdev->dev = &pdev->dev;
> > + strscpy(subdev->name, dev_name(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->pad);
> > + if (ret < 0)
> > + goto error;
> > +
> > + /* Initialise and register the controls */
> > + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > +
> > + if (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(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(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > +
> > + if (core->include_edh) {
> > + for (i = 0; i < num_edh_ctrls; i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_edh_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > + }
> > +
> > + if (xsdirxss->ctrl_handler.error) {
> > + dev_err(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(dev, "failed to set controls\n");
> > + goto error;
> > + }
> > +
> > + platform_set_drvdata(pdev, xsdirxss);
> > +
> > + ret = v4l2_async_register_subdev(subdev);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to register subdev\n");
> > + goto error;
> > + }
> > +
> > + xsdirxss->streaming = false;
> > +
> > + xsdirx_core_enable(core);
> > +
> > + dev_info(dev, "probe success\n");
> > +
> > + return 0;
> > +error:
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +clk_err:
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > + v4l2_async_unregister_subdev(subdev);
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > +
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_core_disable(core);
> > + xsdirx_streamflow_control(core, false);
> > +
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> > + { }
> > +};
> > +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 <[email protected]>");
> > +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 000000000000..6f2a093968d9
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,179 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode, event, custom timings
> > + * and flag definitions.
> > + *
> > + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * Events
> > + *
> > + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
> underflowed
> > + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
> overflowed
> > + */
> > +#define V4L2_EVENT_XILINX_SDIRX_CLASS
> (V4L2_EVENT_PRIVATE_START | 0x200)
> > +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> > +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> > +
> > +/*
> > + * 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
> > +
> > +/* Xilinx DV timings not in mainline yet */
> > +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
>
> Why not add these to v4l2-dv-timings.h? I do need to know on which standard
> they are based.
>

Currently these are based off a timing table used in the corresponding bare metal driver.
I will try to get the standards from which these are based on.
Meanwhile I hope it is ok to add them as custom timings.

> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index b6441fe705c5..e9de65e82642 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
>
> Why is this in a separate header? It seems to me that it makes more sense to
> have
> a single public header for this driver.
>

I think the xilinx-v4l2-controls.h is created to house all custom controls of Xilinx V4L2 drivers.
Since the Xilinx Video Test Pattern generator has its controls in the xilinx-v4l2-controls.h,
I decided to keep the controls in this file and the events in the SDI Rx specific header.

> > @@ -71,4 +71,71 @@
> > /* Noise level */
> > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> (V4L2_CID_XILINX_TPG + 17)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* The base for the sdi rx driver controls.
> > + * We reserve 32 controls for this driver.
> > + *
> > + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> > + * EDH is enabled.
> > + * The controls which can be set should only be set before enabling
> > + * streaming. The controls which can be got should be called while
> > + * streaming to get correct values.
> > + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
> dv timing
> > + * returns a valid timing.
> > + */
> > +
> > +#define V4L2_CID_XILINX_SDIRX
> (V4L2_CID_XILINX_BASE + 0x20)
> > +
> > +/* Framer Control to enable or disable the framer */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER
> (V4L2_CID_XILINX_SDIRX + 1)
> > +/*
> > + * Video Lock Window Control to set the video lock window value
> > + * This is the amount of time the mode and transport stream need
> > + * to be locked before a video lock interrupt occurs.
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> (V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control to enable EDH error count */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> (V4L2_CID_XILINX_SDIRX + 3)
> > +/*
> > + * Mode search Control to pass the bit mask of modes to detect.
> > + *
> > + * bit 0 set to detect SD mode,
> > + * bit 1 set to detect HD mode,
> > + * bit 2 set to detect 3G (3GA & 3GB) mode,
> > + * bit 3 set to detect 6G mode,
> > + * bit 4 set to detect 12G integer frame rate mode,
> > + * bit 5 set to detect 12G fractional frame rate mode,
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> (V4L2_CID_XILINX_SDIRX + 4)
> > +/*
> > + * Get Detected Mode control
> > + *
> > + * Control Value - Mode detected
> > + * 0 - SD
> > + * 1 - HD
> > + * 2 - 3G (3GA & 3GB)
> > + * 3 - 6G
> > + * 4 - 12G integer frame rate
> > + * 5 - 12G fractional frame rate
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
> (V4L2_CID_XILINX_SDIRX + 5)
> > +/* Get number of CRC errors status control */
> > +#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 whether it is interlaced or not */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> (V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get number of Active Streams */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> (V4L2_CID_XILINX_SDIRX + 10)
> > +/*
> > + * Get if the detected mode is 3GB.
> > + * Can be used to distinguished between 3GA and 3GB
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB
> (V4L2_CID_XILINX_SDIRX + 11)
> > +
> > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> >
>
> Regards,
>
> Hans

Regards
Vishal Sagar

2020-06-01 15:19:15

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH v2 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem

Hi Laurent,

Thanks for the review.

> -----Original Message-----
> From: Laurent Pinchart <[email protected]>
> Sent: Wednesday, May 6, 2020 6:32 PM
> To: Vishal Sagar <[email protected]>
> Cc: Hyun Kwon <[email protected]>; [email protected];
> [email protected]; [email protected]; Michal Simek
> <[email protected]>; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]; Dinesh Kumar
> <[email protected]>; Sandip Kothari <[email protected]>; Joe Perches
> <[email protected]>
> Subject: Re: [PATCH v2 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-
> SDI Receiver Subsystem
>
> Hi Vishal,
>
> Thank you for the patch.
>
> On Wed, Apr 29, 2020 at 07:47:03PM +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 <[email protected]>
> > ---
> > v2
> > - Removed references to xlnx,video*
> > - Fixed as per Sakari Ailus and Rob Herring's comments
> > - Converted to yaml format
> >
> > .../bindings/media/xilinx/xlnx,sdirxss.yaml | 132 ++++++++++++++++++
> > 1 file changed, 132 insertions(+)
> > create mode 100644
> Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> >
> > diff --git
> a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> > new file mode 100644
> > index 000000000000..9133ad19df55
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> > @@ -0,0 +1,132 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/media/xilinx/xlnx,sdirxss.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +
> > +title: Xilinx SMPTE UHD-SDI Receiver Subsystem
> > +
> > +maintainers:
> > + - Vishal Sagar <[email protected]>
> > +
> > +description: |
> > + 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 upto 12G mode 8 data streams
> and output
>
> s/upto/up to/
>

I will fix this in next version.

> > + a dual pixel per clock RGB/YUV444,422/420 10/12 bits per component
> AXI4-Stream.
> > +
> > +properties:
> > + compatible:
> > + items:
> > + - enum:
> > + - xlnx,v-smpte-uhdsdi-rx-ss-2.0
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > + clocks:
> > + description: List of clock specifiers
> > + items:
> > + - description: AXI4-Lite clock
> > + - description: SMPTE UHD-SDI Rx core clock
> > + - description: Video clock
> > +
> > + clock-names:
> > + items:
> > + - const: s_axi_aclk
> > + - const: sdi_rx_clk
> > + - const: video_out_clk
> > +
> > + xlnx,bpp:
> > + description: Bits per pixel supported. Can be 10 or 12 bits per pixel only.
> > + allOf:
> > + - $ref: "/schemas/types.yaml#/definitions/uint32"
> > + - enum: [10, 12]
>
> I don't see this as a design parameter in the documentation (pg290,
> v2.0). What does it correspond to ? All the BPC mentions in the
> documentation always state that 10-bit is the only supported value.
>

The new version of IP being released will have 10 and 12 bit support. It is already in the Xilinx linux-xlnx repo.
I will rename this to "xlnx,bpc" instead of "xlnx,bpp" to refer to bits per component.

> > +
> > + xlnx,line-rate:
> > + description: |
> > + The maximum mode supported by the design. Possible values are as
> below
> > + 12G_SDI_8DS - 12G mode with 8 data streams
> > + 6G_SDI - 6G mode
> > + 3G_SDI - 3G mode
> > + enum:
> > + - 12G_SDI_8DS
> > + - 6G_SDI
> > + - 3G_SDI
>
> How about making this an integer property, with #define in
> include/dt-bindings/media/xilinx-sdi.h ? As far as I understand, the SDI
> TX subsystem has the same parameter, so the #define could be shared
> between the two.
>

Yes that is ok with me. I will add this in the next version.

> > +
> > + xlnx,include-edh:
> > + type: boolean
> > + description: |
> > + This is present when the Error Detection and Handling processor is
> > + enabled in design.
> > +
> > + ports:
> > + type: object
> > + description: |
> > + 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.
>
> We should still have an input port. It can be connected to a DT node for
> a physical SDI connector, or any other component in the platform (I
> expect the former to be the common case). There are DT bindings for
> connectors in Documentation/devicetree/bindings/display/connector/, we
> should add one for SDI.
>

Yes the input port is a physical SDI connector connected to an equipment like broadcast camera.
But the camera/equipment can't be controlled by the V4L2 pipeline and SDI protocol is unidirectional.

If we add another dt node, then I think another dummy v4l subdev driver will need to implemented and loaded
to complete the pipe as Xilinx Video driver will need it.

Could you please share the reason to have this input port in the SDI Rx driver?

> > + properties:
> > + port@0:
> > + type: object
> > + description: Source port
> > + properties:
> > + reg:
> > + const: 0
> > + endpoint:
> > + type: object
> > + properties:
> > + remote-endpoint: true
> > + required:
> > + - remote-endpoint
> > + additionalProperties: false
> > + additionalProperties: false
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - clocks
> > + - clock-names
> > + - xlnx,line-rate
> > + - xlnx,bpp
> > + - ports
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + uhdsdirxss: v-smpte-uhdsdi-rxss@80000000 {
> > + compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0";
> > + 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";
> > + xlnx,bpp = <10>;
> > +
> > + ports {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > + port@0 {
> > + reg = <0>;
> > + sdirx_out: endpoint {
> > + remote-endpoint = <&vcap_sdirx_in>;
> > + };
> > + };
> > + };
> > + };
>
> --
> Regards,
>
> Laurent Pinchart

Regards
Vishal Sagar

2020-06-02 10:49:37

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

On 01/06/2020 16:59, Vishal Sagar wrote:
> Hi Hans,
>
> Thanks for reviewing!
>
>>> + case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
>>> + if (!xsdirxss->vidlocked) {
>>> + dev_err(dev, "Can't get values when video not
>> locked!\n");
>>> + return -EINVAL;
>>> + }
>>> + ctrl->val = xsdirxss->ts_is_interlaced;
>>
>> This control makes no sense: the v4l2_dv_timings struct will already tell you
>> if it is an interlaced format or not. Same for v4l2_mbus_framefmt.
>>
>
> The SDI has a concept of supporting progressive, interlaced (both as we know normally) and a progressive segmented frames(psf).
> The progressive segmented frames have their video content in progressive format but the transport stream is interlaced.
> This is distinguished using the bit 6 and 7 of Byte 2 in the 4 byte ST352 payload.
> Refer to sec 5.3 in SMPTE ST 352:2010.
>
> This control can be used by the application to distinguish normal interlaced and progressive segmented frames.

Ah, interesting. So this relies on the receiver to reconstruct the progressive
frame by combining the top and bottom field, right?

I think this deserves a new v4l2_field value:

V4L2_FIELD_ALTERNATE_PROG

Basically this is identical to V4L2_FIELD_ALTERNATE, except that the two fields
combine to a single progressive frame.

Regards,

Hans

PS: I'll look at your other comments separately

2020-06-03 01:16:41

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] media: dt-bindings: media: xilinx: Add Xilinx UHD-SDI Receiver Subsystem

Hi Vishal,

On Mon, Jun 01, 2020 at 03:14:52PM +0000, Vishal Sagar wrote:
> On Wednesday, May 6, 2020 6:32 PM, Laurent Pinchart wrote:
> > On Wed, Apr 29, 2020 at 07:47:03PM +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 <[email protected]>
> > > ---
> > > v2
> > > - Removed references to xlnx,video*
> > > - Fixed as per Sakari Ailus and Rob Herring's comments
> > > - Converted to yaml format
> > >
> > > .../bindings/media/xilinx/xlnx,sdirxss.yaml | 132 ++++++++++++++++++
> > > 1 file changed, 132 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> > >
> > > diff --git
> > a/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> > b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> > > new file mode 100644
> > > index 000000000000..9133ad19df55
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,sdirxss.yaml
> > > @@ -0,0 +1,132 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/media/xilinx/xlnx,sdirxss.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +
> > > +title: Xilinx SMPTE UHD-SDI Receiver Subsystem
> > > +
> > > +maintainers:
> > > + - Vishal Sagar <[email protected]>
> > > +
> > > +description: |
> > > + 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 upto 12G mode 8 data streams and output
> >
> > s/upto/up to/
>
> I will fix this in next version.
>
> > > + a dual pixel per clock RGB/YUV444,422/420 10/12 bits per component AXI4-Stream.
> > > +
> > > +properties:
> > > + compatible:
> > > + items:
> > > + - enum:
> > > + - xlnx,v-smpte-uhdsdi-rx-ss-2.0
> > > +
> > > + reg:
> > > + maxItems: 1
> > > +
> > > + interrupts:
> > > + maxItems: 1
> > > +
> > > + clocks:
> > > + description: List of clock specifiers
> > > + items:
> > > + - description: AXI4-Lite clock
> > > + - description: SMPTE UHD-SDI Rx core clock
> > > + - description: Video clock
> > > +
> > > + clock-names:
> > > + items:
> > > + - const: s_axi_aclk
> > > + - const: sdi_rx_clk
> > > + - const: video_out_clk
> > > +
> > > + xlnx,bpp:
> > > + description: Bits per pixel supported. Can be 10 or 12 bits per pixel only.
> > > + allOf:
> > > + - $ref: "/schemas/types.yaml#/definitions/uint32"
> > > + - enum: [10, 12]
> >
> > I don't see this as a design parameter in the documentation (pg290,
> > v2.0). What does it correspond to ? All the BPC mentions in the
> > documentation always state that 10-bit is the only supported value.
>
> The new version of IP being released will have 10 and 12 bit support. It is already in the Xilinx linux-xlnx repo.
> I will rename this to "xlnx,bpc" instead of "xlnx,bpp" to refer to bits per component.

Is the documentation for the new IP core version available ? Should this
property only be allowed for the new version, given that in v2.0 the BPC
is fixed to 10 ?

> > > +
> > > + xlnx,line-rate:
> > > + description: |
> > > + The maximum mode supported by the design. Possible values are as below
> > > + 12G_SDI_8DS - 12G mode with 8 data streams
> > > + 6G_SDI - 6G mode
> > > + 3G_SDI - 3G mode
> > > + enum:
> > > + - 12G_SDI_8DS
> > > + - 6G_SDI
> > > + - 3G_SDI
> >
> > How about making this an integer property, with #define in
> > include/dt-bindings/media/xilinx-sdi.h ? As far as I understand, the SDI
> > TX subsystem has the same parameter, so the #define could be shared
> > between the two.
>
> Yes that is ok with me. I will add this in the next version.
>
> > > +
> > > + xlnx,include-edh:
> > > + type: boolean
> > > + description: |
> > > + This is present when the Error Detection and Handling processor is
> > > + enabled in design.
> > > +
> > > + ports:
> > > + type: object
> > > + description: |
> > > + 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.
> >
> > We should still have an input port. It can be connected to a DT node for
> > a physical SDI connector, or any other component in the platform (I
> > expect the former to be the common case). There are DT bindings for
> > connectors in Documentation/devicetree/bindings/display/connector/, we
> > should add one for SDI.
>
> Yes the input port is a physical SDI connector connected to an equipment like broadcast camera.
> But the camera/equipment can't be controlled by the V4L2 pipeline and SDI protocol is unidirectional.
>
> If we add another dt node, then I think another dummy v4l subdev driver will need to implemented and loaded
> to complete the pipe as Xilinx Video driver will need it.

We don't necessarily need a driver for the connector (although it may be
a good idea to do so, but that's a separate question). The sdi-rx driver
could handle the SDI connector DT node by parsing its properties
manually (assuming it would contain properties that need to be parsed).

> Could you please share the reason to have this input port in the SDI Rx driver?

We try to make sure the whole pipeline is modelled in DT. This applies
to both V4L2 and DRM/KMS. While the connector doesn't need to be
controlled by software (it's just a connector), it may still have
properties that matter from a software point of view. For instance the
label property can be used to specify how the connector is labeled on
the board or on the device's case, allowing applications to display the
correct labels to the users. Another use case related to the 4-pin
mini-DIN connectors typically used for S-Video. On some devices, they
are also used for composite video, with multiple video sources connected
to the same mini-DIN connector with a special cable. Kernel drivers need
to know how signals are routed, and DT nodes help there.

> > > + properties:
> > > + port@0:
> > > + type: object
> > > + description: Source port
> > > + properties:
> > > + reg:
> > > + const: 0
> > > + endpoint:
> > > + type: object
> > > + properties:
> > > + remote-endpoint: true
> > > + required:
> > > + - remote-endpoint
> > > + additionalProperties: false
> > > + additionalProperties: false
> > > +
> > > +required:
> > > + - compatible
> > > + - reg
> > > + - interrupts
> > > + - clocks
> > > + - clock-names
> > > + - xlnx,line-rate
> > > + - xlnx,bpp
> > > + - ports
> > > +
> > > +additionalProperties: false
> > > +
> > > +examples:
> > > + - |
> > > + uhdsdirxss: v-smpte-uhdsdi-rxss@80000000 {

I forgot to mention, you can drop the label as it's not used.

> > > + compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0";
> > > + 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";
> > > + xlnx,bpp = <10>;

And I would group the xlnx,* properties after the standard properties.

> > > +
> > > + ports {
> > > + #address-cells = <1>;
> > > + #size-cells = <0>;
> > > + port@0 {
> > > + reg = <0>;
> > > + sdirx_out: endpoint {
> > > + remote-endpoint = <&vcap_sdirx_in>;
> > > + };
> > > + };
> > > + };
> > > + };

--
Regards,

Laurent Pinchart

2020-06-09 17:52:08

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

On 01/06/2020 16:59, Vishal Sagar wrote:
> Hi Hans,
>
> Thanks for reviewing!
>
>> -----Original Message-----
>> From: Hans Verkuil <[email protected]>
>> Sent: Wednesday, May 6, 2020 3:25 PM
>> To: Vishal Sagar <[email protected]>; Hyun Kwon <[email protected]>;
>> [email protected]; [email protected];
>> [email protected]; [email protected]; Michal Simek
>> <[email protected]>; [email protected];
>> [email protected]; [email protected]; linux-arm-
>> [email protected]; [email protected]; Dinesh Kumar
>> <[email protected]>; Sandip Kothari <[email protected]>; Joe Perches
>> <[email protected]>
>> Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
>> driver
>>
>> Hi Vishal,
>>
>> Thank you for this patch.
>>
>> I have some comments below:
>>
>> On 29/04/2020 16:17, 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 10/12 bpc YUV 422 media bus format currently. 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 IP configuration.
>>>
>>> Signed-off-by: Vishal Sagar <[email protected]>
>>> ---

<snip>

>>> +/**
>>> + * 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;
>>
>> It should fill in the width and height based on the current DV timings.
>> Ditto for the field (I assume that's fixed as well based on whether this
>> is interlaced or not). I'm not sure how colorspace information is handled
>> for SDI.
>>
>
> Yes. I will update this logic to update the width, height and field based on current DV timings.
> Please have a look at this in the next version.
> The current IP supports BT709 colorimetry.
> The colorimetry information of incoming stream is present in the ST 352 payload Byte 3 (bit 5 and bit 4) as per SMPTE ST 2081-10:2018.
> B5:4 = 0 -> BT 709 as per SMPTE ST 274

OK, so this can be filled in by the driver as well.

>
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/**
>>> + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
>>> + * @sd: pointer to v4l2 subdev structure
>>> + * @cfg: V4L2 subdev pad configuration
>>> + * @code: pointer to v4l2_subdev_mbus_code_enum structure
>>> + *
>>> + * Return: -EINVAL or zero on success
>>> + */
>>> +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
>>> + struct v4l2_subdev_pad_config *cfg,
>>> + struct v4l2_subdev_mbus_code_enum
>> *code)
>>> +{
>>> + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
>>> + u32 index = code->index;
>>> + u32 maxindex;
>>> +
>>> + if (xsdirxss->core.bpc == 10)
>>> + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
>>> + else
>>> + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
>>> +
>>> + if (code->pad || index >= maxindex)
>>> + return -EINVAL;
>>> +
>>> + if (xsdirxss->core.bpc == 10)
>>> + code->code = xsdirxss_10bpc_mbus_fmts[index];
>>> + else
>>> + code->code = xsdirxss_12bpc_mbus_fmts[index];
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/**
>>> + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
>>> + * @sd: pointer to v4l2 subdev structure
>>> + * @timings: DV timings structure to be returned.
>>> + *
>>> + * Return: -EINVAL incase of invalid index and pad or zero on success
>>> + */
>>> +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
>>> + struct v4l2_enum_dv_timings *timings)
>>> +{
>>> + if (timings->index >= ARRAY_SIZE(fmt_cap))
>>> + return -EINVAL;
>>> +
>>> + if (timings->pad != 0)
>>> + return -EINVAL;
>>> +
>>> + timings->timings = fmt_cap[timings->index];
>>> + return 0;
>>> +}
>>> +
>>> +/**
>>> + * xsdirxss_query_dv_timings: Query for the current DV timings
>>> + * @sd: pointer to v4l2 subdev structure
>>> + * @timings: DV timings structure to be returned.
>>> + *
>>> + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding
>> timing
>>> + * entry is not found or zero on success.
>>> + */
>>> +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
>>> + struct v4l2_dv_timings *timings)
>>> +{
>>> + struct xsdirxss_state *state = to_xsdirxssstate(sd);
>>> + unsigned int i;
>>> +
>>> + if (!state->vidlocked)
>>> + return -ENOLCK;
>>> +
>>> + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
>>> + if (state->format.width == xsdirxss_dv_timings[i].width &&
>>> + state->format.height == xsdirxss_dv_timings[i].height &&
>>> + state->frame_interval.denominator ==
>>> + (xsdirxss_dv_timings[i].fps * 1000)) {
>>> + *timings = xsdirxss_dv_timings[i].format;
>>> + return 0;
>>> + }
>>> + }
>>> +
>>> + return -ERANGE;
>>> +}
>>> +
>>> +/* -----------------------------------------------------------------------------
>>> + * 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 const 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 const 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,
>>> + }
>>
>> All these controls need to be documented in the header. Some of these controls
>> may turn out to be controls that can be standardized for SDI receivers, others
>> might be more vendor or driver specific.
>>
>
> I have documented these in the header. But it seems insufficient. Let me add more info in it.
> Most of this is IP specific. Probably Mode detected and Active streams can be standardized.

I'll review this some more when v3 is posted. It's likely I'll discuss this some more
with you.

>
>>> +};
>>> +
>>> +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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
>>> + .g_frame_interval = xsdirxss_g_frame_interval,
>>> + .s_stream = xsdirxss_s_stream,
>>> + .g_input_status = xsdirxss_g_input_status,
>>> + .query_dv_timings = xsdirxss_query_dv_timings,
>>
>> This is missing g/s_dv_timings. You need to keep track of the current
>> timings in the driver state: typically userspace will query timings and
>> if that returns valid timings it will set them (s_dv_timings). With
>> g_dv_timings the last set timings are returned.
>>
>
> Thanks for sharing this information. I didn't get the sequence from the current documentation.
> Let me know if there is a standard application that I may refer to for this.

v4l2-ctl does this, actually. streaming_set_cap() in v4l2-ctl-streaming.cpp is what
is used when you run 'v4l2-ctl --stream-mmap'.

>
>> If the timings change then the driver stops streaming and reports the
>> SOURCE_CHANGE event, at which point userspace will query the new timings.
>>
>
> The SOURCE_CHANGE event will be generated only after stopping the streaming to the downstream by
> disabling the bridges. The core will always be enabled to detect any other incoming stream.
> Once a new incoming stream is detected for video_lock_window amount of time, a video lock interrupt occurs.

I don't quite understand this.

The SOURCE_CHANGE event should be issued when the driver detects that it
lost signal, or a new signal appears, or the stream changes resolution.
I.e. all cases where userspace needs to take action.

>
>> The reported mediabus format resolution only changes when s_dv_timings
>> is called: it's updated with the new width/height info.
>>
>
> I will create another v4l2_mbus_framefmt member in the state structure which will be updated with the detected formats in the s_dv_timings.
> Then application may call get/set_fmt().
>
>> In other words: the timings reported by g_dv_timings are controlled by
>> userspace, the timings reported by query_dv_timings reflect the actual
>> timings received on the SDI bus.
>>
>
> Thanks for this detailed explanation. ????
>
>>> +};
>>> +
>>> +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
>>> + .init_cfg = xsdirxss_init_cfg,
>>> + .get_fmt = xsdirxss_get_format,
>>> + .set_fmt = xsdirxss_set_format,
>>> + .enum_mbus_code = xsdirxss_enum_mbus_code,
>>> + .enum_dv_timings = xsdirxss_enum_dv_timings,
>>
>> This is missing dv_timings_cap.
>>
>
> I will add this in the next version.
>
>>> +};
>>> +
>>> +static const struct v4l2_subdev_ops xsdirxss_ops = {
>>> + .core = &xsdirxss_core_ops,
>>> + .video = &xsdirxss_video_ops,
>>> + .pad = &xsdirxss_pad_ops
>>> +};
>>> +
>>> +/* -----------------------------------------------------------------------------
>>> + * Platform Device Driver
>>> + */
>>> +
>>> +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
>>> +{
>>> + struct device_node *node = xsdirxss->core.dev->of_node;
>>> + struct xsdirxss_core *core = &xsdirxss->core;
>>> + struct device *dev = core->dev;
>>> + struct fwnode_handle *ep, *rep;
>>> + int ret;
>>> + const char *sdi_std;
>>> +
>>> + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
>>> + dev_dbg(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(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(dev, "Invalid Line Rate\n");
>>> + return -EINVAL;
>>> + }
>>> + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
>>> + core->mode);
>>> +
>>> + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
>>> + if (ret < 0) {
>>> + dev_err(dev, "failed to get xlnx,bpp\n");
>>> + return ret;
>>> + }
>>> +
>>> + if (core->bpc != 10 && core->bpc != 12) {
>>> + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
>>> + core->bpc);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
>>> +
>> FWNODE_GRAPH_ENDPOINT_NEXT);
>>> + if (!ep) {
>>> + dev_err(dev, "no source port found");
>>> + ret = -EINVAL;
>>> + goto dt_parse_done;
>>> + }
>>> +
>>> + rep = fwnode_graph_get_remote_endpoint(ep);
>>> + if (!rep) {
>>> + dev_err(dev, "no remote sink endpoint found");
>>> + ret = -EINVAL;
>>> + }
>>> +
>>> + fwnode_handle_put(rep);
>>> +dt_parse_done:
>>> + fwnode_handle_put(ep);
>>> + return ret;
>>> +}
>>> +
>>> +static int xsdirxss_probe(struct platform_device *pdev)
>>> +{
>>> + struct v4l2_subdev *subdev;
>>> + struct xsdirxss_state *xsdirxss;
>>> + struct xsdirxss_core *core;
>>> + struct device *dev;
>>> + 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;
>>> + dev = core->dev;
>>> +
>>> + /* Register interrupt handler */
>>> + core->irq = platform_get_irq(pdev, 0);
>>> + ret = devm_request_threaded_irq(dev, core->irq, NULL,
>>> + xsdirxss_irq_handler, IRQF_ONESHOT,
>>> + dev_name(dev), xsdirxss);
>>> + if (ret) {
>>> + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
>>> + ret);
>>> + return ret;
>>> + }
>>> +
>>> + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
>>> + core->clks = devm_kcalloc(dev, core->num_clks,
>>> + sizeof(*core->clks), GFP_KERNEL);
>>> + if (!core->clks)
>>> + return -ENOMEM;
>>> +
>>> + for (i = 0; i < core->num_clks; i++)
>>> + core->clks[i].id = xsdirxss_clks[i];
>>> +
>>> + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = xsdirxss_parse_of(xsdirxss);
>>> + if (ret < 0)
>>> + goto clk_err;
>>> +
>>> + core->iomem = devm_platform_ioremap_resource(pdev, 0);
>>> + if (IS_ERR(core->iomem)) {
>>> + ret = PTR_ERR(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->pad.flags = MEDIA_PAD_FL_SOURCE;
>>> +
>>> + /* Initialize the default format */
>>> + if (core->bpc == 10)
>>> + xsdirxss->default_format.code =
>> MEDIA_BUS_FMT_UYVY10_1X20;
>>> + else
>>> + xsdirxss->default_format.code =
>> MEDIA_BUS_FMT_UYVY12_1X24;
>>> + xsdirxss->default_format.field = V4L2_FIELD_NONE;
>>> + xsdirxss->default_format.colorspace = V4L2_COLORSPACE_DEFAULT;
>>
>> This shouldn't be DEFAULT. Can you explain a bit how colorspace (or
>> colorimetry
>> in general) is determined for SDI?
>>
>
> That is correct. As mentioned earlier the colorspace in SDI is determined by bit 5 and 4 of byte 3
> in the 4 byte ST 352 payload as per SMPTE 2081-10:2018
>
> b5:b4 = 0h identifies Rec 709 colorimetry in accordance with Recommendation ITU-R BT.709 as referenced by SMPTE ST 274
> b5:b4 = 1h identifies that the colorimetry is defined in the Color VANC packet as defined in SMPTE ST 2048-1
> b5:b4 = 2h identifies UHDTV colorimetry in accordance with the reference primaries and reference white as defined in SMPTE ST 2036-1. See Note 2 to Table 3.
> b5:b4 = 3h identifies unknown colorimetry

OK. Based on that I'd pick REC709 as the default colorspace. This will be updated to
the actual colorspace when you receive a new video stream and can extract this information.

>
>>> + xsdirxss->default_format.width = XSDIRX_DEFAULT_WIDTH;
>>> + xsdirxss->default_format.height = XSDIRX_DEFAULT_HEIGHT;
>>> +
>>> + xsdirxss->format = xsdirxss->default_format;
>>> +
>>> + /* Initialize V4L2 subdevice and media entity */
>>> + subdev = &xsdirxss->subdev;
>>> + v4l2_subdev_init(subdev, &xsdirxss_ops);
>>> +
>>> + subdev->dev = &pdev->dev;
>>> + strscpy(subdev->name, dev_name(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->pad);
>>> + if (ret < 0)
>>> + goto error;
>>> +
>>> + /* Initialise and register the controls */
>>> + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
>>> +
>>> + if (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(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(dev, "Failed to add %s ctrl\n",
>>> + xsdirxss_ctrls[i].name);
>>> + goto error;
>>> + }
>>> + }
>>> +
>>> + if (core->include_edh) {
>>> + for (i = 0; i < num_edh_ctrls; i++) {
>>> + struct v4l2_ctrl *ctrl;
>>> +
>>> + dev_dbg(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(dev, "Failed to add %s ctrl\n",
>>> + xsdirxss_edh_ctrls[i].name);
>>> + goto error;
>>> + }
>>> + }
>>> + }
>>> +
>>> + if (xsdirxss->ctrl_handler.error) {
>>> + dev_err(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(dev, "failed to set controls\n");
>>> + goto error;
>>> + }
>>> +
>>> + platform_set_drvdata(pdev, xsdirxss);
>>> +
>>> + ret = v4l2_async_register_subdev(subdev);
>>> + if (ret < 0) {
>>> + dev_err(dev, "failed to register subdev\n");
>>> + goto error;
>>> + }
>>> +
>>> + xsdirxss->streaming = false;
>>> +
>>> + xsdirx_core_enable(core);
>>> +
>>> + dev_info(dev, "probe success\n");
>>> +
>>> + return 0;
>>> +error:
>>> + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
>>> + media_entity_cleanup(&subdev->entity);
>>> + xsdirx_globalintr(core, false);
>>> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
>>> +clk_err:
>>> + clk_bulk_disable_unprepare(core->num_clks, core->clks);
>>> + return ret;
>>> +}
>>> +
>>> +static int xsdirxss_remove(struct platform_device *pdev)
>>> +{
>>> + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
>>> + struct xsdirxss_core *core = &xsdirxss->core;
>>> + struct v4l2_subdev *subdev = &xsdirxss->subdev;
>>> +
>>> + v4l2_async_unregister_subdev(subdev);
>>> + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
>>> + media_entity_cleanup(&subdev->entity);
>>> +
>>> + xsdirx_globalintr(core, false);
>>> + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
>>> + xsdirx_core_disable(core);
>>> + xsdirx_streamflow_control(core, false);
>>> +
>>> + clk_bulk_disable_unprepare(core->num_clks, core->clks);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct of_device_id xsdirxss_of_id_table[] = {
>>> + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
>>> + { }
>>> +};
>>> +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 <[email protected]>");
>>> +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 000000000000..6f2a093968d9
>>> --- /dev/null
>>> +++ b/include/uapi/linux/xilinx-sdirxss.h
>>> @@ -0,0 +1,179 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>> +/*
>>> + * Xilinx SDI Rx Subsystem mode, event, custom timings
>>> + * and flag definitions.
>>> + *
>>> + * Copyright (C) 2019 - 2020 Xilinx, Inc.
>>> + *
>>> + * Contacts: Vishal Sagar <[email protected]>
>>> + */
>>> +
>>> +#ifndef __UAPI_XILINX_SDIRXSS_H__
>>> +#define __UAPI_XILINX_SDIRXSS_H__
>>> +
>>> +#include <linux/types.h>
>>> +#include <linux/v4l2-dv-timings.h>
>>> +#include <linux/videodev2.h>
>>> +
>>> +/*
>>> + * Events
>>> + *
>>> + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
>> underflowed
>>> + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
>> overflowed
>>> + */
>>> +#define V4L2_EVENT_XILINX_SDIRX_CLASS
>> (V4L2_EVENT_PRIVATE_START | 0x200)
>>> +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
>> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
>>> +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
>> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
>>> +
>>> +/*
>>> + * 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
>>> +
>>> +/* Xilinx DV timings not in mainline yet */
>>> +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>> +
>>> +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
>>> + .type = V4L2_DV_BT_656_1120, \
>>> + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
>>> + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
>>> + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
>>> + V4L2_DV_BT_STD_SDI) \
>>> +}
>>
>> Why not add these to v4l2-dv-timings.h? I do need to know on which standard
>> they are based.
>>
>
> Currently these are based off a timing table used in the corresponding bare metal driver.
> I will try to get the standards from which these are based on.
> Meanwhile I hope it is ok to add them as custom timings.

For now, but this really needs to be sorted. The 2048x1080 timings appear to be
DCI Digital Cinema related. 3840x2160p48 is a CTA-861 timing (VIC 114) and
4096x2160p48 is VIC 115. I'm not sure where the 1920x1080i48 timings come from.

The two CTA-derived timings can definitely be added to v4l2-dv-timings.h.

>
>>> +
>>> +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
>>> diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
>> b/include/uapi/linux/xilinx-v4l2-controls.h
>>> index b6441fe705c5..e9de65e82642 100644
>>> --- a/include/uapi/linux/xilinx-v4l2-controls.h
>>> +++ b/include/uapi/linux/xilinx-v4l2-controls.h
>>
>> Why is this in a separate header? It seems to me that it makes more sense to
>> have
>> a single public header for this driver.
>>
>
> I think the xilinx-v4l2-controls.h is created to house all custom controls of Xilinx V4L2 drivers.
> Since the Xilinx Video Test Pattern generator has its controls in the xilinx-v4l2-controls.h,
> I decided to keep the controls in this file and the events in the SDI Rx specific header.

So the TPG controls are shared between different xilinx IP blocks?

Assuming that the controls you add here for your driver are specific to just this
IP block, I would add them to xilinx-sdirxss.h instead.

Looking at xilinx-v4l2-controls.h: it shouldn't set V4L2_CID_XILINX_OFFSET/BASE here,
instead it should reserve the range in v4l2-controls.h. This to ensure CIDs reserved
here do not clash with other CIDs reserved by other drivers.

For the controls specific to this driver I would also reserve your own range in
v4l2-controls.h, rather than taking it from the range reserved by xilinx-v4l2-controls.h.

Regards,

Hans

>
>>> @@ -71,4 +71,71 @@
>>> /* Noise level */
>>> #define V4L2_CID_XILINX_TPG_NOISE_GAIN
>> (V4L2_CID_XILINX_TPG + 17)
>>>
>>> +/*
>>> + * Xilinx SDI Rx Subsystem
>>> + */
>>> +
>>> +/* The base for the sdi rx driver controls.
>>> + * We reserve 32 controls for this driver.
>>> + *
>>> + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
>>> + * EDH is enabled.
>>> + * The controls which can be set should only be set before enabling
>>> + * streaming. The controls which can be got should be called while
>>> + * streaming to get correct values.
>>> + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
>> dv timing
>>> + * returns a valid timing.
>>> + */
>>> +
>>> +#define V4L2_CID_XILINX_SDIRX
>> (V4L2_CID_XILINX_BASE + 0x20)
>>> +
>>> +/* Framer Control to enable or disable the framer */
>>> +#define V4L2_CID_XILINX_SDIRX_FRAMER
>> (V4L2_CID_XILINX_SDIRX + 1)
>>> +/*
>>> + * Video Lock Window Control to set the video lock window value
>>> + * This is the amount of time the mode and transport stream need
>>> + * to be locked before a video lock interrupt occurs.
>>> + */
>>> +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
>> (V4L2_CID_XILINX_SDIRX + 2)
>>> +/* EDH Error Mask Control to enable EDH error count */
>>> +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
>> (V4L2_CID_XILINX_SDIRX + 3)
>>> +/*
>>> + * Mode search Control to pass the bit mask of modes to detect.
>>> + *
>>> + * bit 0 set to detect SD mode,
>>> + * bit 1 set to detect HD mode,
>>> + * bit 2 set to detect 3G (3GA & 3GB) mode,
>>> + * bit 3 set to detect 6G mode,
>>> + * bit 4 set to detect 12G integer frame rate mode,
>>> + * bit 5 set to detect 12G fractional frame rate mode,
>>> + */
>>> +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
>> (V4L2_CID_XILINX_SDIRX + 4)
>>> +/*
>>> + * Get Detected Mode control
>>> + *
>>> + * Control Value - Mode detected
>>> + * 0 - SD
>>> + * 1 - HD
>>> + * 2 - 3G (3GA & 3GB)
>>> + * 3 - 6G
>>> + * 4 - 12G integer frame rate
>>> + * 5 - 12G fractional frame rate
>>> + */
>>> +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
>> (V4L2_CID_XILINX_SDIRX + 5)
>>> +/* Get number of CRC errors status control */
>>> +#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 whether it is interlaced or not */
>>> +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
>> (V4L2_CID_XILINX_SDIRX + 9)
>>> +/* Get number of Active Streams */
>>> +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
>> (V4L2_CID_XILINX_SDIRX + 10)
>>> +/*
>>> + * Get if the detected mode is 3GB.
>>> + * Can be used to distinguished between 3GA and 3GB
>>> + */
>>> +#define V4L2_CID_XILINX_SDIRX_IS_3GB
>> (V4L2_CID_XILINX_SDIRX + 11)
>>> +
>>> #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
>>>
>>
>> Regards,
>>
>> Hans
>
> Regards
> Vishal Sagar
>

2020-06-09 18:28:49

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Hi Hyun,

Thanks for the review.

> -----Original Message-----
> From: Hyun Kwon <[email protected]>
> Sent: Thursday, May 7, 2020 12:13 AM
> To: Vishal Sagar <[email protected]>
> Cc: Hyun Kwon <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected]; Michal
> Simek <[email protected]>; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]; Dinesh Kumar
> <[email protected]>; Sandip Kothari <[email protected]>; Joe Perches
> <[email protected]>; Vishal Sagar <[email protected]>
> Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
>
> Hi Vishal,
>
> Thanks for the patch. Sorry for late reply.
>
> On Wed, 2020-04-29 at 07:17:04 -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 10/12 bpc YUV 422 media bus format currently. 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 IP configuration.
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > v2
> > - Added DV timing support based on Hans Verkuilś feedback
> > - More documentation to custom v4l controls and events
> > - Fixed Hyunś comments
> > - Added macro for masking and shifting as per Joe Perches comments
> > - Updated to latest as per Xilinx github repo driver like
> > adding new DV timings not in mainline yet uptill 03/21/20
> >
> > drivers/media/platform/xilinx/Kconfig | 11 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > 5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -12,6 +12,17 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +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 4cdc0b1ec7a5..6c375f03f587 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -2,6 +2,7 @@
> >
> > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> >
> > +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
>
> This better come after CONFIG_VIDEO_XILINX.
>

Ok I will take care of this in next version.

> > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,2162 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + *
> > + */
> > +
> > +#include <dt-bindings/media/xilinx-vip.h>
> > +#include <linux/bitops.h>
> > +#include <linux/compiler.h>
>
> Is this needed? Maybe unused ones can be removed.
>

Yes most will be removed in the next version.

> > +#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-dv-timings.h>
> > +#include <linux/v4l2-subdev.h>
> > +#include <linux/xilinx-sdirxss.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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12,
> 10)
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> > +
> > +#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_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)
> > +
> > +#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_BYTE1_MASK 0xFF
> > +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> > +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> > +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> > +#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
> > +#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_OFFSET 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_YUV444 0x1
> > +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> > +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> > +
> > +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> > +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> > +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> > +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> > +
> > +#define CLK_INT 148500000UL
>
> A comment for this would be helpful.
>

Ok I will add a comment about this.
"GT input clock for sdi_rx_clk"

> > +
> > +#define rshift_and_mask(val, type) \
> > + (((val) >> type##_OFFSET) & type##_MASK)
> > +
> > +#define mask_and_rshift(val, type) \
> > + (((val) & type##_MASK) >> type##_OFFSET)
> > +
> > +/**
> > + * 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
> > + * @clks: array of clocks
> > + * @num_clks: number of clocks
> > + * @bpc: Bits per component, can be 10 or 12
> > + */
> > +struct xsdirxss_core {
> > + struct device *dev;
> > + void __iomem *iomem;
> > + int irq;
>
> I don't think this has to be stored.
>

Agreed. This will be removed in next version.

> > + bool include_edh;
> > + int mode;
>
> unsigned type?

Agreed. This will be u32 in next version.
>
> > + struct clk_bulk_data *clks;
> > + int num_clks;
>
> unsigned type?
>

Agreed. I will make this u32 in next version. But I don't know why clk apis like devm_clk_bulk_get() use int as data type for num_clks.

> > + u32 bpc;
> > +};
> > +
> > +/**
> > + * 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
> > + * @format: Active V4L2 format on source pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @pad: source media pad
> > + * @vidlockwin: Video lock window value set by control
> > + * @edhmask: EDH mask set by control
> > + * @searchmask: Search mask set by control
> > + * @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.
> > + * @framer_enable: Flag for framer enabled or not set by control
> > + *
> > + * 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 format;
> > + struct v4l2_mbus_framefmt default_format;
> > + struct v4l2_fract frame_interval;
> > + struct media_pad pad;
> > + u32 vidlockwin;
> > + u32 edhmask;
> > + u16 searchmask;
> > + bool streaming;
> > + bool vidlocked;
> > + bool ts_is_interlaced;
> > + bool framer_enable;
> > +};
> > +
> > +/* List of clocks required by UHD-SDI Rx subsystem */
> > +static const char * const xsdirxss_clks[] = {
> > + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> > +};
> > +
> > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> > +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY10_1X20,
> > +};
> > +
> > +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY12_1X24,
> > +};
> > +
> > +static const struct v4l2_dv_timings fmt_cap[] = {
> > + V4L2_DV_BT_SDI_720X487I60,
> > + V4L2_DV_BT_CEA_720X576I50,
> > + V4L2_DV_BT_CEA_1280X720P24,
> > + V4L2_DV_BT_CEA_1280X720P25,
> > + V4L2_DV_BT_CEA_1280X720P30,
> > + V4L2_DV_BT_CEA_1280X720P50,
> > + V4L2_DV_BT_CEA_1280X720P60,
> > + V4L2_DV_BT_CEA_1920X1080P24,
> > + V4L2_DV_BT_CEA_1920X1080P30,
> > + V4L2_DV_BT_CEA_1920X1080I50,
> > + V4L2_DV_BT_CEA_1920X1080I60,
> > + V4L2_DV_BT_CEA_1920X1080P50,
> > + V4L2_DV_BT_CEA_1920X1080P60,
> > + V4L2_DV_BT_CEA_3840X2160P24,
> > + V4L2_DV_BT_CEA_3840X2160P30,
> > + V4L2_DV_BT_CEA_3840X2160P50,
> > + V4L2_DV_BT_CEA_3840X2160P60,
> > + V4L2_DV_BT_CEA_4096X2160P24,
> > + V4L2_DV_BT_CEA_4096X2160P25,
> > + V4L2_DV_BT_CEA_4096X2160P30,
> > + V4L2_DV_BT_CEA_4096X2160P50,
> > + V4L2_DV_BT_CEA_4096X2160P60,
> > +
> > + XLNX_V4L2_DV_BT_2048X1080P24,
> > + XLNX_V4L2_DV_BT_2048X1080P25,
> > + XLNX_V4L2_DV_BT_2048X1080P30,
> > + XLNX_V4L2_DV_BT_2048X1080I48,
> > + XLNX_V4L2_DV_BT_2048X1080I50,
> > + XLNX_V4L2_DV_BT_2048X1080I60,
> > + XLNX_V4L2_DV_BT_2048X1080P48,
> > + XLNX_V4L2_DV_BT_2048X1080P50,
> > + XLNX_V4L2_DV_BT_2048X1080P60,
> > + XLNX_V4L2_DV_BT_1920X1080P48,
> > + XLNX_V4L2_DV_BT_1920X1080I48,
> > + XLNX_V4L2_DV_BT_3840X2160P48,
> > + XLNX_V4L2_DV_BT_4096X2160P48,
> > +};
> > +
> > +struct xsdirxss_dv_map {
> > + u32 width;
> > + u32 height;
> > + u32 fps;
> > + struct v4l2_dv_timings format;
> > +};
> > +
> > +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> > + /* SD - 720x487i60 */
> > + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> > + /* SD - 720x576i50 */
> > + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> > + /* HD - 1280x720p23.98 */
> > + /* HD - 1280x720p24 */
> > + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> > + /* HD - 1280x720p25 */
> > + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> > + /* HD - 1280x720p29.97 */
> > + /* HD - 1280x720p30 */
> > + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> > + /* HD - 1280x720p50 */
> > + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> > + /* HD - 1280x720p59.94 */
> > + /* HD - 1280x720p60 */
> > + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> > + /* HD - 1920x1080p23.98 */
> > + /* HD - 1920x1080p24 */
> > + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> > + /* HD - 1920x1080p25 */
> > + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> > + /* HD - 1920x1080p29.97 */
> > + /* HD - 1920x1080p30 */
> > + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> > +
> > + /* HD - 2048x1080p23.98 */
> > + /* HD - 2048x1080p24 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> > + /* HD - 2048x1080p25 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> > + /* HD - 2048x1080p29.97 */
> > + /* HD - 2048x1080p30 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> > + /* HD - 1920x1080i47.95 */
> > + /* HD - 1920x1080i48 */
> > + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> > +
> > + /* HD - 1920x1080i50 */
> > + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> > + /* HD - 1920x1080i59.94 */
> > + /* HD - 1920x1080i60 */
> > + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> > +
> > + /* HD - 2048x1080i47.95 */
> > + /* HD - 2048x1080i48 */
> > + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> > + /* HD - 2048x1080i50 */
> > + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> > + /* HD - 2048x1080i59.94 */
> > + /* HD - 2048x1080i60 */
> > + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> > + /* 3G - 1920x1080p47.95 */
> > + /* 3G - 1920x1080p48 */
> > + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> > +
> > + /* 3G - 1920x1080p50 148.5 */
> > + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> > + /* 3G - 1920x1080p59.94 148.5/1.001 */
> > + /* 3G - 1920x1080p60 148.5 */
> > + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> > +
> > + /* 3G - 2048x1080p47.95 */
> > + /* 3G - 2048x1080p48 */
> > + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> > + /* 3G - 2048x1080p50 */
> > + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> > + /* 3G - 2048x1080p59.94 */
> > + /* 3G - 2048x1080p60 */
> > + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> > +
> > + /* 6G - 3840X2160p23.98 */
> > + /* 6G - 3840X2160p24 */
> > + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> > + /* 6G - 3840X2160p25 */
> > + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> > + /* 6G - 3840X2160p29.97 */
> > + /* 6G - 3840X2160p30 */
> > + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> > + /* 6G - 4096X2160p23.98 */
> > + /* 6G - 4096X2160p24 */
> > + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> > + /* 6G - 4096X2160p25 */
> > + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> > + /* 6G - 4096X2160p29.97 */
> > + /* 6G - 4096X2160p30 */
> > + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> > + /* 12G - 3840X2160p47.95 */
> > + /* 12G - 3840X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> > +
> > + /* 12G - 3840X2160p50 */
> > + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> > + /* 12G - 3840X2160p59.94 */
> > + /* 12G - 3840X2160p60 */
> > + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> > +
> > + /* 12G - 4096X2160p47.95 */
> > + /* 12G - 4096X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> > +
> > + /* 12G - 4096X2160p50 */
> > + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> > + /* 12G - 4096X2160p59.94 */
> > + /* 12G - 4096X2160p60 */
> > + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> > +};
> > +
> > +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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
> > +{
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static inline 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 val;
> > + struct device *dev = core->dev;
> > +
> > + mask &= XSDIRX_DETECT_ALL_MODES;
> > + if (!mask) {
> > + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> > + return -EINVAL;
> > + }
> > +
> > + dev_dbg(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(dev, "Detect multiple modes\n");
> > +
> > + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > +
> > + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > + } else {
> > + /* Fixed Mode */
> > + u32 forced_mode_mask;
> > +
> > + dev_dbg(dev, "Detect fixed mode\n");
> > +
> > + /* Find offset of first bit set */
> > + switch (__ffs(mask)) {
>
> Isn't it single bit at this point, so __ffs() is not needed?
>

Right. I will modify this in next version.

> > + 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;
> > + default:
> > + forced_mode_mask = 0;
>
> Shouldn't this be an error?
>

Right. I will add an error message "invalid mask for fixed detect mode" here and return -EINVAL.

> > + }
> > + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> > + forced_mode_mask);
> > + val |= forced_mode_mask <<
> XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > + }
> > +
> > + dev_dbg(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);
>
> I'd use {}, but probably up to you.
>

Ok. I will add {}.

> > +}
> > +
> > +static void xsdirx_setedherrcnttrigger(struct xsdirxss_core *core, u32
> enable)
>
> Can this and similar names have some '_'? Maybe the name gets too long. I
> know
> how difficult naming can be. :-)
>

Fixing this in the next version.

> > +{
> > + u32 val = xsdirxss_read(core, XSDIRX_EDH_ERRCNT_EN_REG);
> > +
> > + val = enable & XSDIRX_EDH_ALLERR_MASK;
> > +
>
> Nit. I'd remove this empty line, but up to you.
>

Will remove the empty line here.

> > + xsdirxss_write(core, XSDIRX_EDH_ERRCNT_EN_REG, val);
> > +}
> > +
> > +static inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static inline 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 inline void xsdirx_clearintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_set(core, XSDIRX_ISR_REG, mask);
> > +}
>
> Not sure if these single line wrappers are needed. Inlining would be simpler.
>

Adding function names makes it easier for me to understand rather than set or clear functions.
So I will keep these. If you still feel strongly about removing this, let me know and I can remove them in next version.

> > +
> > +static void xsdirx_vid_bridge_control(struct xsdirxss_core *core,
> > + bool enable)
> > +{
> > + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> > +
> > + /*
> > + * TODO - Enable YUV444/RBG format in the bridge based
> > + * on BYTE3 color format.
> > + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> > + */
> > + if (enable)
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > + else
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, 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);
> > + }
> > +}
>
> Even these wrappers. The if check for enable can be removed when these are
> inlined, hence less operations.
>

Same comment as above.

> > +
> > +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;
> > + }
> > +}
> > +
> > +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> > +{
> > + struct clk *gtclk;
> > + unsigned long clkrate;
> > + int ret, is_frac;
> > + struct xsdirxss_core *core = &state->core;
> > + u32 mode;
> > + static int prev_is_frac = -1;
>
> This would prevent multiple instances. Can it be part of xsdirxss_state?
>
> But I wonder why this previous setting and also being fractional fps
> really matter. Instead, this always has to set to the required rate.
>

Good catch I will move the prev_is_frac to xsdirxss_state.
The prev_is_frac setting helps us confirm if a clock programming is really required or not.
The check will help in changing the clock only when changed clock is required.

> > +
> > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + /*
> > + * TODO: For now, don't change the clock rate for any mode except
> 12G.
> > + * In future, configure gt clock for all modes and enable clock only
> > + * when needed (stream on/off).
> > + */
> > + if (mode != XSDIRX_MODE_12GI_MASK && mode !=
> XSDIRX_MODE_12GF_MASK)
> > + return;
> > +
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
>
> This is related to how frame intervals are defined in xsdirxss_get_framerate(),
> correct? Maybe some comments on both sides would be helpful, saying
> numerator
> == 1001 is treated as fractional fps?
>

Sure. I will add a comment for this.
"When numerator is 1001 then frame rate is fractional else integer"

> > +
> > + if (prev_is_frac == is_frac)
> > + return;
> > +
> > + xsdirx_core_disable(core);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +
> > + /* get sdi_rx_clk */
> > + gtclk = core->clks[1].clk;
> > + clkrate = clk_get_rate(gtclk);
>
> This is not needed.
>
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
>
> Not needed.
>

I will remove both.

> > +
> > + /* calcualte clkrate */
> > + if (!is_frac)
> > + clkrate = CLK_INT;
> > + else
> > + clkrate = (CLK_INT * 1000) / 1001;
> > +
> > + ret = clk_set_rate(gtclk, clkrate);
> > + if (ret)
> > + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
>
> If this puts the controller in non-functional state, better to be an error,
> and stop.
>

There is no stop. The core has to be enabled in driver as there is no way from
userspace to control the SDI Rx core.

> > +
> > + clkrate = clk_get_rate(gtclk);
>
> Shouldn't this rate be checked as set_rate() doesn't guarantee the requested
> rate?
>

The requested clock rate and set clock rate may vary and depend on clocking hardware chip.
The delta may vary. So no check has been put.

> > +
> > + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> > + clkrate, is_frac);
> > +
> > + xsdirx_framer(core, state->framer_enable);
> > + xsdirx_setedherrcnttrigger(core, state->edhmask);
> > + xsdirx_setvidlockwindow(core, state->vidlockwin);
> > + xsdirx_set_modedetect(core, state->searchmask);
> > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_globalintr(core, true);
> > + xsdirx_core_enable(core);
> > +
> > + prev_is_frac = is_frac;
> > +}
> > +
> > +/**
> > + * 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;
> > + struct device *dev = core->dev;
> > + 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->format;
> > + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> > +
> > + 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> > + active_luma = mask_and_rshift(payload,
> > +
> XST352_BYTE3_ACT_LUMA_COUNT);
> > + pic_type = mask_and_rshift(payload,
> XST352_BYTE2_PIC_TYPE);
> > + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> > + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> > + sampling = mask_and_rshift(payload,
> XST352_BYTE3_COLOR_FORMAT);
> > + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> > + } else {
> > + dev_dbg(dev, "No ST352 payload available : Mode = %d\n",
> mode);
> > + framerate = mask_and_rshift(val,
> XSDIRX_TS_DET_STAT_RATE);
> > + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> > + }
> > +
> > + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> > + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> > + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc =
> %d\n",
> > + bpc, core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> > + state->ts_is_interlaced = tscan ? false : true;
> > +
> > + dev_dbg(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(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(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) {
>
> This seems like a little too much nested switch - case and if statements in
> single function. Do you think this can be cleaned up using some table,
> ex using XSDIRX_MODE_*, XSDIRX_TS_DET_STAT_RATE_*, adjusted
> XST352_BYTE1_*,,,
> as indexes to it? Or with separate functions Or is it overkill? I let you
> decide though.
>

Currently we are keeping the same logic in the bare metal driver and in Linux driver.
I will try to do this later.

> > + 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(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(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(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(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;
> > + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(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(dev, "Unknown 12G Mode SMPTE
> standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + dev_err(dev, "Invalid Mode\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (valid) {
> > + if (pic_type)
> > + format->field = V4L2_FIELD_NONE;
> > + else
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + if (format->height == 1080 && pic_type && !tscan)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + /*
> > + * In 3GB DL pSF mode the video is similar to interlaced.
> > + * So though it is a progressive video, its transport is
> > + * interlaced and is sent as two width x (height/2) buffers.
> > + */
> > + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> > + if (state->ts_is_interlaced)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > + else
> > + format->field = V4L2_FIELD_NONE;
> > + }
> > + }
> > +
> > + if (format->field == V4L2_FIELD_ALTERNATE)
> > + format->height = format->height / 2;
> > +
> > + switch (sampling) {
> > + case XST352_BYTE3_COLOR_FORMAT_422:
> > + if (core->bpc == 10)
> > + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> > + break;
> > + case XST352_BYTE3_COLOR_FORMAT_420:
> > + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> > + case XST352_BYTE3_COLOR_FORMAT_GBR:
> > + format->code = 0;
> > + dev_dbg(dev, "No corresponding media bus formats\n");
> > + break;
> > + default:
> > + dev_err(dev, "Unsupported color format : %d\n", sampling);
> > + return -EINVAL;
> > + }
> > +
> > + xsdirxss_get_framerate(&state->frame_interval, framerate);
> > +
> > + dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
> > + state->frame_interval.numerator,
> > + state->frame_interval.denominator);
> > + dev_dbg(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 writing 1 to corresponding bit.
> > + *
> > + * 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;
> > + struct device *dev = core->dev;
> > + u32 status;
> > +
> > + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> > + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
>
> maybe,
> if (!status)
> return IRQ_NONE;
>

Agree. I will add this in next version.

> > +
> > + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> > + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > + u32 val1, val2;
> > +
> > + dev_dbg(dev, "video lock/unlock interrupt\n");
> > +
> > + xsdirx_streamflow_control(core, false);
> > + state->streaming = false;
> > +
> > + 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
> > + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> > +
> > + if (!xsdirx_get_stream_properties(state)) {
> > + state->vidlocked = true;
> > + xsdirxss_set_gtclk(state);
> > + } else {
> > + dev_err(dev, "Unable to get stream
> properties!\n");
> > + state->vidlocked = false;
> > + }
> > + } else {
> > + dev_dbg(dev, "video unlock interrupt\n");
> > + state->vidlocked = false;
> > + }
> > +
> > + 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);
> > + }
> > +
> > + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core underflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core overflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_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_XILINX_SDIRX_UNDERFLOW:
> > + case V4L2_EVENT_XILINX_SDIRX_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);
>
> Having this in the beginning may help catch invalid subcription type too?

Agreed. I will move this to beginning of function.

>
> > + 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;
> > + struct device *dev = core->dev;
> > +
> > + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > + ctrl->id, ctrl->val);
> > +
> > + if (xsdirxss->streaming) {
> > + dev_err(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);
> > + xsdirxss->framer_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > + xsdirx_setvidlockwindow(core, ctrl->val);
> > + xsdirxss->vidlockwin = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
> > + xsdirx_setedherrcnttrigger(core, ctrl->val);
> > + xsdirxss->edhmask = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > + if (ctrl->val) {
> > + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > + dev_dbg(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(dev, "Upto 6G supported\n");
> > + ctrl->val &=
> ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +
> BIT(XSDIRX_MODE_12GF_OFFSET));
> > + }
> > +
> > + ret = xsdirx_set_modedetect(core, ctrl->val);
> > + if (!ret)
> > + xsdirxss->searchmask = ctrl->val;
> > + } else {
>
> This can be done in te begining.
>
> if (!ctrl->val) {
> dev_err();
> return -EINVAL;
> }
>
> if (core->mode ...)
>

Agree I will modify this next time.

> > + dev_err(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);
>
> I realized that the core is enabled all the time. Can it be enabled / disabled
> per stream on / off? Or is it tricky?
>

Yes the core is enabled all the time. We only enable/disable bridges in the s_stream().
This is done so that the core can detect what is the incoming stream properties.
If we don’t have stream properties, the user space won't be able to correctly allocate /
Initialize the pipeline.

> > + 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;
> > + struct device *dev = core->dev;
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(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(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(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(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(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(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(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + dev_dbg(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 i;
> > +
> > + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > + for (i = 0; i < 0x28; i++) {
> > + u32 data;
> > +
> > + 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;
> > +}
> > +
> > +/**
> > + * 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;
> > + struct device *dev = core->dev;
> > +
> > + if (enable) {
> > + if (!xsdirxss->vidlocked) {
> > + dev_dbg(dev, "Video is not locked\n");
>
> dev_err()?
>

Agree I will modify this in next version.

> > + return -EINVAL;
> > + }
> > + if (xsdirxss->streaming) {
> > + dev_dbg(dev, "Already streaming\n");
> > + return -EINVAL;
>
> Is this an error? I think it can just return 0 instead.
>

True. I will update this. I am modifying this according to Laurent's suggestions.

> > + }
> > +
> > + xsdirx_streamflow_control(core, true);
> > + xsdirxss->streaming = true;
> > + dev_dbg(dev, "Streaming started\n");
> > + } else {
> > + if (!xsdirxss->streaming) {
> > + dev_dbg(dev, "Stopped streaming already\n");
> > + return 0;
> > + }
> > +
> > + xsdirx_streamflow_control(core, false);
> > + xsdirxss->streaming = false;
> > + dev_dbg(dev, "Streaming stopped\n");
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_input_status - It is used to determine if the video signal
> > + * is present / locked onto or not.
> > + *
> > + * @sd: V4L2 Sub device
> > + * @status: status of signal locked
> > + *
> > + * This is used to determine if the video signal is present and locked onto
> > + * by the SDI Rx core or not based on vidlocked flag.
> > + *
> > + * Return: zero on success
> > + */
> > +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > + if (!xsdirxss->vidlocked)
> > + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> > + else
> > + *status = 0;
> > +
> > + 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->format;
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +/**
> > + * xsdirxss_init_cfg - Initialise the pad format config to default
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + *
> > + * This function is used to initialize the pad format with the default
> > + * values.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct v4l2_mbus_framefmt *format;
> > +
> > + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> > + *format = xsdirxss->default_format;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * 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 */
>
> Ah, what blocks to implement this? :-)
>

Nothing now. I will be updating this in the next version for width and height too.

> > + fmt->format.code = __format->code;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> > + * @sd: pointer to v4l2 subdev structure
> > + * @cfg: V4L2 subdev pad configuration
> > + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> > + *
> > + * Return: -EINVAL or zero on success
> > + */
> > +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_mbus_code_enum
> *code)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + u32 index = code->index;
> > + u32 maxindex;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> > + else
> > + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> > +
> > + if (code->pad || index >= maxindex)
> > + return -EINVAL;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + code->code = xsdirxss_10bpc_mbus_fmts[index];
> > + else
> > + code->code = xsdirxss_12bpc_mbus_fmts[index];
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -EINVAL incase of invalid index and pad or zero on success
> > + */
> > +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_enum_dv_timings *timings)
> > +{
> > + if (timings->index >= ARRAY_SIZE(fmt_cap))
> > + return -EINVAL;
> > +
> > + if (timings->pad != 0)
> > + return -EINVAL;
> > +
> > + timings->timings = fmt_cap[timings->index];
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_query_dv_timings: Query for the current DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding
> timing
> > + * entry is not found or zero on success.
> > + */
> > +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_dv_timings *timings)
> > +{
> > + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> > + unsigned int i;
> > +
> > + if (!state->vidlocked)
> > + return -ENOLCK;
> > +
> > + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> > + if (state->format.width == xsdirxss_dv_timings[i].width &&
> > + state->format.height == xsdirxss_dv_timings[i].height &&
> > + state->frame_interval.denominator ==
> > + (xsdirxss_dv_timings[i].fps * 1000)) {
> > + *timings = xsdirxss_dv_timings[i].format;
> > + return 0;
> > + }
> > + }
> > +
> > + return -ERANGE;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * 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 const 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 const 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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > + .g_frame_interval = xsdirxss_g_frame_interval,
> > + .s_stream = xsdirxss_s_stream,
> > + .g_input_status = xsdirxss_g_input_status,
> > + .query_dv_timings = xsdirxss_query_dv_timings,
> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > + .init_cfg = xsdirxss_init_cfg,
> > + .get_fmt = xsdirxss_get_format,
> > + .set_fmt = xsdirxss_set_format,
> > + .enum_mbus_code = xsdirxss_enum_mbus_code,
> > + .enum_dv_timings = xsdirxss_enum_dv_timings,
> > +};
> > +
> > +static const struct v4l2_subdev_ops xsdirxss_ops = {
> > + .core = &xsdirxss_core_ops,
> > + .video = &xsdirxss_video_ops,
> > + .pad = &xsdirxss_pad_ops
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > +{
> > + struct device_node *node = xsdirxss->core.dev->of_node;
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > + struct fwnode_handle *ep, *rep;
> > + int ret;
> > + const char *sdi_std;
> > +
> > + core->include_edh = of_property_read_bool(node, "xlnx,include-
> edh");
> > + dev_dbg(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(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(dev, "Invalid Line Rate\n");
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > + core->mode);
> > +
> > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
>
> But, the bpp and bpc are quite different. Shouldn't it be bpc?
>

Yes this is a mistake. I will rectify this in next version.


> > + if (ret < 0) {
> > + dev_err(dev, "failed to get xlnx,bpp\n");
> > + return ret;
> > + }
> > +
> > + if (core->bpc != 10 && core->bpc != 12) {
> > + dev_err(dev, "bits per component=%u. Can be 10 or 12
> only\n",
> > + core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > +
> FWNODE_GRAPH_ENDPOINT_NEXT);
> > + if (!ep) {
> > + dev_err(dev, "no source port found");
> > + ret = -EINVAL;
> > + goto dt_parse_done;
> > + }
> > +
> > + rep = fwnode_graph_get_remote_endpoint(ep);
> > + if (!rep) {
> > + dev_err(dev, "no remote sink endpoint found");
> > + ret = -EINVAL;
> > + }
> > +
> > + fwnode_handle_put(rep);
> > +dt_parse_done:
> > + fwnode_handle_put(ep);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_probe(struct platform_device *pdev)
> > +{
> > + struct v4l2_subdev *subdev;
> > + struct xsdirxss_state *xsdirxss;
> > + struct xsdirxss_core *core;
> > + struct device *dev;
> > + 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;
> > + dev = core->dev;
> > +
> > + /* Register interrupt handler */
> > + core->irq = platform_get_irq(pdev, 0);
>
> As mentioned, this doesn't have to be stored.
>

Yes this will be removed in next version.

> > + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> > + xsdirxss_irq_handler, IRQF_ONESHOT,
> > + dev_name(dev), xsdirxss);
> > + if (ret) {
> > + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> > + core->clks = devm_kcalloc(dev, core->num_clks,
> > + sizeof(*core->clks), GFP_KERNEL);
> > + if (!core->clks)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < core->num_clks; i++)
> > + core->clks[i].id = xsdirxss_clks[i];
> > +
> > + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = xsdirxss_parse_of(xsdirxss);
> > + if (ret < 0)
> > + goto clk_err;
> > +
> > + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(core->iomem)) {
> > + ret = PTR_ERR(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->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +
> > + /* Initialize the default format */
> > + if (core->bpc == 10)
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY12_1X24;
> > + 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->format = xsdirxss->default_format;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + subdev = &xsdirxss->subdev;
> > + v4l2_subdev_init(subdev, &xsdirxss_ops);
> > +
> > + subdev->dev = &pdev->dev;
> > + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name));
> > +
> > + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS |
> V4L2_SUBDEV_FL_HAS_DEVNODE;
>
> I guess it doesn't matter, but this can be assigned directly to be clearer, not
> ORed.
>

I will fix this in next version.

> > +
> > + subdev->entity.ops = &xsdirxss_media_ops;
> > +
> > + v4l2_set_subdevdata(subdev, xsdirxss);
> > +
> > + ret = media_entity_pads_init(&subdev->entity, 1, &xsdirxss->pad);
> > + if (ret < 0)
> > + goto error;
> > +
> > + /* Initialise and register the controls */
> > + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > +
> > + if (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(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(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > +
> > + if (core->include_edh) {
> > + for (i = 0; i < num_edh_ctrls; i++) {
> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_edh_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > + }
> > +
> > + if (xsdirxss->ctrl_handler.error) {
> > + dev_err(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(dev, "failed to set controls\n");
> > + goto error;
> > + }
> > +
> > + platform_set_drvdata(pdev, xsdirxss);
> > +
> > + ret = v4l2_async_register_subdev(subdev);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to register subdev\n");
> > + goto error;
> > + }
> > +
> > + xsdirxss->streaming = false;
>
> Is this needed?
>

I think it is better to explicitly initialize these state variables.

> > +
> > + xsdirx_core_enable(core);
> > +
> > + dev_info(dev, "probe success\n");
> > +
> > + return 0;
> > +error:
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +clk_err:
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > + v4l2_async_unregister_subdev(subdev);
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > +
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_core_disable(core);
>
> I believe the core has to be disabled first.
>

Doesn't matter but ok I will put it above.

> > + xsdirx_streamflow_control(core, false);
>
> But to be safer side, maybe all these can be done first? Similar for enable,
> so enabling all after v4l/media registrations?
>

I will test and update if working in next version.

> Thanks,
> -hyun
>
> > +
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> > + { }
> > +};
> > +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 <[email protected]>");
> > +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 000000000000..6f2a093968d9
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,179 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode, event, custom timings
>
> Nit. ',' at the end.
>

The "," is not needed as it follows to "and".

> > + * and flag definitions.
> > + *
> > + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * Events
> > + *
> > + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
> underflowed
> > + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
> overflowed
> > + */
> > +#define V4L2_EVENT_XILINX_SDIRX_CLASS
> (V4L2_EVENT_PRIVATE_START | 0x200)
> > +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> > +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
>
> Since this is many definitions for a single subdev, do you think some of these
> can be minimized? For example, if the action for underflow/overflow isn't
> different, resetting the device, then these can fold into one event.
>

I will join this overflow/underflow into a single event.

>
> Could you double-check some below too? Unless each is associated with
> different
> handling, it'd be good to minimize the user interface, ex by merging or even
> removing.
>

The controls are added to give user the flexibility to get custom info like EDH error count, CRC errors, etc.
So I would like to go ahead with them.

> Thanks,
> -hyun
>
> > +
> > +/*
> > + * 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
> > +
> > +/* Xilinx DV timings not in mainline yet */
> > +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index b6441fe705c5..e9de65e82642 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -71,4 +71,71 @@
> > /* Noise level */
> > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> (V4L2_CID_XILINX_TPG + 17)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* The base for the sdi rx driver controls.
> > + * We reserve 32 controls for this driver.
> > + *
> > + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> > + * EDH is enabled.
> > + * The controls which can be set should only be set before enabling
> > + * streaming. The controls which can be got should be called while
> > + * streaming to get correct values.
> > + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
> dv timing
> > + * returns a valid timing.
> > + */
> > +
> > +#define V4L2_CID_XILINX_SDIRX
> (V4L2_CID_XILINX_BASE + 0x20)
> > +
> > +/* Framer Control to enable or disable the framer */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER
> (V4L2_CID_XILINX_SDIRX + 1)
> > +/*
> > + * Video Lock Window Control to set the video lock window value
> > + * This is the amount of time the mode and transport stream need
> > + * to be locked before a video lock interrupt occurs.
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> (V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control to enable EDH error count */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> (V4L2_CID_XILINX_SDIRX + 3)
> > +/*
> > + * Mode search Control to pass the bit mask of modes to detect.
> > + *
> > + * bit 0 set to detect SD mode,
> > + * bit 1 set to detect HD mode,
> > + * bit 2 set to detect 3G (3GA & 3GB) mode,
> > + * bit 3 set to detect 6G mode,
> > + * bit 4 set to detect 12G integer frame rate mode,
> > + * bit 5 set to detect 12G fractional frame rate mode,
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> (V4L2_CID_XILINX_SDIRX + 4)
> > +/*
> > + * Get Detected Mode control
> > + *
> > + * Control Value - Mode detected
> > + * 0 - SD
> > + * 1 - HD
> > + * 2 - 3G (3GA & 3GB)
> > + * 3 - 6G
> > + * 4 - 12G integer frame rate
> > + * 5 - 12G fractional frame rate
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
> (V4L2_CID_XILINX_SDIRX + 5)
> > +/* Get number of CRC errors status control */
> > +#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 whether it is interlaced or not */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> (V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get number of Active Streams */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> (V4L2_CID_XILINX_SDIRX + 10)
> > +/*
> > + * Get if the detected mode is 3GB.
> > + * Can be used to distinguished between 3GA and 3GB
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB
> (V4L2_CID_XILINX_SDIRX + 11)
> > +
> > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
> > --
> > 2.21.0
> >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > [email protected]
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> >

Regards
Vishal Sagar

2020-06-09 18:29:55

by Vishal Sagar

[permalink] [raw]
Subject: RE: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Hi Laurent

Thanks for the review.

> -----Original Message-----
> From: Laurent Pinchart <[email protected]>
> Sent: Wednesday, May 6, 2020 8:42 PM
> To: Vishal Sagar <[email protected]>
> Cc: Hyun Kwon <[email protected]>; [email protected];
> [email protected]; [email protected]; Michal Simek
> <[email protected]>; [email protected];
> [email protected]; [email protected]; linux-arm-
> [email protected]; [email protected]; Dinesh Kumar
> <[email protected]>; Sandip Kothari <[email protected]>; Joe Perches
> <[email protected]>
> Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem
> driver
>
> Hi Vishal,
>
> Thank you for the patch.
>
> There are a few questions for Hans below.
>
> On Wed, Apr 29, 2020 at 07:47:04PM +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 10/12 bpc YUV 422 media bus format currently. 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.
>
> As commented on patch 1/2, I don't see a mention of 12 bpc in the
> documentation. Let's discuss it as part of patch 1/2.
>

Ok.

> > The driver currently supports only the AXI4-Stream IP configuration.
> >
> > Signed-off-by: Vishal Sagar <[email protected]>
> > ---
> > v2
> > - Added DV timing support based on Hans Verkuilś feedback
> > - More documentation to custom v4l controls and events
> > - Fixed Hyunś comments
> > - Added macro for masking and shifting as per Joe Perches comments
> > - Updated to latest as per Xilinx github repo driver like
> > adding new DV timings not in mainline yet uptill 03/21/20
> >
> > drivers/media/platform/xilinx/Kconfig | 11 +
> > drivers/media/platform/xilinx/Makefile | 1 +
> > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > 5 files changed, 2420 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 01c96fb66414..77091318a9c9 100644
> > --- a/drivers/media/platform/xilinx/Kconfig
> > +++ b/drivers/media/platform/xilinx/Kconfig
> > @@ -12,6 +12,17 @@ config VIDEO_XILINX
> >
> > if VIDEO_XILINX
> >
> > +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
>
> s/comprises of/comprises/
>

Ok I will fix this in v3.

> > + 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 4cdc0b1ec7a5..6c375f03f587 100644
> > --- a/drivers/media/platform/xilinx/Makefile
> > +++ b/drivers/media/platform/xilinx/Makefile
> > @@ -2,6 +2,7 @@
> >
> > xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o
> >
> > +obj-$(CONFIG_VIDEO_XILINX_SDIRXSS) += xilinx-sdirxss.o
> > obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.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 000000000000..c536ea3aaa0d
> > --- /dev/null
> > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > @@ -0,0 +1,2162 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Driver for Xilinx SDI Rx Subsystem
> > + *
> > + * Copyright (C) 2017 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + *
>
> You can remove this blank line.
>
Agree. I will remove this in v3.

> > + */
> > +
> > +#include <dt-bindings/media/xilinx-vip.h>
> > +#include <linux/bitops.h>
> > +#include <linux/compiler.h>
> > +#include <linux/clk.h>
>
> You nearly got the alphabetical order right ;-)
>
> > +#include <linux/delay.h>
>
> No use of delay or sleep functions in the code, is this needed ?
>
> > +#include <linux/device.h>
>
> You can probably drop this one as you include platform_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>
>
> I don't think of_irq is needed either.
>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/spinlock_types.h>
>
> The driver doesn't seems to use spinlocks, you can remove those two
> headers.
>
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/v4l2-subdev.h>
> > +#include <linux/xilinx-sdirxss.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"
> > +

I will fix the headers list by removing unnecessary includes

> > +/*
> > + * 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
>
> Could you use lower-case hex values ? That's the most common option in
> kernel code. If you use vim,
>
> :%s/\<0x[0-9A-F]\+\>/\L\0/
>
> I'm sure there's an emacs equivalent :-)
>
[Vishal Sagar]

Sure I will change this. Thanks for sharing the regex to do that.
I use vim ????

> > +#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_RST_CTRL_BRIDGE_CH_FMT_OFFSET 10
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_MASK GENMASK(12,
> 10)
> > +#define XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444 1
> > +
> > +#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_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)
> > +
> > +#define XSDIRX_DEFAULT_WIDTH (1920)
>
> No need for parentheses around integer values in macros.
>

I will remove these in next version.

> > +#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
>
> Should these three constants be placed in an enum ? The
> xsdirxss_core.mode field would then become an enum.
>

I will get core->mode directly from dt-bindings. So enum type won't be necessary.

> > +
> > +#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)
>
> Do we really need such a high number ? How often are the overflow and
> underflow expected ?
>

An overflow or underflow events will occur when a resolution / rate change occurs.
But since we are stopping the bridges in irq handler, these events will stop coming.
I can reduce this to 8 events but it is arbitrary. Is that ok?

> > +
> > +/* ST352 related macros */
> > +#define XST352_PAYLOAD_BYTE1_MASK 0xFF
> > +#define XST352_PAYLOAD_BYTE1_OFFSET 0
> > +#define XST352_PAYLOAD_BYTE2_OFFSET 8
> > +#define XST352_PAYLOAD_BYTE3_OFFSET 16
> > +#define XST352_PAYLOAD_BYTE4_OFFSET 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_2_1080L_6G 0xC1
> > +#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_OFFSET 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_YUV444 0x1
> > +#define XST352_BYTE3_COLOR_FORMAT_420 0x3
> > +#define XST352_BYTE3_COLOR_FORMAT_GBR 0x2
> > +
> > +#define XST352_BYTE4_BIT_DEPTH_MASK GENMASK(25, 24)
> > +#define XST352_BYTE4_BIT_DEPTH_OFFSET 24
> > +#define XST352_BYTE4_BIT_DEPTH_10 0x1
> > +#define XST352_BYTE4_BIT_DEPTH_12 0x2
> > +
> > +#define CLK_INT 148500000UL
> > +
> > +#define rshift_and_mask(val, type) \
> > + (((val) >> type##_OFFSET) & type##_MASK)
> > +
> > +#define mask_and_rshift(val, type) \
> > + (((val) & type##_MASK) >> type##_OFFSET)
>
> How about using FIELD_GET() from linux/bitfield.h ? It's more standard,
> and will thus be less confusing to readers.
>

Ok I will try to convert this to FIELD_GET()

> > +
> > +/**
> > + * 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
> > +};
>
> This should ideally go in a header file shared between the SDI RX and
> TX, as it's part of the UHD-SDI IP core, but it can be done later.
>

Right. We can look into this later.

> > +
> > +/**
> > + * 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
> > + * @clks: array of clocks
> > + * @num_clks: number of clocks
> > + * @bpc: Bits per component, can be 10 or 12
> > + */
> > +struct xsdirxss_core {
> > + struct device *dev;
> > + void __iomem *iomem;
> > + int irq;
> > + bool include_edh;
> > + int mode;
> > + struct clk_bulk_data *clks;
> > + int num_clks;
>
> num_clks is never negative, you can make it an unsigned int.

Ok. I will make num_clks as unsigned int.
So why devm_clk_bulk_get() and clk_bulk_prepare_enable() take in num_clks argument as int ?

>
> > + u32 bpc;
> > +};
> > +
> > +/**
> > + * 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
> > + * @format: Active V4L2 format on source pad
> > + * @default_format: default V4L2 media bus format
> > + * @frame_interval: Captures the frame rate
> > + * @pad: source media pad
> > + * @vidlockwin: Video lock window value set by control
> > + * @edhmask: EDH mask set by control
> > + * @searchmask: Search mask set by control
> > + * @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.
> > + * @framer_enable: Flag for framer enabled or not set by control
> > + *
> > + * 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;
>
> Is this needed, can't it be a local variable in the function where it is
> used ?

Yes this will be removed in next version and made a local variable in the irq handler.

>
> > + struct v4l2_mbus_framefmt format;
> > + struct v4l2_mbus_framefmt default_format;
> > + struct v4l2_fract frame_interval;
> > + struct media_pad pad;
>
> You will need two pads as there should be two ports (let's discuss it as
> part of patch 1/2).
>

Ok

> > + u32 vidlockwin;
> > + u32 edhmask;
> > + u16 searchmask;
> > + bool streaming;
> > + bool vidlocked;
> > + bool ts_is_interlaced;
> > + bool framer_enable;
> > +};
>
> As for the CSI2-RX, splitting core and state in separate structures
> isn't an established practice. I think it leads to more complex code as
> you have to remember in which structure each field is located.
>

I will merge the core and state into a single structure similar to CSI2-Rx case.

> > +
> > +/* List of clocks required by UHD-SDI Rx subsystem */
> > +static const char * const xsdirxss_clks[] = {
> > + "s_axi_aclk", "sdi_rx_clk", "video_out_clk",
> > +};
> > +
> > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
>
> s/RBG/RGB/ ?
>

No RBG is correct because as per Xilinx User Guide 934 Figure 1-4 and Table 1-4,
the Blue is middle component in the AXI4-Stream protocol followed by all Xilinx Video IPs.
https://www.xilinx.com/support/documentation/ip_documentation/axi_videoip/v1_0/ug934_axi_videoIP.pdf

We will add the RBG 10 and 12 bpc formats later.

> > +static const u32 xsdirxss_10bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY10_1X20,
> > +};
> > +
> > +static const u32 xsdirxss_12bpc_mbus_fmts[] = {
> > + MEDIA_BUS_FMT_UYVY12_1X24,
> > +};
> > +
> > +static const struct v4l2_dv_timings fmt_cap[] = {
> > + V4L2_DV_BT_SDI_720X487I60,
> > + V4L2_DV_BT_CEA_720X576I50,
> > + V4L2_DV_BT_CEA_1280X720P24,
> > + V4L2_DV_BT_CEA_1280X720P25,
> > + V4L2_DV_BT_CEA_1280X720P30,
> > + V4L2_DV_BT_CEA_1280X720P50,
> > + V4L2_DV_BT_CEA_1280X720P60,
> > + V4L2_DV_BT_CEA_1920X1080P24,
> > + V4L2_DV_BT_CEA_1920X1080P30,
> > + V4L2_DV_BT_CEA_1920X1080I50,
> > + V4L2_DV_BT_CEA_1920X1080I60,
> > + V4L2_DV_BT_CEA_1920X1080P50,
> > + V4L2_DV_BT_CEA_1920X1080P60,
> > + V4L2_DV_BT_CEA_3840X2160P24,
> > + V4L2_DV_BT_CEA_3840X2160P30,
> > + V4L2_DV_BT_CEA_3840X2160P50,
> > + V4L2_DV_BT_CEA_3840X2160P60,
> > + V4L2_DV_BT_CEA_4096X2160P24,
> > + V4L2_DV_BT_CEA_4096X2160P25,
> > + V4L2_DV_BT_CEA_4096X2160P30,
> > + V4L2_DV_BT_CEA_4096X2160P50,
> > + V4L2_DV_BT_CEA_4096X2160P60,
> > +
> > + XLNX_V4L2_DV_BT_2048X1080P24,
> > + XLNX_V4L2_DV_BT_2048X1080P25,
> > + XLNX_V4L2_DV_BT_2048X1080P30,
> > + XLNX_V4L2_DV_BT_2048X1080I48,
> > + XLNX_V4L2_DV_BT_2048X1080I50,
> > + XLNX_V4L2_DV_BT_2048X1080I60,
> > + XLNX_V4L2_DV_BT_2048X1080P48,
> > + XLNX_V4L2_DV_BT_2048X1080P50,
> > + XLNX_V4L2_DV_BT_2048X1080P60,
> > + XLNX_V4L2_DV_BT_1920X1080P48,
> > + XLNX_V4L2_DV_BT_1920X1080I48,
> > + XLNX_V4L2_DV_BT_3840X2160P48,
> > + XLNX_V4L2_DV_BT_4096X2160P48,
> > +};
> > +
> > +struct xsdirxss_dv_map {
> > + u32 width;
> > + u32 height;
> > + u32 fps;
> > + struct v4l2_dv_timings format;
> > +};
> > +
> > +static const struct xsdirxss_dv_map xsdirxss_dv_timings[] = {
> > + /* SD - 720x487i60 */
> > + { 720, 243, 30, V4L2_DV_BT_SDI_720X487I60 },
> > + /* SD - 720x576i50 */
> > + { 720, 288, 25, V4L2_DV_BT_CEA_720X576I50 },
> > + /* HD - 1280x720p23.98 */
> > + /* HD - 1280x720p24 */
> > + { 1280, 720, 24, V4L2_DV_BT_CEA_1280X720P24 },
> > + /* HD - 1280x720p25 */
> > + { 1280, 720, 25, V4L2_DV_BT_CEA_1280X720P25 },
> > + /* HD - 1280x720p29.97 */
> > + /* HD - 1280x720p30 */
> > + { 1280, 720, 30, V4L2_DV_BT_CEA_1280X720P30 },
> > + /* HD - 1280x720p50 */
> > + { 1280, 720, 50, V4L2_DV_BT_CEA_1280X720P50 },
> > + /* HD - 1280x720p59.94 */
> > + /* HD - 1280x720p60 */
> > + { 1280, 720, 60, V4L2_DV_BT_CEA_1280X720P60 },
> > + /* HD - 1920x1080p23.98 */
> > + /* HD - 1920x1080p24 */
> > + { 1920, 1080, 24, V4L2_DV_BT_CEA_1920X1080P24 },
> > + /* HD - 1920x1080p25 */
> > + { 1920, 1080, 25, V4L2_DV_BT_CEA_1920X1080P25 },
> > + /* HD - 1920x1080p29.97 */
> > + /* HD - 1920x1080p30 */
> > + { 1920, 1080, 30, V4L2_DV_BT_CEA_1920X1080P30 },
> > +
> > + /* HD - 2048x1080p23.98 */
> > + /* HD - 2048x1080p24 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P24 },
> > + /* HD - 2048x1080p25 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P25 },
> > + /* HD - 2048x1080p29.97 */
> > + /* HD - 2048x1080p30 */
> > + { 2048, 1080, 24, XLNX_V4L2_DV_BT_2048X1080P30 },
> > + /* HD - 1920x1080i47.95 */
> > + /* HD - 1920x1080i48 */
> > + { 1920, 540, 24, XLNX_V4L2_DV_BT_1920X1080I48 },
> > +
> > + /* HD - 1920x1080i50 */
> > + { 1920, 540, 25, V4L2_DV_BT_CEA_1920X1080I50 },
> > + /* HD - 1920x1080i59.94 */
> > + /* HD - 1920x1080i60 */
> > + { 1920, 540, 30, V4L2_DV_BT_CEA_1920X1080I60 },
> > +
> > + /* HD - 2048x1080i47.95 */
> > + /* HD - 2048x1080i48 */
> > + { 2048, 540, 24, XLNX_V4L2_DV_BT_2048X1080I48 },
> > + /* HD - 2048x1080i50 */
> > + { 2048, 540, 25, XLNX_V4L2_DV_BT_2048X1080I50 },
> > + /* HD - 2048x1080i59.94 */
> > + /* HD - 2048x1080i60 */
> > + { 2048, 540, 30, XLNX_V4L2_DV_BT_2048X1080I60 },
> > + /* 3G - 1920x1080p47.95 */
> > + /* 3G - 1920x1080p48 */
> > + { 1920, 1080, 48, XLNX_V4L2_DV_BT_1920X1080P48 },
> > +
> > + /* 3G - 1920x1080p50 148.5 */
> > + { 1920, 1080, 50, V4L2_DV_BT_CEA_1920X1080P50 },
> > + /* 3G - 1920x1080p59.94 148.5/1.001 */
> > + /* 3G - 1920x1080p60 148.5 */
> > + { 1920, 1080, 60, V4L2_DV_BT_CEA_1920X1080P60 },
> > +
> > + /* 3G - 2048x1080p47.95 */
> > + /* 3G - 2048x1080p48 */
> > + { 2048, 1080, 48, XLNX_V4L2_DV_BT_2048X1080P48 },
> > + /* 3G - 2048x1080p50 */
> > + { 2048, 1080, 50, XLNX_V4L2_DV_BT_2048X1080P50 },
> > + /* 3G - 2048x1080p59.94 */
> > + /* 3G - 2048x1080p60 */
> > + { 2048, 1080, 60, XLNX_V4L2_DV_BT_2048X1080P60 },
> > +
> > + /* 6G - 3840X2160p23.98 */
> > + /* 6G - 3840X2160p24 */
> > + { 3840, 2160, 24, V4L2_DV_BT_CEA_3840X2160P24 },
> > + /* 6G - 3840X2160p25 */
> > + { 3840, 2160, 25, V4L2_DV_BT_CEA_3840X2160P25 },
> > + /* 6G - 3840X2160p29.97 */
> > + /* 6G - 3840X2160p30 */
> > + { 3840, 2160, 30, V4L2_DV_BT_CEA_3840X2160P30 },
> > + /* 6G - 4096X2160p23.98 */
> > + /* 6G - 4096X2160p24 */
> > + { 4096, 2160, 24, V4L2_DV_BT_CEA_4096X2160P24 },
> > + /* 6G - 4096X2160p25 */
> > + { 4096, 2160, 25, V4L2_DV_BT_CEA_4096X2160P25 },
> > + /* 6G - 4096X2160p29.97 */
> > + /* 6G - 4096X2160p30 */
> > + { 4096, 2160, 30, V4L2_DV_BT_CEA_4096X2160P30 },
> > + /* 12G - 3840X2160p47.95 */
> > + /* 12G - 3840X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_3840X2160P48 },
> > +
> > + /* 12G - 3840X2160p50 */
> > + { 3840, 2160, 50, V4L2_DV_BT_CEA_3840X2160P50 },
> > + /* 12G - 3840X2160p59.94 */
> > + /* 12G - 3840X2160p60 */
> > + { 3840, 2160, 60, V4L2_DV_BT_CEA_3840X2160P60 },
> > +
> > + /* 12G - 4096X2160p47.95 */
> > + /* 12G - 4096X2160p48 */
> > + { 3840, 2160, 48, XLNX_V4L2_DV_BT_4096X2160P48 },
> > +
> > + /* 12G - 4096X2160p50 */
> > + { 4096, 2160, 50, V4L2_DV_BT_CEA_4096X2160P50 },
> > + /* 12G - 4096X2160p59.94 */
> > + /* 12G - 4096X2160p60 */
> > + { 4096, 2160, 60, V4L2_DV_BT_CEA_4096X2160P60 },
> > +};
> > +
> > +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 inline void xsdirx_core_disable(struct xsdirxss_core *core)
> > +{
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG,
> XSDIRX_RST_CTRL_SS_EN_MASK);
> > +}
> > +
> > +static inline 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 val;
> > + struct device *dev = core->dev;
> > +
> > + mask &= XSDIRX_DETECT_ALL_MODES;
> > + if (!mask) {
> > + dev_err(dev, "Invalid bit mask = 0x%08x\n", mask);
> > + return -EINVAL;
> > + }
> > +
> > + dev_dbg(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(dev, "Detect multiple modes\n");
> > +
> > + if (mask & BIT(XSDIRX_MODE_SD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_SD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_HD_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_HD_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_3G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_3G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_6G_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_6G_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GI_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GI_EN_MASK;
> > + if (mask & BIT(XSDIRX_MODE_12GF_OFFSET))
> > + val |= XSDIRX_MDL_CTRL_MODE_12GF_EN_MASK;
> > +
> > + val |= XSDIRX_MDL_CTRL_MODE_DET_EN_MASK;
> > + } else {
> > + /* Fixed Mode */
> > + u32 forced_mode_mask;
> > +
> > + dev_dbg(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;
> > + default:
> > + forced_mode_mask = 0;
> > + }
> > + dev_dbg(dev, "Forced Mode Mask : 0x%x\n",
> > + forced_mode_mask);
> > + val |= forced_mode_mask <<
> XSDIRX_MDL_CTRL_FORCED_MODE_OFFSET;
> > + }
> > +
> > + dev_dbg(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)
>
> That's a hard to read function name. Could you separate words with
> underscores ? Same for functions below that concatenate multiple words
> together.
>

Ok. I will change the naming for this function and others below in next version.

> > +{
> > + 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 inline 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 inline void xsdirx_disableintr(struct xsdirxss_core *core, u32 mask)
> > +{
> > + xsdirxss_clr(core, XSDIRX_IER_REG, mask);
> > +}
> > +
> > +static inline 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 inline 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)
> > +{
> > + u32 mask = XSDIRX_RST_CTRL_SDIRX_BRIDGE_ENB_MASK;
> > +
> > + /*
> > + * TODO - Enable YUV444/RBG format in the bridge based
> > + * on BYTE3 color format.
> > + * XSDIRX_RST_CTRL_BRIDGE_CH_FMT_YUV444
> > + */
> > + if (enable)
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG, mask);
> > + else
> > + xsdirxss_clr(core, XSDIRX_RST_CTRL_REG, mask);
> > +}
>
> This is only used in xsdirx_streamflow_control(), inlining it manually
> would be simpler I think.
>

Agree. I will update this in the next version.

> > +
> > +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);
> > +}
>
> Same for this one.
>

Agree. I will update this in the next version.

> > +
> > +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 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;
> > + }
> > +}
> > +
> > +static void xsdirxss_set_gtclk(struct xsdirxss_state *state)
> > +{
> > + struct clk *gtclk;
> > + unsigned long clkrate;
> > + int ret, is_frac;
> > + struct xsdirxss_core *core = &state->core;
> > + u32 mode;
> > + static int prev_is_frac = -1;
> > +
> > + mode = xsdirxss_read(core, XSDIRX_MODE_DET_STAT_REG);
> > + mode &= XSDIRX_MODE_DET_STAT_RX_MODE_MASK;
> > +
> > + /*
> > + * TODO: For now, don't change the clock rate for any mode except
> 12G.
> > + * In future, configure gt clock for all modes and enable clock only
> > + * when needed (stream on/off).
> > + */
> > + if (mode != XSDIRX_MODE_12GI_MASK && mode !=
> XSDIRX_MODE_12GF_MASK)
> > + return;
> > +
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + if (prev_is_frac == is_frac)
> > + return;
> > +
> > + xsdirx_core_disable(core);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +
> > + /* get sdi_rx_clk */
> > + gtclk = core->clks[1].clk;
> > + clkrate = clk_get_rate(gtclk);
> > + is_frac = state->frame_interval.numerator == 1001 ? 1 : 0;
> > +
> > + /* calcualte clkrate */
>
> s/calcualte/calculate/

Will fix this in next version.

>
> > + if (!is_frac)
> > + clkrate = CLK_INT;
> > + else
> > + clkrate = (CLK_INT * 1000) / 1001;
>
> Shouldn't the clock rate also depend on the mode ? Table 3-2 lists
> different clock rates for HD-SDI and 12G-SDI.
>

There is a GT (gigabit transceiver PHY) IP which takes this clock from the onboard clock source like Si570
and generates the sdi_rx_clk required as per mode.
As mentioned in above comment, for all modes except 12G mode, the clock need not be changed from 148.5 MHz.
The GT block is capable of detecting integer as well as fractional frame rates in this case.
For 12G integer / fractional mode the clock needs to be 148.5 MHz / 148.5/1.001 MHz to correctly deliver the video.
Hence after detecting the mode(12G integer/fractional), the clock source is changed accordingly.

> > +
> > + ret = clk_set_rate(gtclk, clkrate);
> > + if (ret)
> > + dev_err(core->dev, "failed to set clk rate = %d\n", ret);
> > +
> > + clkrate = clk_get_rate(gtclk);
> > +
> > + dev_dbg(core->dev, "clkrate = %lu is_frac = %d\n",
> > + clkrate, is_frac);
> > +
> > + xsdirx_framer(core, state->framer_enable);
>
> As this is called in both the IRQ handler and the s_ctrl handler, you
> could have a race when accessing the XSDIRX_MDL_CTRL_REG register. You
> will need to protect all accesses to registers that are written from
> both the IRQ handler and userspace contexts with a spinlock.
>

Agree. This was missed. I will add the necessary spinlock.

> > + xsdirx_setedherrcnttrigger(core, state->edhmask);
> > + xsdirx_setvidlockwindow(core, state->vidlockwin);
> > + xsdirx_set_modedetect(core, state->searchmask);
> > + xsdirx_enableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_globalintr(core, true);
> > + xsdirx_core_enable(core);
> > +
> > + prev_is_frac = is_frac;
>
> That's lots of work done in an interrupt handler. As this function is
> called when a stream lock is detected, if the clock rate has to be
> changed, do I assume correctly that the resolution may have changed too
> ? That may require reconfiguring the pipeline and reallocating buffers.
> With SD and HD TV, I think the usual practice is to notify userspace of
> video lock and let it do the reconfiguration is required, instead of
> attempting to do so automatically. Hans, is that correct ? How would we
> handle then the case where the pipeline is already correctly configured
> with big enough buffers (I'm not sure this can actually happen, for the
> buffers yes, but for the pipeline configuration, maybe the format will
> always need to change) ?
>
> And I've now seen that we do emit the V4L2_EVENT_SRC_CH_RESOLUTION
> event
> :-) Do we thus need to reconfigure the IP core here ?
>

Yes we need to reconfigure the IP core and enable it here so that the core is running
but the bridges are disabled. With the SDI Rx core enabled, we will get to know when a new stream has started and its properties.
The application will be notified of this and it can query for the stream properties to accordingly setup the pipeline.
With the bridges disabled, the downstream IPs will not be able to receive any data.

> > +}
> > +
> > +/**
> > + * 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;
> > + struct device *dev = core->dev;
> > + 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->format;
> > + u32 bpc = XST352_BYTE4_BIT_DEPTH_10;
> > +
> > + 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(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 = rshift_and_mask(payload, XST352_PAYLOAD_BYTE1);
> > + active_luma = mask_and_rshift(payload,
> > +
> XST352_BYTE3_ACT_LUMA_COUNT);
> > + pic_type = mask_and_rshift(payload,
> XST352_BYTE2_PIC_TYPE);
> > + framerate = rshift_and_mask(payload, XST352_BYTE2_FPS);
> > + tscan = mask_and_rshift(payload, XST352_BYTE2_TS_TYPE);
> > + sampling = mask_and_rshift(payload,
> XST352_BYTE3_COLOR_FORMAT);
> > + bpc = mask_and_rshift(payload, XST352_BYTE4_BIT_DEPTH);
> > + } else {
> > + dev_dbg(dev, "No ST352 payload available : Mode = %d\n",
> mode);
> > + framerate = mask_and_rshift(val,
> XSDIRX_TS_DET_STAT_RATE);
> > + tscan = mask_and_rshift(val, XSDIRX_TS_DET_STAT_SCAN);
> > + }
> > +
> > + if ((bpc == XST352_BYTE4_BIT_DEPTH_10 && core->bpc != 10) ||
> > + (bpc == XST352_BYTE4_BIT_DEPTH_12 && core->bpc != 12)) {
> > + dev_dbg(dev, "Bit depth not supported. bpc = %d core->bpc =
> %d\n",
> > + bpc, core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + family = mask_and_rshift(val, XSDIRX_TS_DET_STAT_FAMILY);
> > + state->ts_is_interlaced = tscan ? false : true;
> > +
> > + dev_dbg(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(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(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(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(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(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(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;
> > + case XST352_BYTE1_ST2081_10_2_1080L_6G:
> > + format->height = 1080;
> > + if (active_luma)
> > + format->width = 2048;
> > + else
> > + format->width = 1920;
> > + break;
> > + default:
> > + dev_dbg(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(dev, "Unknown 12G Mode SMPTE
> standard\n");
> > + return -EINVAL;
> > + }
> > + break;
> > + default:
> > + dev_err(dev, "Invalid Mode\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (valid) {
> > + if (pic_type)
> > + format->field = V4L2_FIELD_NONE;
> > + else
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + if (format->height == 1080 && pic_type && !tscan)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > +
> > + /*
> > + * In 3GB DL pSF mode the video is similar to interlaced.
> > + * So though it is a progressive video, its transport is
> > + * interlaced and is sent as two width x (height/2) buffers.
> > + */
> > + if (byte1 == XST352_BYTE1_ST372_DL_3GB) {
> > + if (state->ts_is_interlaced)
> > + format->field = V4L2_FIELD_ALTERNATE;
> > + else
> > + format->field = V4L2_FIELD_NONE;
> > + }
> > + }
> > +
> > + if (format->field == V4L2_FIELD_ALTERNATE)
> > + format->height = format->height / 2;
> > +
> > + switch (sampling) {
> > + case XST352_BYTE3_COLOR_FORMAT_422:
> > + if (core->bpc == 10)
> > + format->code = MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + format->code = MEDIA_BUS_FMT_UYVY12_1X24;
> > + break;
> > + case XST352_BYTE3_COLOR_FORMAT_420:
> > + case XST352_BYTE3_COLOR_FORMAT_YUV444:
> > + case XST352_BYTE3_COLOR_FORMAT_GBR:
> > + format->code = 0;
> > + dev_dbg(dev, "No corresponding media bus formats\n");
> > + break;
> > + default:
> > + dev_err(dev, "Unsupported color format : %d\n", sampling);
> > + return -EINVAL;
> > + }
> > +
> > + xsdirxss_get_framerate(&state->frame_interval, framerate);
> > +
> > + dev_dbg(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(dev, "frame rate numerator = %d denominator = %d\n",
> > + state->frame_interval.numerator,
> > + state->frame_interval.denominator);
> > + dev_dbg(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 writing 1 to corresponding bit.
> > + *
> > + * 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;
> > + struct device *dev = core->dev;
> > + u32 status;
> > +
> > + status = xsdirxss_read(core, XSDIRX_ISR_REG);
> > + xsdirxss_write(core, XSDIRX_ISR_REG, status);
> > + dev_dbg(dev, "interrupt status = 0x%08x\n", status);
> > +
> > + if (status & XSDIRX_INTR_VIDLOCK_MASK ||
> > + status & XSDIRX_INTR_VIDUNLOCK_MASK) {
> > + u32 val1, val2;
> > +
> > + dev_dbg(dev, "video lock/unlock interrupt\n");
> > +
> > + xsdirx_streamflow_control(core, false);
> > + state->streaming = false;
> > +
> > + 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(dev, "video lock interrupt\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(dev, "valid st352 mask = 0x%08x\n", val1);
> > + dev_dbg(dev, "st352 payload = 0x%08x\n", val2);
> > +
> > + if (!xsdirx_get_stream_properties(state)) {
> > + state->vidlocked = true;
> > + xsdirxss_set_gtclk(state);
> > + } else {
> > + dev_err(dev, "Unable to get stream
> properties!\n");
> > + state->vidlocked = false;
> > + }
> > + } else {
> > + dev_dbg(dev, "video unlock interrupt\n");
> > + state->vidlocked = false;
> > + }
> > +
> > + 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);
> > + }
> > +
> > + if (status & XSDIRX_INTR_UNDERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core underflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_UNDERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
> > +
> > + if (status & XSDIRX_INTR_OVERFLOW_MASK) {
> > + dev_dbg(dev, "Video in to AXI4 Stream core overflow
> interrupt\n");
> > +
> > + memset(&state->event, 0, sizeof(state->event));
> > + state->event.type = V4L2_EVENT_XILINX_SDIRX_OVERFLOW;
> > + v4l2_subdev_notify_event(&state->subdev, &state->event);
> > + }
>
> Can't we combine these two in a single event type, with underflow and
> overflow reported in the event payload in a bitmask ?
>

Yes we can. I will combine these two into a single event in the next version.

> > + 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_XILINX_SDIRX_UNDERFLOW:
> > + case V4L2_EVENT_XILINX_SDIRX_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;
>
> As Hans commented, you need to call v4l2_ctrl_subscribe_event() here.
>

Agree. I will add this in next revision.

> > + }
> > + 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);
> > +}
>
> I think you can drop this function and use v4l2_event_subdev_unsubscribe
> as the ..unsubscribe_event() handler.
>

Agree I will remove this in next version.

> > +
> > +/**
> > + * 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;
> > + struct device *dev = core->dev;
> > +
> > + dev_dbg(dev, "set ctrl id = 0x%08x val = 0x%08x\n",
> > + ctrl->id, ctrl->val);
> > +
> > + if (xsdirxss->streaming) {
> > + dev_err(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);
> > + xsdirxss->framer_enable = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW:
> > + xsdirx_setvidlockwindow(core, ctrl->val);
> > + xsdirxss->vidlockwin = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE:
>
> As this is used to select which error sources to enable, how about
> naming the control V4L2_CID_XILINX_SDIRX_EDH_ERROR_SOURCES or
> V4L2_CID_XILINX_SDIRX_EDH_ERRORS_MASK ?
>

Ok. I will rename this to V4L2_CID_XILINX_SDIRX_EDH_ERROR_SOURCES

> > + xsdirx_setedherrcnttrigger(core, ctrl->val);
> > + xsdirxss->edhmask = ctrl->val;
> > + break;
> > + case V4L2_CID_XILINX_SDIRX_SEARCH_MODES:
> > + if (ctrl->val) {
> > + if (core->mode == XSDIRXSS_SDI_STD_3G) {
> > + dev_dbg(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(dev, "Upto 6G supported\n");
> > + ctrl->val &=
> ~(BIT(XSDIRX_MODE_12GI_OFFSET) |
> > +
> BIT(XSDIRX_MODE_12GF_OFFSET));
> > + }
> > +
> > + ret = xsdirx_set_modedetect(core, ctrl->val);
> > + if (!ret)
> > + xsdirxss->searchmask = ctrl->val;
> > + } else {
> > + dev_err(dev, "Select at least one mode!\n");
> > + return -EINVAL;
> > + }
> > + break;
>
> The traditional approach to select timing standards is to use ioctls.
> Hans, do you think a custom control is fine here, or should the dv
> timings ioctls be extended (or new sdi timings ioctls created) ?
>
> If we go for a single control, I'm a bit concerned that this control
> will restrict the search when multiple bits are set, but force a
> specific mode when a single bit is set. I don't have enough experience
> with SDI to tell whether this is the right behaviour.
>
> > + default:
> > + xsdirxss_set(core, XSDIRX_RST_CTRL_REG,
> > + XSDIRX_RST_CTRL_SS_EN_MASK);
> > + return -EINVAL;
>
> This looks weird.
>
> ret = -EINVAL;
> break;
>
> would be better.
>

Agree. I will fix this.

> > + }
> > + xsdirx_core_enable(core);
>
> Do we really need to disable and reenable the core as we're guaranteed
> not to be streaming ?
>

The SDI Rx core will always be enabled to be able to understand what is the incoming stream properties.
During stream on/off we only enable/disable the video bridges which convert the SDI stream to AXI4-S
and pass to downstream IP.
If the SDI Rx core isn't enabled before stream_on, the stream properties (mode, width, height, frame rate, etc) won't be known
to the driver and hence the application.

> > + 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;
> > + struct device *dev = core->dev;
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(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;
>
> Hans commented that the dv timings structure will report whether the
> mode is interlaced, I wonder if reporting the SDI mode shouldn't go
> through that structure too.
>

I don’t know about this.
We may add a feature in V4L2 / DRM framework to set / get the SDI modes.

> > + case V4L2_CID_XILINX_SDIRX_CRC:
> > + ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> > + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > + break;
>
> Are CRC errors common (and recoverable), or are they rare and fatal
> errors ? In other words, does this report information that would be
> classified as link quality, or fatal errors ? In the later case I wonder
> if an event wouldn't make more sense. It could be reported as another
> bit in the overflow/underflow event.
>

CRC errors will reflect link quality.
There is no interrupt for CRC errors.
So driver software won't know when to read this register.
Also this is specific to Xilinx implementation.

> > + 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(dev, "%d - not in SD mode\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + break;
>
> If my understanding of the datasheet is correct, this will be reset for
> every frame, right ? Reporting the information this way is thus quite
> racy. Isn't it better to send it through an event ?
>

This is not reset per frame.
This is an error reporting mechanism that application can use to get errors in stream while
running SD modes (720x487 and 720x576).
So I think it is ok to send it like this.

> > + 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(dev, "%d - not in SD mode\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + break;
>
> Same here, seems quite racy.
>

Same here as earlier about EDH Counter. There is no interrupt here too.

> > + case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> > + if (!xsdirxss->vidlocked) {
> > + dev_err(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(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(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;
>
> Shouldn't this also go through DV timings ? If not, I think it should at
> least be combined with V4L2_CID_XILINX_SDIRX_MODE_DETECT.
>

I am trying to use the same values to set mode and detect it.
As there is no different bit to specifically detect 3GB Mode, hence this differentiation is there.
I will try to combine this in later patch.

> > + default:
> > + dev_err(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "Get ctrl id = 0x%08x val = 0x%08x\n", ctrl->id,
> > + ctrl->val);
>
> I would drop this line.
>

Ok I will remove this in next patch.

> > + 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 i;
> > +
> > + v4l2_info(sd, "***** SDI Rx subsystem reg dump start *****\n");
> > + for (i = 0; i < 0x28; i++) {
>
> There are registers in this range that are not documented in the
> datasheet. Is it really safe to read them ? There are also registers
> that are of no real interest I believe (ISR for instance), or registers
> that seems to map to a FIFO (RX_ST352_DATA_DS*) which may cause issue
> when read as the IRQ handler extracts data from them (I could be wrong
> here though). Should this be restricted to a handful of carefully
> selected registers ? You could then also print their name instead of
> their address, that would be more readable.
>

Ok. I will print registers values with register names and offsets in next version.

> > + u32 data;
> > +
> > + 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;
> > +}
> > +
> > +/**
> > + * 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;
>
> There's a data race with the interrupt handler here.
>

Yes I will fix this in next version.

> > +
> > + 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;
> > + struct device *dev = core->dev;
> > +
>
> Maybe
>
> if (enable == xsdirxss->streaming) {
> dev_dbg(...);
> return enable ? -EINVAL : 0;
> }
>
> ? Up to you.
>

Agree. This will be similar to MIPI CSI-2 Rx driver then.
I will fix this in next version.

> > + if (enable) {
> > + if (!xsdirxss->vidlocked) {
> > + dev_dbg(dev, "Video is not locked\n");
> > + return -EINVAL;
> > + }
> > + if (xsdirxss->streaming) {
> > + dev_dbg(dev, "Already streaming\n");
> > + return -EINVAL;
> > + }
> > +
> > + xsdirx_streamflow_control(core, true);
> > + xsdirxss->streaming = true;
> > + dev_dbg(dev, "Streaming started\n");
> > + } else {
> > + if (!xsdirxss->streaming) {
> > + dev_dbg(dev, "Stopped streaming already\n");
> > + return 0;
> > + }
> > +
> > + xsdirx_streamflow_control(core, false);
> > + xsdirxss->streaming = false;
> > + dev_dbg(dev, "Streaming stopped\n");
> > + }
> > +
>
> And possibly
>
> xsdirxss->streaming = enable;
> dev_dbg(dev, "Streaming %s\", enable ? "started" : "stopped");
>

Agree to this too. I will update this in next version.

> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_g_input_status - It is used to determine if the video signal
> > + * is present / locked onto or not.
> > + *
> > + * @sd: V4L2 Sub device
> > + * @status: status of signal locked
> > + *
> > + * This is used to determine if the video signal is present and locked onto
> > + * by the SDI Rx core or not based on vidlocked flag.
> > + *
> > + * Return: zero on success
> > + */
> > +static int xsdirxss_g_input_status(struct v4l2_subdev *sd, u32 *status)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > +
> > + if (!xsdirxss->vidlocked)
> > + *status = V4L2_IN_ST_NO_SYNC | V4L2_IN_ST_NO_SIGNAL;
> > + else
> > + *status = 0;
> > +
> > + 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->format;
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +/**
> > + * xsdirxss_init_cfg - Initialise the pad format config to default
> > + * @sd: Pointer to V4L2 Sub device structure
> > + * @cfg: Pointer to sub device pad information structure
> > + *
> > + * This function is used to initialize the pad format with the default
> > + * values.
> > + *
> > + * Return: 0 on success
> > + */
> > +static int xsdirxss_init_cfg(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + struct v4l2_mbus_framefmt *format;
> > +
> > + format = v4l2_subdev_get_try_format(sd, cfg, 0);
> > + *format = xsdirxss->default_format;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * 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;
>
> As Hans requested, width, height and field should be set here too, and
> probably colorspace information too.
>

Yes. I will be updating this in next version.

> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_mbus_code - Handle pixel format enumeration
> > + * @sd: pointer to v4l2 subdev structure
> > + * @cfg: V4L2 subdev pad configuration
> > + * @code: pointer to v4l2_subdev_mbus_code_enum structure
> > + *
> > + * Return: -EINVAL or zero on success
> > + */
> > +static int xsdirxss_enum_mbus_code(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_mbus_code_enum
> *code)
> > +{
> > + struct xsdirxss_state *xsdirxss = to_xsdirxssstate(sd);
> > + u32 index = code->index;
> > + u32 maxindex;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + maxindex = ARRAY_SIZE(xsdirxss_10bpc_mbus_fmts);
> > + else
> > + maxindex = ARRAY_SIZE(xsdirxss_12bpc_mbus_fmts);
> > +
> > + if (code->pad || index >= maxindex)
> > + return -EINVAL;
> > +
> > + if (xsdirxss->core.bpc == 10)
> > + code->code = xsdirxss_10bpc_mbus_fmts[index];
> > + else
> > + code->code = xsdirxss_12bpc_mbus_fmts[index];
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_enum_dv_timings: Enumerate all the supported DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -EINVAL incase of invalid index and pad or zero on success
> > + */
> > +static int xsdirxss_enum_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_enum_dv_timings *timings)
> > +{
> > + if (timings->index >= ARRAY_SIZE(fmt_cap))
> > + return -EINVAL;
> > +
> > + if (timings->pad != 0)
> > + return -EINVAL;
> > +
> > + timings->timings = fmt_cap[timings->index];
> > + return 0;
> > +}
> > +
> > +/**
> > + * xsdirxss_query_dv_timings: Query for the current DV timings
> > + * @sd: pointer to v4l2 subdev structure
> > + * @timings: DV timings structure to be returned.
> > + *
> > + * Return: -ENOLCK when video is not locked, -ERANGE when corresponding
> timing
> > + * entry is not found or zero on success.
> > + */
> > +static int xsdirxss_query_dv_timings(struct v4l2_subdev *sd,
> > + struct v4l2_dv_timings *timings)
> > +{
> > + struct xsdirxss_state *state = to_xsdirxssstate(sd);
> > + unsigned int i;
> > +
> > + if (!state->vidlocked)
> > + return -ENOLCK;
> > +
> > + for (i = 0; i < ARRAY_SIZE(xsdirxss_dv_timings); i++) {
> > + if (state->format.width == xsdirxss_dv_timings[i].width &&
> > + state->format.height == xsdirxss_dv_timings[i].height &&
> > + state->frame_interval.denominator ==
> > + (xsdirxss_dv_timings[i].fps * 1000)) {
> > + *timings = xsdirxss_dv_timings[i].format;
> > + return 0;
> > + }
> > + }
> > +
> > + return -ERANGE;
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * 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 const 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 const 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 const struct v4l2_subdev_video_ops xsdirxss_video_ops = {
> > + .g_frame_interval = xsdirxss_g_frame_interval,
> > + .s_stream = xsdirxss_s_stream,
> > + .g_input_status = xsdirxss_g_input_status,
> > + .query_dv_timings = xsdirxss_query_dv_timings,
> > +};
> > +
> > +static const struct v4l2_subdev_pad_ops xsdirxss_pad_ops = {
> > + .init_cfg = xsdirxss_init_cfg,
> > + .get_fmt = xsdirxss_get_format,
> > + .set_fmt = xsdirxss_set_format,
> > + .enum_mbus_code = xsdirxss_enum_mbus_code,
> > + .enum_dv_timings = xsdirxss_enum_dv_timings,
> > +};
> > +
> > +static const struct v4l2_subdev_ops xsdirxss_ops = {
> > + .core = &xsdirxss_core_ops,
> > + .video = &xsdirxss_video_ops,
> > + .pad = &xsdirxss_pad_ops
> > +};
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Platform Device Driver
> > + */
> > +
> > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > +{
> > + struct device_node *node = xsdirxss->core.dev->of_node;
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct device *dev = core->dev;
> > + struct fwnode_handle *ep, *rep;
> > + int ret;
> > + const char *sdi_std;
> > +
> > + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > + dev_dbg(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(dev, "xlnx,line-rate property not found\n");
> > + return ret;
> > + }
> > +
> > + if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
>
> Strings in DT are null-terminated, you can use strcmp() and drop
> XSDIRX_MAX_STR_LENGTH.
>

I will remove the strings from DT and have values 0, 1 and 2 to represent 3G, 6G and 12G 8DS mode configurations.

> > + 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(dev, "Invalid Line Rate\n");
> > + return -EINVAL;
> > + }
> > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > + core->mode);
> > +
> > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to get xlnx,bpp\n");
> > + return ret;
> > + }
> > +
> > + if (core->bpc != 10 && core->bpc != 12) {
> > + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> > + core->bpc);
> > + return -EINVAL;
> > + }
> > +
> > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > +
> FWNODE_GRAPH_ENDPOINT_NEXT);
> > + if (!ep) {
> > + dev_err(dev, "no source port found");
> > + ret = -EINVAL;
> > + goto dt_parse_done;
> > + }
> > +
> > + rep = fwnode_graph_get_remote_endpoint(ep);
> > + if (!rep) {
> > + dev_err(dev, "no remote sink endpoint found");
> > + ret = -EINVAL;
> > + }
>
> I don't think you need to check this, the subdev won't be linked
> properly in the pipeline if there's no port anyway.
>

Ok. I thought it was subdev driver's responsibility to validate this too. Hence added.
I will remove this parsing in the next version.

> > +
> > + fwnode_handle_put(rep);
> > +dt_parse_done:
> > + fwnode_handle_put(ep);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_probe(struct platform_device *pdev)
> > +{
> > + struct v4l2_subdev *subdev;
> > + struct xsdirxss_state *xsdirxss;
> > + struct xsdirxss_core *core;
> > + struct device *dev;
> > + 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;
> > + dev = core->dev;
> > +
> > + /* Register interrupt handler */
> > + core->irq = platform_get_irq(pdev, 0);
> > + ret = devm_request_threaded_irq(dev, core->irq, NULL,
> > + xsdirxss_irq_handler, IRQF_ONESHOT,
> > + dev_name(dev), xsdirxss);
>
> Why a threaded IRQ ?

The irq handler calls xsdirxss_set_gtclk() which in turn calls clk_set_rate() and clk_get_rate() which can't be called in the interrupt context.
Hence a threaded irq handler.

>
> > + if (ret) {
> > + dev_err(dev, "Err = %d Interrupt handler reg failed!\n",
> > + ret);
> > + return ret;
> > + }
>
> You should request the IRQ after resetting the core, otherwise
> interrupts could be triggered immediately.
>

Ok I will move this after core disabled below.

> > +
> > + core->num_clks = ARRAY_SIZE(xsdirxss_clks);
> > + core->clks = devm_kcalloc(dev, core->num_clks,
> > + sizeof(*core->clks), GFP_KERNEL);
> > + if (!core->clks)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < core->num_clks; i++)
> > + core->clks[i].id = xsdirxss_clks[i];
> > +
> > + ret = devm_clk_bulk_get(dev, core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = clk_bulk_prepare_enable(core->num_clks, core->clks);
> > + if (ret)
> > + return ret;
> > +
> > + ret = xsdirxss_parse_of(xsdirxss);
> > + if (ret < 0)
> > + goto clk_err;
> > +
> > + core->iomem = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(core->iomem)) {
> > + ret = PTR_ERR(core->iomem);
> > + goto clk_err;
> > + }
>
> If you move these two calls above handling of the clocks, you will be
> able to return immediately and avoid the goto.
>

Agreed. I will move them above the clock initialization.

> > +
> > + /* 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->pad.flags = MEDIA_PAD_FL_SOURCE;
> > +
> > + /* Initialize the default format */
> > + if (core->bpc == 10)
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY10_1X20;
> > + else
> > + xsdirxss->default_format.code =
> MEDIA_BUS_FMT_UYVY12_1X24;
> > + 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->format = xsdirxss->default_format;
> > +
> > + /* Initialize V4L2 subdevice and media entity */
> > + subdev = &xsdirxss->subdev;
> > + v4l2_subdev_init(subdev, &xsdirxss_ops);
> > +
> > + subdev->dev = &pdev->dev;
> > + strscpy(subdev->name, dev_name(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->pad);
> > + if (ret < 0)
> > + goto error;
> > +
> > + /* Initialise and register the controls */
> > + num_ctrls = ARRAY_SIZE(xsdirxss_ctrls);
> > +
> > + if (core->include_edh)
> > + num_edh_ctrls = ARRAY_SIZE(xsdirxss_edh_ctrls);
>
> How about
>
> num_ctrls += ARRAY_SIZE(xsdirxss_edh_ctrls);
>
> > +
> > + v4l2_ctrl_handler_init(&xsdirxss->ctrl_handler,
> > + (num_ctrls + num_edh_ctrls));
>
> num_ctrls);
>

Agree. I will make this change in next version.

> > +
> > + for (i = 0; i < num_ctrls; i++) {
>
> for (i = 0; i < ARRAY_SIZE(xsdirxss_ctrls); i++) {
>
> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_ctrls[i].name);
> > + goto error;
> > + }
>
> As you check for xsdirxss->ctrl_handler.error below you can drop this
> check and the equivalent one for the EDH controls.
>

Ok I will remove this check here and in EDH controls below.

> > + }
> > +
> > + if (core->include_edh) {
> > + for (i = 0; i < num_edh_ctrls; i++) {
>
> for (i = 0; i < ARRAY_SIZE(xsdirxss_edh_ctrls); i++) {
>

This will be updated in next version.

> > + struct v4l2_ctrl *ctrl;
> > +
> > + dev_dbg(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(dev, "Failed to add %s ctrl\n",
> > + xsdirxss_edh_ctrls[i].name);
> > + goto error;
> > + }
> > + }
> > + }
> > +
> > + if (xsdirxss->ctrl_handler.error) {
> > + dev_err(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(dev, "failed to set controls\n");
> > + goto error;
> > + }
> > +
> > + platform_set_drvdata(pdev, xsdirxss);
> > +
> > + ret = v4l2_async_register_subdev(subdev);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to register subdev\n");
> > + goto error;
> > + }
> > +
> > + xsdirxss->streaming = false;
> > +
> > + xsdirx_core_enable(core);
> > +
> > + dev_info(dev, "probe success\n");
>
> You can drop this line, successful driver probing should be silent.
>

Right. I will remove this in next version.

> > +
> > + return 0;
> > +error:
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > +clk_err:
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > + return ret;
> > +}
> > +
> > +static int xsdirxss_remove(struct platform_device *pdev)
> > +{
> > + struct xsdirxss_state *xsdirxss = platform_get_drvdata(pdev);
> > + struct xsdirxss_core *core = &xsdirxss->core;
> > + struct v4l2_subdev *subdev = &xsdirxss->subdev;
> > +
> > + v4l2_async_unregister_subdev(subdev);
> > + v4l2_ctrl_handler_free(&xsdirxss->ctrl_handler);
> > + media_entity_cleanup(&subdev->entity);
> > +
> > + xsdirx_globalintr(core, false);
> > + xsdirx_disableintr(core, XSDIRX_INTR_ALL_MASK);
> > + xsdirx_core_disable(core);
> > + xsdirx_streamflow_control(core, false);
> > +
> > + clk_bulk_disable_unprepare(core->num_clks, core->clks);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id xsdirxss_of_id_table[] = {
> > + { .compatible = "xlnx,v-smpte-uhdsdi-rx-ss-2.0" },
> > + { }
> > +};
> > +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 <[email protected]>");
> > +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 000000000000..6f2a093968d9
> > --- /dev/null
> > +++ b/include/uapi/linux/xilinx-sdirxss.h
> > @@ -0,0 +1,179 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +/*
> > + * Xilinx SDI Rx Subsystem mode, event, custom timings
> > + * and flag definitions.
> > + *
> > + * Copyright (C) 2019 - 2020 Xilinx, Inc.
> > + *
> > + * Contacts: Vishal Sagar <[email protected]>
> > + */
> > +
> > +#ifndef __UAPI_XILINX_SDIRXSS_H__
> > +#define __UAPI_XILINX_SDIRXSS_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/v4l2-dv-timings.h>
> > +#include <linux/videodev2.h>
> > +
> > +/*
> > + * Events
> > + *
> > + * V4L2_EVENT_XILINX_SDIRX_UNDERFLOW: Video in to AXI4 Stream core
> underflowed
> > + * V4L2_EVENT_XILINX_SDIRX_OVERFLOW: Video in to AXI4 Stream core
> overflowed
> > + */
> > +#define V4L2_EVENT_XILINX_SDIRX_CLASS
> (V4L2_EVENT_PRIVATE_START | 0x200)
> > +#define V4L2_EVENT_XILINX_SDIRX_UNDERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x1)
> > +#define V4L2_EVENT_XILINX_SDIRX_OVERFLOW
> (V4L2_EVENT_XILINX_SDIRX_CLASS | 0x2)
> > +
> > +/*
> > + * 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
> > +
> > +/* Xilinx DV timings not in mainline yet */
> > +#define XLNX_V4L2_DV_BT_2048X1080P24 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P25 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P30 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 329, 44, 329, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 274, 44, 274, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080I60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 74250000, 66, 20, 66, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 510, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P50 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 400, 44, 148, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_2048X1080P60 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(2048, 1080, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 88, 44, 20, 4, 5, 36, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_3840X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_4096X2160P48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 594000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#define XLNX_V4L2_DV_BT_1920X1080I48 { \
> > + .type = V4L2_DV_BT_656_1120, \
> > + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \
> > + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
> > + 148500000, 371, 88, 371, 2, 5, 15, 3, 5, 15, \
> > + V4L2_DV_BT_STD_SDI) \
> > +}
> > +
> > +#endif /* __UAPI_XILINX_SDIRXSS_H__ */
> > diff --git a/include/uapi/linux/xilinx-v4l2-controls.h
> b/include/uapi/linux/xilinx-v4l2-controls.h
> > index b6441fe705c5..e9de65e82642 100644
> > --- a/include/uapi/linux/xilinx-v4l2-controls.h
> > +++ b/include/uapi/linux/xilinx-v4l2-controls.h
> > @@ -71,4 +71,71 @@
> > /* Noise level */
> > #define V4L2_CID_XILINX_TPG_NOISE_GAIN
> (V4L2_CID_XILINX_TPG + 17)
> >
> > +/*
> > + * Xilinx SDI Rx Subsystem
> > + */
> > +
> > +/* The base for the sdi rx driver controls.
> > + * We reserve 32 controls for this driver.
> > + *
> > + * The V4L2_CID_XILINX_SDIRX_EDH_* controls are present only if
> > + * EDH is enabled.
> > + * The controls which can be set should only be set before enabling
> > + * streaming. The controls which can be got should be called while
> > + * streaming to get correct values.
> > + * The V4L2_CID_XILINX_SDIRX_MODE_DETECT can be called when query
> dv timing
> > + * returns a valid timing.
> > + */
> > +
> > +#define V4L2_CID_XILINX_SDIRX
> (V4L2_CID_XILINX_BASE + 0x20)
> > +
> > +/* Framer Control to enable or disable the framer */
> > +#define V4L2_CID_XILINX_SDIRX_FRAMER
> (V4L2_CID_XILINX_SDIRX + 1)
>
> This control needs more documentation. What does it do, and how should
> it be used ?

This is documented in the Table 2-2 rx_frame_en row of PG 205
https://www.xilinx.com/support/documentation/ip_documentation/v_smpte_uhdsdi/v1_0/pg205-v-smpte-uhdsdi.pdf

"This input enables the SDI framer function. When this input is High, the framer automatically readjusts the output word
alignment to match the alignment of each timing reference signal (TRS). TRS is a generic term referring to both EAV and SAV sequences.
Normally, this input should be always be High. But, the user application may control this input to implement TRS filtering
to prevent a signal misaligned TRS from causing erroneous alignment changes."
E/SAV is End/Start of Active Video.

I will update this in next version

>
> > +/*
> > + * Video Lock Window Control to set the video lock window value
> > + * This is the amount of time the mode and transport stream need
> > + * to be locked before a video lock interrupt occurs.
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_VIDLOCK_WINDOW
> (V4L2_CID_XILINX_SDIRX + 2)
> > +/* EDH Error Mask Control to enable EDH error count */
> > +#define V4L2_CID_XILINX_SDIRX_EDH_ERRCNT_ENABLE
> (V4L2_CID_XILINX_SDIRX + 3)
> > +/*
> > + * Mode search Control to pass the bit mask of modes to detect.
> > + *
> > + * bit 0 set to detect SD mode,
> > + * bit 1 set to detect HD mode,
> > + * bit 2 set to detect 3G (3GA & 3GB) mode,
> > + * bit 3 set to detect 6G mode,
> > + * bit 4 set to detect 12G integer frame rate mode,
> > + * bit 5 set to detect 12G fractional frame rate mode,
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_SEARCH_MODES
> (V4L2_CID_XILINX_SDIRX + 4)
> > +/*
> > + * Get Detected Mode control
> > + *
> > + * Control Value - Mode detected
> > + * 0 - SD
> > + * 1 - HD
> > + * 2 - 3G (3GA & 3GB)
> > + * 3 - 6G
> > + * 4 - 12G integer frame rate
> > + * 5 - 12G fractional frame rate
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_MODE_DETECT
> (V4L2_CID_XILINX_SDIRX + 5)
> > +/* Get number of CRC errors status control */
> > +#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 whether it is interlaced or not */
> > +#define V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED
> (V4L2_CID_XILINX_SDIRX + 9)
> > +/* Get number of Active Streams */
> > +#define V4L2_CID_XILINX_SDIRX_ACTIVE_STREAMS
> (V4L2_CID_XILINX_SDIRX + 10)
> > +/*
> > + * Get if the detected mode is 3GB.
> > + * Can be used to distinguished between 3GA and 3GB
> > + */
> > +#define V4L2_CID_XILINX_SDIRX_IS_3GB
> (V4L2_CID_XILINX_SDIRX + 11)
> > +
> > #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
>
> --
> Regards,
>
> Laurent Pinchart

Regards
Vishal Sagar

2020-06-16 23:16:20

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] media: v4l: xilinx: Add Xilinx UHD-SDI Rx Subsystem driver

Hi Vishal,

On Tue, Jun 09, 2020 at 06:23:43PM +0000, Vishal Sagar wrote:
> On Wednesday, May 6, 2020 8:42 PM, Laurent Pinchart wrote:
> > On Wed, Apr 29, 2020 at 07:47:04PM +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 10/12 bpc YUV 422 media bus format currently. 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.
> >
> > As commented on patch 1/2, I don't see a mention of 12 bpc in the
> > documentation. Let's discuss it as part of patch 1/2.
>
> Ok.
>
> > > The driver currently supports only the AXI4-Stream IP configuration.
> > >
> > > Signed-off-by: Vishal Sagar <[email protected]>
> > > ---
> > > v2
> > > - Added DV timing support based on Hans Verkuilś feedback
> > > - More documentation to custom v4l controls and events
> > > - Fixed Hyunś comments
> > > - Added macro for masking and shifting as per Joe Perches comments
> > > - Updated to latest as per Xilinx github repo driver like
> > > adding new DV timings not in mainline yet uptill 03/21/20
> > >
> > > drivers/media/platform/xilinx/Kconfig | 11 +
> > > drivers/media/platform/xilinx/Makefile | 1 +
> > > .../media/platform/xilinx/xilinx-sdirxss.c | 2162 +++++++++++++++++
> > > include/uapi/linux/xilinx-sdirxss.h | 179 ++
> > > include/uapi/linux/xilinx-v4l2-controls.h | 67 +
> > > 5 files changed, 2420 insertions(+)
> > > create mode 100644 drivers/media/platform/xilinx/xilinx-sdirxss.c
> > > create mode 100644 include/uapi/linux/xilinx-sdirxss.h

[snip]

> > > diff --git a/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > > new file mode 100644
> > > index 000000000000..c536ea3aaa0d
> > > --- /dev/null
> > > +++ b/drivers/media/platform/xilinx/xilinx-sdirxss.c
> > > @@ -0,0 +1,2162 @@

[snip]

> > > +/* Maximum number of events per file handle. */
> > > +#define XSDIRX_MAX_EVENTS (128)
> >
> > Do we really need such a high number ? How often are the overflow and
> > underflow expected ?
>
> An overflow or underflow events will occur when a resolution / rate change occurs.
> But since we are stopping the bridges in irq handler, these events will stop coming.
> I can reduce this to 8 events but it is arbitrary. Is that ok?

Yes, I think that's a better value.

[snip]

> > > +/**
> > > + * 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
> > > + * @clks: array of clocks
> > > + * @num_clks: number of clocks
> > > + * @bpc: Bits per component, can be 10 or 12
> > > + */
> > > +struct xsdirxss_core {
> > > + struct device *dev;
> > > + void __iomem *iomem;
> > > + int irq;
> > > + bool include_edh;
> > > + int mode;
> > > + struct clk_bulk_data *clks;
> > > + int num_clks;
> >
> > num_clks is never negative, you can make it an unsigned int.
>
> Ok. I will make num_clks as unsigned int.
> So why devm_clk_bulk_get() and clk_bulk_prepare_enable() take in num_clks argument as int ?

That's a good question. I assume that's because nobody has cared to make
it unsigned. It makes little difference in practice (there's a small
impact on code generation depending on what operation you perform on the
value, but it's usually negligible). Where it makes a difference, I
believe, is in expressing the intent. If you make field unsigned, you
express that it doesn't make sense to assign it a negative value. It's
API documentation in a way. It can also avoid some errors:

struct foo {
u8 elements[16];
};

int zero_foo_elements(struct foo *foo, int num_elements)
{
if (num_elements > ARRAY_SIZE(foo->elements))
return -EOVERFLOW;

memset(foo->elements, 0, num_elements);

return 0;
}

If called with a negative value, bad things will happen. If num_elements
was unsigned, there would be no issue. Of course the function could be
written

int zero_foo_elements(struct foo *foo, int num_elements)
{
if (num_elements < 0 || num_elements > ARRAY_SIZE(foo->elements))
return -EOVERFLOW;

memset(foo->elements, 0, num_elements);

return 0;
}

but it's easy to overlook the need for a < 0 check when there's no valid
use case for passing a negative value.

> > > + u32 bpc;
> > > +};

[snip]

> > > +/* TODO - Add YUV 444/420 and RBG 10/12 bpc mbus formats here */
> >
> > s/RBG/RGB/ ?
> >
>
> No RBG is correct because as per Xilinx User Guide 934 Figure 1-4 and Table 1-4,
> the Blue is middle component in the AXI4-Stream protocol followed by all Xilinx Video IPs.
> https://www.xilinx.com/support/documentation/ip_documentation/axi_videoip/v1_0/ug934_axi_videoIP.pdf

Indeed, my bad. I wonder why the AXI4 stream protocol uses RBG, it's a
quite unusual order.

> We will add the RBG 10 and 12 bpc formats later.

[snip]

> > > +
> > > +/**
> > > + * 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;
> > > + struct device *dev = core->dev;
> > > +
> > > + switch (ctrl->id) {
> > > + case V4L2_CID_XILINX_SDIRX_MODE_DETECT:
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(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;
> >
> > Hans commented that the dv timings structure will report whether the
> > mode is interlaced, I wonder if reporting the SDI mode shouldn't go
> > through that structure too.
> >
>
> I don’t know about this.
> We may add a feature in V4L2 / DRM framework to set / get the SDI modes.

I'd like to get Hans' opinion on this, he has more experience with DV
timings than I do.

> > > + case V4L2_CID_XILINX_SDIRX_CRC:
> > > + ctrl->val = xsdirxss_read(core, XSDIRX_CRC_ERRCNT_REG);
> > > + xsdirxss_write(core, XSDIRX_CRC_ERRCNT_REG, 0xFFFF);
> > > + break;
> >
> > Are CRC errors common (and recoverable), or are they rare and fatal
> > errors ? In other words, does this report information that would be
> > classified as link quality, or fatal errors ? In the later case I wonder
> > if an event wouldn't make more sense. It could be reported as another
> > bit in the overflow/underflow event.
>
> CRC errors will reflect link quality.
> There is no interrupt for CRC errors.
> So driver software won't know when to read this register.
> Also this is specific to Xilinx implementation.

Thanks for the clarification, this makes sense.

> > > + 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(dev, "%d - not in SD mode\n", ctrl->id);
> > > + return -EINVAL;
> > > + }
> > > + break;
> >
> > If my understanding of the datasheet is correct, this will be reset for
> > every frame, right ? Reporting the information this way is thus quite
> > racy. Isn't it better to send it through an event ?
>
> This is not reset per frame.
> This is an error reporting mechanism that application can use to get errors in stream while
> running SD modes (720x487 and 720x576).
> So I think it is ok to send it like this.

I'm not sure why I thought it was reset per frame, sorry about the
noise.

> > > + 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(dev, "%d - not in SD mode\n", ctrl->id);
> > > + return -EINVAL;
> > > + }
> > > + break;
> >
> > Same here, seems quite racy.
> >
>
> Same here as earlier about EDH Counter. There is no interrupt here too.
>
> > > + case V4L2_CID_XILINX_SDIRX_TS_IS_INTERLACED:
> > > + if (!xsdirxss->vidlocked) {
> > > + dev_err(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(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(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;
> >
> > Shouldn't this also go through DV timings ? If not, I think it should at
> > least be combined with V4L2_CID_XILINX_SDIRX_MODE_DETECT.
>
> I am trying to use the same values to set mode and detect it.
> As there is no different bit to specifically detect 3GB Mode, hence this differentiation is there.
> I will try to combine this in later patch.
>
> > > + default:
> > > + dev_err(dev, "Get Invalid control id 0x%0x\n", ctrl->id);
> > > + return -EINVAL;
> > > + }
> > > + dev_dbg(dev, "Get ctrl id = 0x%08x val = 0x%08x\n", ctrl->id,
> > > + ctrl->val);
> >
> > I would drop this line.
> >
>
> Ok I will remove this in next patch.
>
> > > + return 0;
> > > +}

[snip]

> > > +static int xsdirxss_parse_of(struct xsdirxss_state *xsdirxss)
> > > +{
> > > + struct device_node *node = xsdirxss->core.dev->of_node;
> > > + struct xsdirxss_core *core = &xsdirxss->core;
> > > + struct device *dev = core->dev;
> > > + struct fwnode_handle *ep, *rep;
> > > + int ret;
> > > + const char *sdi_std;
> > > +
> > > + core->include_edh = of_property_read_bool(node, "xlnx,include-edh");
> > > + dev_dbg(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(dev, "xlnx,line-rate property not found\n");
> > > + return ret;
> > > + }
> > > +
> > > + if (!strncmp(sdi_std, "12G_SDI_8DS", XSDIRX_MAX_STR_LENGTH)) {
> >
> > Strings in DT are null-terminated, you can use strcmp() and drop
> > XSDIRX_MAX_STR_LENGTH.
>
> I will remove the strings from DT and have values 0, 1 and 2 to
> represent 3G, 6G and 12G 8DS mode configurations.

Then please add a header file in include/dt-bindings/media with macros
for these values. An alternative would be to use 3, 6 and 12
respectively, in which case we could do without macros I think. I'm
fine with either option.

> > > + 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(dev, "Invalid Line Rate\n");
> > > + return -EINVAL;
> > > + }
> > > + dev_dbg(dev, "SDI Rx Line Rate = %s, mode = %d\n", sdi_std,
> > > + core->mode);
> > > +
> > > + ret = of_property_read_u32(node, "xlnx,bpp", &core->bpc);
> > > + if (ret < 0) {
> > > + dev_err(dev, "failed to get xlnx,bpp\n");
> > > + return ret;
> > > + }
> > > +
> > > + if (core->bpc != 10 && core->bpc != 12) {
> > > + dev_err(dev, "bits per component=%u. Can be 10 or 12 only\n",
> > > + core->bpc);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
> > > + FWNODE_GRAPH_ENDPOINT_NEXT);
> > > + if (!ep) {
> > > + dev_err(dev, "no source port found");
> > > + ret = -EINVAL;
> > > + goto dt_parse_done;
> > > + }
> > > +
> > > + rep = fwnode_graph_get_remote_endpoint(ep);
> > > + if (!rep) {
> > > + dev_err(dev, "no remote sink endpoint found");
> > > + ret = -EINVAL;
> > > + }
> >
> > I don't think you need to check this, the subdev won't be linked
> > properly in the pipeline if there's no port anyway.
>
> Ok. I thought it was subdev driver's responsibility to validate this too. Hence added.
> I will remove this parsing in the next version.
>
> > > +
> > > + fwnode_handle_put(rep);
> > > +dt_parse_done:
> > > + fwnode_handle_put(ep);
> > > + return ret;
> > > +}

[snip]

--
Regards,

Laurent Pinchart