diff --git a/Documentation/userspace-api/media/drivers/fsd-csis-uapi.rst b/Documentation/userspace-api/media/drivers/fsd-csis-uapi.rst
new file mode 100644
index 000000000000..6d714e9c5d45
--- /dev/null
+++ b/Documentation/userspace-api/media/drivers/fsd-csis-uapi.rst
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+FSD MIPI CSI2 Rx Controller driver
+==================================
+
+The CSI2 Rx Controller driver is compliant to MIPI CSI2 v1.3, MIPI D-PHY v1.2 specifications.
+The controller receives images over a 4 lane D-PHY interface.
+A single D-PHY interface is shared among 4 CSI2 Rx controllers.
+
+
+Private IOCTLs
+~~~~~~~~~~~~~~
+
+The FSD CSI2 Rx Controller implements below private IOCTLs
+
+VIDIOC_CSIS_DMA_SKIP
+^^^^^^^^^^^^^^^^^^^^
+
+Argument: struct dma_skip_str
+
+**Description**:
+
+        The DMA controller can be configured to skip incoming frames
+        from being written to memory when needed. e.g. when user application
+        needs bandwidth control without reconfiguring camera sensor.
+
+**Return value**:
+
+       On success 0 is returned. On error -1 is returned and errno is set
+       appropriately.
+
+**Data types**:
+
+.. code-block:: none
+
+        * struct dma_skip_str
+
+        __u32   ta      turn around pointer varibale
+        __u32   sseq    dma skip sequence variable
+        __u32   en      dma skip enable
+        __u32   vc      virtual channel
+
+
+Custom controls
+~~~~~~~~~~~~~~~
+
+FSD CSI2 Rx controller implements below custom cotrols
+
+V4L2_CID_USER_FSD_CSIS_NO_OF_LANE
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Argument: struct v4l2_control
+
+**Description**:
+
+        The D-PHY interface can be configured to receive streaming
+        on data lanes between 1 to 4 (inclusive). User applications
+        can set the desired number of lanes with this control using
+        the video device interface
+
+**Return value**:
+
+       On success 0 is returned. On error -1 is returned and errno is set
+       appropriately.
+
+**Data types**:
+
+.. code-block:: none
+
+        * struct v4l2_control
+
+        __u32   id      V4L2_CID_USER_FSD_CSIS_NO_OF_LANE
+        __s32   value   1 to 4 (inclusive)
+
+References
+----------
+
+.. [#] include/uapi/linux/fsd-csis.h
diff --git a/MAINTAINERS b/MAINTAINERS
index bbadba5888ab..c65bacd43f54 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8386,6 +8386,7 @@ M:	Sathyakam M <sathya@samsung.com>
 L:	linux-media@vger.kernel.org
 S:	Orphan
 F:	Documentation/devicetree/bindings/media/tesla-fsd-csis.yaml
+F:	drivers/media/platform/fsd/*
 
 FSI SUBSYSTEM
 M:	Jeremy Kerr <jk@ozlabs.org>
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index a9334263fa9b..b48ca5f78bdd 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -69,6 +69,7 @@ source "drivers/media/platform/aspeed/Kconfig"
 source "drivers/media/platform/atmel/Kconfig"
 source "drivers/media/platform/cadence/Kconfig"
 source "drivers/media/platform/chips-media/Kconfig"
+source "drivers/media/platform/fsd/Kconfig"
 source "drivers/media/platform/intel/Kconfig"
 source "drivers/media/platform/marvell/Kconfig"
 source "drivers/media/platform/mediatek/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index a91f42024273..d73ab62ab0cf 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -12,6 +12,7 @@ obj-y += aspeed/
 obj-y += atmel/
 obj-y += cadence/
 obj-y += chips-media/
+obj-y += fsd/
 obj-y += intel/
 obj-y += marvell/
 obj-y += mediatek/
diff --git a/drivers/media/platform/fsd/Kconfig b/drivers/media/platform/fsd/Kconfig
new file mode 100644
index 000000000000..9ce44becf3ec
--- /dev/null
+++ b/drivers/media/platform/fsd/Kconfig
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# FSD MIPI CSI-2 Rx controller configurations
+
+config VIDEO_FSD_MIPI_CSIS
+	tristate "FSD SoC MIPI-CSI2 Rx controller driver"
+	depends on VIDEO_DEV && VIDEO_V4L2_SUBDEV_API
+	depends on HAS_DMA
+	depends on OF
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_FWNODE
+	help
+	  This is a V4L2 driver for FSD SoC MIPI-CSI2 Rx interface.
+	  To compile this driver as a module, choose M here.
+	  The module will be called fsd-csis.
+	  Please select appropriate data rate for D-PHY configuration
+
+choice
+	prompt "Select PHY control values"
+	depends on VIDEO_FSD_MIPI_CSIS
+	default FSD_CSI_1600_MEGA_BITS_PER_SEC
+	help
+	  Select D-PHY Common control values based on CSI Rx
+	  bandwidth requirement.
+	  The PHY parameters are set according to the
+	  selected data rate.
+
+config FSD_CSI_800_MEGA_BITS_PER_SEC
+	bool "800Mbps"
+	help
+	  D-PHY Common control values for 800Mbps.
+	  If set FSD CSI2 Rx controller and the D-PHY are configured
+	  for data rate up to 800Mbps over the 4 lane interface.
+	  The D-PHY parameters for HS and Clock settle timings
+	  are set accordingly.
+
+config FSD_CSI_1000_MEGA_BITS_PER_SEC
+	bool "1000Mbps"
+	help
+	  D-PHY Common control values for 1000Mbps.
+	  If set FSD CSI2 Rx controller and the D-PHY are configured
+	  for data rate up to 1000Mbps over the 4 lane interface.
+	  The D-PHY parameters for HS and Clock settle timings
+	  are set accordingly.
+
+config FSD_CSI_1500_MEGA_BITS_PER_SEC
+	bool "1500Mbps"
+	help
+	  D-PHY Common control values for 1500Mbps.
+	  If set FSD CSI2 Rx controller and the D-PHY are configured
+	  for data rate up to 1500Mbps over the 4 lane interface.
+	  The D-PHY parameters for HS and Clock settle timings
+	  are set accordingly.
+
+config FSD_CSI_1600_MEGA_BITS_PER_SEC
+	bool "1600Mbps"
+	help
+	  D-PHY Common control values for 1600Mbps.
+	  If set FSD CSI2 Rx controller and the D-PHY are configured
+	  for data rate up to 1600Mbps over the 4 lane interface.
+	  The D-PHY parameters for HS and Clock settle timings
+	  are set accordingly.
+
+config FSD_CSI_2100_MEGA_BITS_PER_SEC
+	bool "2100Mbps"
+	help
+	  D-PHY Common control values for 2100Mbps.
+	  If set FSD CSI2 Rx controller and the D-PHY are configured
+	  for data rate up to 2100Mbps over the 4 lane interface.
+	  The D-PHY parameters for HS and Clock settle timings
+	  are set accordingly.
+
+endchoice
diff --git a/drivers/media/platform/fsd/Makefile b/drivers/media/platform/fsd/Makefile
new file mode 100644
index 000000000000..41d9e1ceb11c
--- /dev/null
+++ b/drivers/media/platform/fsd/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_FSD_MIPI_CSIS) += fsd-csis.o
diff --git a/drivers/media/platform/fsd/fsd-csis.c b/drivers/media/platform/fsd/fsd-csis.c
new file mode 100644
index 000000000000..713c63c46f09
--- /dev/null
+++ b/drivers/media/platform/fsd/fsd-csis.c
@@ -0,0 +1,2664 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FSD CSIS camera interface driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-fwnode.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include <uapi/linux/fsd-csis.h>
+
+#include "fsd-csis.h"
+
+#define FSD_CSIS_MODULE_NAME	"csis"
+#define FSD_CSIS_MODULE_VERSION	"0.0.1"
+
+static unsigned int video_nr = -1;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activities debug info");
+
+static atomic_t drv_instance = ATOMIC_INIT(0);
+
+/* fsd_csis_formats - array of image formats supported */
+static const struct fsd_csis_fmt fsd_csis_formats[FSD_CSIS_MAX_FORMATS] = {
+	{
+		.name		= "RGB565",
+		.fourcc		= V4L2_PIX_FMT_RGB565,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.code		= MEDIA_BUS_FMT_RGB565_1X16,
+		.depth		= 16,
+	}, {
+		.name		= "RGB666",
+		.fourcc		= V4L2_PIX_FMT_BGR666,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.code		= MEDIA_BUS_FMT_RGB666_1X18,
+		.depth		= 18,
+	}, {
+		.name		= "RGB888-24",
+		.fourcc		= V4L2_PIX_FMT_RGB24,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.code		= MEDIA_BUS_FMT_RGB888_1X24,
+		.depth		= 24,
+	}, {
+		.name		= "RGB888-32",
+		.fourcc		= V4L2_PIX_FMT_RGB32,
+		.colorspace	= V4L2_COLORSPACE_SRGB,
+		.code           = MEDIA_BUS_FMT_RGB888_1X32_PADHI,
+		.depth		= 32,
+	}, {
+		.name		= "XRGB888",
+		.fourcc         = V4L2_PIX_FMT_XRGB32,
+		.colorspace     = V4L2_COLORSPACE_SRGB,
+		.code           = MEDIA_BUS_FMT_RGB888_1X32_PADHI,
+		.depth          = 32,
+	}, {
+		.name		= "UYVY-16",
+		.fourcc		= V4L2_PIX_FMT_UYVY,
+		.colorspace	= V4L2_COLORSPACE_RAW,
+		.code		= MEDIA_BUS_FMT_UYVY8_2X8,
+		.depth		= 16,
+	}, {
+		.name		= "YUV422-8",
+		.fourcc		= V4L2_PIX_FMT_YUYV,
+		.colorspace	= V4L2_COLORSPACE_RAW,
+		.code		= MEDIA_BUS_FMT_VYUY8_2X8,
+		.depth		= 16,
+	}, {
+		.name		= "SBGGR8",
+		.fourcc         = V4L2_PIX_FMT_SBGGR8,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SBGGR8_1X8,
+		.depth          = 8,
+	}, {
+		.name		= "SGBRG8",
+		.fourcc         = V4L2_PIX_FMT_SGBRG8,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SGBRG8_1X8,
+		.depth          = 8,
+	}, {
+		.name		= "SGRBG8",
+		.fourcc         = V4L2_PIX_FMT_SGRBG8,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SGRBG8_1X8,
+		.depth          = 8,
+	}, {
+		.name		= "SRGGB8",
+		.fourcc         = V4L2_PIX_FMT_SRGGB8,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SRGGB8_1X8,
+		.depth          = 8,
+	}, {
+		.name		= "SBGGR10",
+		.fourcc         = V4L2_PIX_FMT_SBGGR10,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.depth          = 10,
+	}, {
+		.name		= "SGBRG10",
+		.fourcc         = V4L2_PIX_FMT_SGBRG10,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SGBRG10_1X10,
+		.depth          = 10,
+	}, {
+		.name		= "SGRBG10",
+		.fourcc         = V4L2_PIX_FMT_SGRBG10,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SGRBG10_1X10,
+		.depth          = 10,
+	}, {
+		.name		= "SRGGB10",
+		.fourcc         = V4L2_PIX_FMT_SRGGB10,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SRGGB10_1X10,
+		.depth          = 10,
+	}, {
+		.name		= "SBGGR12",
+		.fourcc         = V4L2_PIX_FMT_SBGGR12,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SBGGR12_1X12,
+		.depth          = 12,
+	}, {
+		.name		= "SGBRG12",
+		.fourcc         = V4L2_PIX_FMT_SGBRG12,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SGBRG12_1X12,
+		.depth          = 12,
+	}, {
+		.name		= "SGRBG12",
+		.fourcc         = V4L2_PIX_FMT_SGRBG12,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.depth          = 12,
+	}, {
+		.name		= "SRGGB12",
+		.fourcc         = V4L2_PIX_FMT_SRGGB12,
+		.colorspace     = V4L2_COLORSPACE_RAW,
+		.code           = MEDIA_BUS_FMT_SRGGB12_1X12,
+		.depth          = 12,
+	}, {
+		.name		= "JPEG",
+		.fourcc         = V4L2_PIX_FMT_JPEG,
+		.colorspace     = V4L2_COLORSPACE_JPEG,
+		.code           = MEDIA_BUS_FMT_JPEG_1X8,
+		.depth          = 16,
+	},
+};
+
+/*
+ * fourcc_to_str() - Utility function to display fourcc
+ * @fmt: fourcc value of image format
+ * Return: string equevalent of fourcc value
+ */
+static char *fourcc_to_str(u32 fmt)
+{
+	static unsigned char code[5];
+
+	code[0] = (unsigned char)(fmt & 0xff);
+	code[1] = (unsigned char)((fmt >> 8) & 0xff);
+	code[2] = (unsigned char)((fmt >> 16) & 0xff);
+	code[3] = (unsigned char)((fmt >> 24) & 0xff);
+	code[4] = '\0';
+	return code;
+}
+
+/*
+ *  timeperframe: min/max and default
+ */
+static const struct v4l2_fract fsd_csis_tpf_default = {
+	.numerator = 1001,
+	.denominator = 30000
+};
+
+/*
+ * fsd_csis_clear_vid_irqs() - clear the interrupt sources
+ * @dev: pointer to fsd_csis_dev structure
+ * Return: none
+ */
+static void fsd_csis_clear_vid_irqs(struct fsd_csis_dev *dev)
+{
+	unsigned int int_src = 0;
+
+	int_src = readl(dev->base + CSIS_INT_SRC0);
+	writel(int_src, dev->base + CSIS_INT_SRC0);
+
+	int_src = readl(dev->base + CSIS_INT_SRC1);
+	writel(int_src, dev->base + CSIS_INT_SRC1);
+}
+
+/*
+ * fsd_csis_disable_interrupts() - Disable the interrupt sources by masking
+ * @dev: pointer to fsd_csis_dev structure
+ * Return: none
+ */
+static void fsd_csis_disable_irqs(struct fsd_csis_dev *dev)
+{
+	writel(CSIS_INT_MSK0_MASK_ALL, dev->base + CSIS_INT_MSK0);
+	writel(CSIS_INT_MSK1_MASK_ALL, dev->base + CSIS_INT_MSK1);
+}
+
+/*
+ * fsd_csis_enable_vid_irqs() - Enable the interrupt sources by unmasking
+ * @dev: pointer to fsd_csis_dev structure
+ * Return: none
+ */
+static void fsd_csis_enable_vid_irqs(struct fsd_csis_dev *dev)
+{
+	writel(CSIS_INT_MSK0_ENABLE_ALL, dev->base + CSIS_INT_MSK0);
+	writel(CSIS_INT_MSK1_ENABLE_ALL, dev->base + CSIS_INT_MSK1);
+}
+
+/*
+ * fsd_csis_dphy_reset() - reset and release D-PHY i/f
+ * for the given csi
+ * @dev: pointer to fsd_csis_dev structure
+ * @reset: Reset enable/ disable
+ * Return: none
+ */
+static void fsd_csis_dphy_reset(struct fsd_csis_dev *dev, bool reset)
+{
+	unsigned int dphy = 0, sw_resetn_dphy = 0x0;
+
+	/* There are 4 CSIs per each D-PHY i/f */
+	dphy = dev->id / FSD_CSIS_NB_CSI_PER_PHY;
+	regmap_read(dev->sysreg_map, SW_RESETEN_DPHY, &sw_resetn_dphy);
+
+	/*
+	 * 0: reset
+	 * 1: reset release
+	 */
+	if (reset)
+		sw_resetn_dphy &= reset_bits(CSIS_SW_RESETEN_DPHY_MASK(dphy));
+	else
+		sw_resetn_dphy |= set_bits(CSIS_SW_RESETEN_DPHY, CSIS_SW_RESETEN_DPHY_MASK(dphy));
+
+	regmap_write(dev->sysreg_map, SW_RESETEN_DPHY, sw_resetn_dphy);
+}
+
+/*
+ * fsd_csis_mipi_dphy_init() - initialize D-PHY slave rx parameters
+ * @dev: pointer to fsd_csis_dev structure
+ * Return: none
+ */
+static void fsd_csis_mipi_dphy_init(struct fsd_csis_dev *dev)
+{
+	unsigned int dphy_sctrl = 0;
+
+	dphy_sctrl = readl(dev->base + PHY_SCTRL_H);
+	dphy_sctrl &= reset_bits(SKEW_CAL_MAX_SKEW_CODE_CTRL_MASK | SKEW_CAL_EN_MASK);
+	/* Enable Rx Skew calibration */
+	dphy_sctrl |= set_bits(SKEW_CAL_EN, SKEW_CAL_EN_MASK);
+	/* Set Rx Skew Calibratin to Max Code Control */
+	dphy_sctrl |= set_bits(SKEW_CAL_MAX_SKEW_CODE_CTRL, SKEW_CAL_MAX_SKEW_CODE_CTRL_MASK);
+	writel(dphy_sctrl, dev->base + PHY_SCTRL_H);
+}
+
+/*
+ * fsd_csis_set_hs_settle() - set HSsettle[7:0] value for PHY
+ * @dev: pointer to fsd_csis_dev structure
+ * @hs_settle: HS-Rx Settle time
+ * Return: none
+ */
+static void fsd_csis_set_hs_settle(struct fsd_csis_dev *dev, unsigned int hs_settle)
+{
+	u32 phy_cmn_ctrl;
+
+	phy_cmn_ctrl = readl(dev->base + PHY_CMN_CTRL);
+	phy_cmn_ctrl &= reset_bits(HSSETTLE_MASK);
+	phy_cmn_ctrl |= set_bits(hs_settle, HSSETTLE_MASK);
+	writel(phy_cmn_ctrl, (dev->base + PHY_CMN_CTRL));
+}
+
+/*
+ * fsd_csis_setclk_settle_ctl() - set slave clock lane settle time
+ * @dev: pointer to fsd_csis_dev structure
+ * @clksettlectl: T-CLK_SETTLE value
+ * Return: none
+ */
+static void fsd_csis_setclk_settle_ctl(struct fsd_csis_dev *dev, unsigned int clksettlectl)
+{
+	u32 phy_cmn_ctrl;
+
+	phy_cmn_ctrl = readl(dev->base + PHY_CMN_CTRL);
+	phy_cmn_ctrl &= reset_bits(S_CLKSETTLE_MASK);
+	phy_cmn_ctrl |= set_bits(clksettlectl, S_CLKSETTLE_MASK);
+	writel(phy_cmn_ctrl, (dev->base + PHY_CMN_CTRL));
+}
+
+/*
+ * fsd_csis_enable_deskew_logic()- enable or disable DeSkew logic
+ * @dev: pointer to fsd_csis_dev structure
+ * @enable: boolean value enable = true/ disable = false
+ * Return: none
+ */
+static void fsd_csis_enable_deskew_logic(struct fsd_csis_dev *dev, bool enable)
+{
+	u32 csis_cmn_ctrl;
+
+	/* CSIS de-skew logic */
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+	csis_cmn_ctrl &= reset_bits(DESKEW_ENABLE_MASK);
+
+	if (enable)
+		csis_cmn_ctrl |= set_bits(DESKEW_ENABLE, DESKEW_ENABLE_MASK);
+	writel(csis_cmn_ctrl, (dev->base + CSIS_CMN_CTRL));
+}
+
+/*
+ * fsd_csis_update_shadow_ctx() - update the CSI configuration
+ * @ctx: pointer to CSI context
+ * Return: none
+ */
+static void fsd_csis_update_shadow_ctx(struct fsd_csis_ctx *ctx)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	u32 csis_cmn_ctrl, vc = ctx->virtual_channel;
+
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+	csis_cmn_ctrl |= set_bits(UPDATE_SHADOW, UPDATE_SHADOW_CH_MASK(vc));
+	writel(csis_cmn_ctrl, (dev->base + CSIS_CMN_CTRL));
+}
+
+/*
+ * fsd_csis_set_update_shadow_ctrl() - set the shadow registers update control
+ * @dev: pointer to csis device structure
+ * @update_shado_ctrl: boolean value to set or reset shadow control
+ * Return: none
+ */
+static void fsd_csis_set_update_shadow_ctrl(struct fsd_csis_dev *dev, bool update)
+{
+	u32 csis_cmn_ctrl;
+
+	/* CSIS Update Shadow control */
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+	csis_cmn_ctrl &= reset_bits(UPDATE_SHADOW_CTRL_MASK);
+
+	if (update)
+		csis_cmn_ctrl |= set_bits(UPDATE_SHADOW_CTRL, UPDATE_SHADOW_CTRL_MASK);
+	writel(csis_cmn_ctrl, (dev->base + CSIS_CMN_CTRL));
+}
+
+/*
+ * fsd_csis_set_clkgate_trail() - set the trailing clocks for ISP i/f
+ * @ctx: csis context structure for this stream
+ * @clkgate_trail: number of trailing clocks
+ * Return: none
+ */
+static void fsd_csis_set_clkgate_trail(struct fsd_csis_ctx *ctx, unsigned short clkgate_trail)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int csis_clk_ctrl, vc = ctx->virtual_channel;
+
+	csis_clk_ctrl = readl(dev->base + CSIS_CLK_CTRL);
+	csis_clk_ctrl &= reset_bits(CLKGATE_TRAIL_MASK(vc));
+	csis_clk_ctrl |= set_bits(clkgate_trail, CLKGATE_TRAIL_MASK(vc));
+
+	writel(csis_clk_ctrl, dev->base + CSIS_CLK_CTRL);
+}
+
+/*
+ * fsd_csis_set_clkgate_en() - enable clock gating for Pixel clock
+ * @ctx: csis context structure for this stream
+ * @clk_gate_en: boolean value to enable or disable pixel clock gating
+ * Return: none
+ */
+static void fsd_csis_set_clkgate_en(struct fsd_csis_ctx *ctx, bool clk_gate_en)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int csis_clk_ctrl, vc = ctx->virtual_channel;
+
+	csis_clk_ctrl = readl(dev->base + CSIS_CLK_CTRL);
+	csis_clk_ctrl &= reset_bits(CLKGATE_EN_MASK(vc));
+
+	if (clk_gate_en)
+		csis_clk_ctrl |= set_bits(CLKGATE_EN, CLKGATE_EN_MASK(vc));
+
+	writel(csis_clk_ctrl, dev->base + CSIS_CLK_CTRL);
+}
+
+/*
+ * fsd_csis_set_vc_passing() - select the Virtual Channel for processing
+ * @ctx: csis context structure for this stream
+ * Return: none
+ */
+static void fsd_csis_set_vc_passing(struct fsd_csis_ctx *ctx)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int vc_passing;
+	unsigned int vc = ctx->virtual_channel;
+
+	vc_passing = readl(dev->base + VC_PASSING);
+	vc_passing &= reset_bits(VC_PASSING_MASK);
+	vc_passing |= set_bits(vc, VC_PASSING_MASK);
+	vc_passing |= set_bits(VC_PASSING_ENABLE, VC_PASSING_ENABLE_MASK);
+	writel(vc_passing, dev->base + VC_PASSING);
+}
+
+/*
+ * fsd_csis_set_dma_clk() - set the number of trailing clocks for DMA clock gating
+ * @dev: pointer to fsd_csis_dev structure
+ * Return: none
+ */
+static void fsd_csis_set_dma_clk(struct fsd_csis_dev *dev)
+{
+	unsigned int dma_clk_ctrl = 0x0;
+
+	dma_clk_ctrl = readl(dev->base + DMA_CLK_CTRL);
+	dma_clk_ctrl &= reset_bits(DMA_CLK_GATE_EN_MASK);
+	dma_clk_ctrl |= set_bits(DMA_CLK_GATE_TRAIL, DMA_CLK_GATE_TRAIL_MASK);
+	writel(dma_clk_ctrl, dev->base + DMA_CLK_CTRL);
+}
+
+/*
+ * fsd_csis_sw_reset() - Soft reset the CSI instance
+ * @dev: pointer to fsd_csis_dev structure
+ * Return: none
+ */
+static void fsd_csis_sw_reset(struct fsd_csis_dev *dev)
+{
+	u32 csis_cmn_ctrl = 0;
+
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+
+	/* Disable CSI first */
+	csis_cmn_ctrl &= reset_bits(CSI_EN_MASK);
+	writel(csis_cmn_ctrl, dev->base + CSIS_CMN_CTRL);
+
+	/* SW Reset CSI */
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+	csis_cmn_ctrl |= set_bits(SW_RESET, SW_RESET_MASK);
+
+	while (csis_cmn_ctrl & SW_RESET_MASK) {
+		writel(csis_cmn_ctrl, dev->base + CSIS_CMN_CTRL);
+		usleep_range(1000, 2000); /* Wait min 10ms, max 20ms */
+		csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+	}
+}
+
+/*
+ * fsd_csis_set_num_of_datalane() - Configure the number of data lanes for use
+ * @dev: pointer to fsd_csis_dev structure
+ * @nb_data_lane: number of data lanes to configure
+ * Return: 0 or -EINVAL
+ */
+static int fsd_csis_set_num_of_datalane(struct fsd_csis_dev *dev, unsigned int nb_data_lane)
+{
+	u32 csis_cmn_ctrl = 0, csis_nb_lane = nb_data_lane - 1;
+
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+	csis_cmn_ctrl &= reset_bits(LANE_NUMBER_MASK);
+
+	switch (csis_nb_lane) {
+	case DATALANE0:
+	case DATALANE1:
+	case DATALANE2:
+	case DATALANE3:
+		csis_cmn_ctrl |= set_bits(csis_nb_lane, LANE_NUMBER_MASK);
+		break;
+	default:
+		fsd_csis_err(dev, "Wrong number of data lanes %d to configure!\n", nb_data_lane);
+		return -EINVAL;
+	}
+	writel(csis_cmn_ctrl, dev->base + CSIS_CMN_CTRL);
+	return 0;
+}
+
+/*
+ * fsd_csis_set_phy_on() - turn on or off the PHY
+ * @dev: pointer to fsd_csis_dev structure
+ * @nb_data_lane: number of data lanes in use by this CSI instance
+ * Return: none
+ */
+static void fsd_csis_set_phy_on(struct fsd_csis_dev *dev, unsigned int nb_data_lane)
+{
+	u32 phy_cmn_ctrl;
+
+	phy_cmn_ctrl = readl(dev->base + PHY_CMN_CTRL);
+	phy_cmn_ctrl &= reset_bits((ENABLE_DAT_MASK | S_BYTE_CLK_ENABLE_MASK | ENABLE_CLK_MASK));
+	phy_cmn_ctrl |= set_bits(ENABLE_DAT(nb_data_lane), ENABLE_DAT_MASK);
+	phy_cmn_ctrl |= set_bits(S_BYTE_CLK_ENABLE, S_BYTE_CLK_ENABLE_MASK);
+	phy_cmn_ctrl |= set_bits(ENABLE_CLK, ENABLE_CLK_MASK);
+	writel(phy_cmn_ctrl, dev->base + PHY_CMN_CTRL);
+
+	fsd_csis_dbg(3, dev, "Data lane %d phy_cmn_ctrl %x\n", nb_data_lane, phy_cmn_ctrl);
+}
+
+/*
+ * fsd_csis_set_pixel_mode() - set pixel i/f OTF mode
+ * to single/dual/quad/octa pixel mode
+ * @ctx: pointer to CSI context
+ * @vc: virtual channel id
+ * @fmt: image format information
+ * Return: none
+ */
+static void fsd_csis_set_pixel_mode(struct fsd_csis_ctx *ctx, unsigned int vc,
+				    const struct fsd_csis_fmt *fmt)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int fourcc = fmt->fourcc;
+	u32 isp_config_ch;
+	unsigned int pixel_mode;
+
+	switch (fourcc) {
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SGBRG8:
+	case V4L2_PIX_FMT_SGRBG8:
+	case V4L2_PIX_FMT_SRGGB8:
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_BGR666:
+	case V4L2_PIX_FMT_RGB24:
+		pixel_mode = QUAD_PIXEL_MODE;
+		break;
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+	case V4L2_PIX_FMT_RGB565:
+		pixel_mode = OCTA_PIXEL_MODE;
+		break;
+	default:
+		pixel_mode = SINGLE_PIXEL_MODE;
+		break;
+	}
+
+	fsd_csis_ctx_dbg(3, ctx, "Selected PIXEL_MODE: %u\n", pixel_mode);
+	isp_config_ch = readl(dev->base + ISP_CONFIG_CH0 + ISP_CH_OFFSET * vc);
+	isp_config_ch &= reset_bits(PIXEL_MODE_MASK);
+	isp_config_ch |= set_bits(pixel_mode, PIXEL_MODE_MASK);
+	writel(isp_config_ch, dev->base + ISP_CONFIG_CH0 + ISP_CH_OFFSET * vc);
+}
+
+/*
+ * fsd_csis_set_paralle_mode() - configure pixel alignmnet for OTF i/f
+ * @ctx: pointer to CSI context
+ * @data_align: parallel mode value indicating alignment
+ * Return: none
+ */
+static void fsd_csis_set_paralle_mode(struct fsd_csis_ctx *ctx,
+				      enum FSD_CSIS_PARALLEL_MODE data_align)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	u32 isp_config_ch, vc = ctx->virtual_channel;
+
+	isp_config_ch = readl(dev->base + ISP_CONFIG_CH0 + ISP_CH_OFFSET * vc);
+	isp_config_ch &= reset_bits(PARALLEL_MODE_MASK);
+	isp_config_ch |= set_bits(data_align, PARALLEL_MODE_MASK);
+	writel(isp_config_ch, dev->base + ISP_CONFIG_CH0 + ISP_CH_OFFSET * vc);
+}
+
+/*
+ * fsd_csis_set_img_fmt() - configure selected image format for streaming
+ * @ctx: pointer to CSI context
+ * @vc: virtual channel id
+ * @fmt: format to configure
+ * Return: none
+ */
+static void fsd_csis_set_img_fmt(struct fsd_csis_ctx *ctx, unsigned int vc,
+				 const struct fsd_csis_fmt *fmt)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int isp_config_ch, fourcc = fmt->fourcc;
+
+	isp_config_ch = readl(dev->base + ISP_CONFIG_CH0 + ISP_CH_OFFSET * vc);
+	isp_config_ch &= reset_bits(DATAFORMAT_MASK);
+
+	switch (fourcc) {
+	case V4L2_PIX_FMT_RGB565:
+		/* RGB565 */
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_RGB565, DATAFORMAT_MASK);
+		break;
+	case V4L2_PIX_FMT_BGR666:
+		/* RGB666 */
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_RGB666, DATAFORMAT_MASK);
+		break;
+	case V4L2_COLORSPACE_SRGB:
+	case V4L2_PIX_FMT_XRGB32:
+	case V4L2_PIX_FMT_RGB24:
+	case V4L2_PIX_FMT_RGB32:
+		/* RGB888 */
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_RGB888, DATAFORMAT_MASK);
+		break;
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_UYVY:
+		/* YUYV-16/YUV422-8, UYVY-16 / YUV 422 */
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_YUV422_8, DATAFORMAT_MASK);
+		fsd_csis_set_paralle_mode(ctx, FSD_CSIS_PARALLEL_MODE_OFF);
+		break;
+	case V4L2_PIX_FMT_SGBRG8:
+	case V4L2_PIX_FMT_SGRBG8:
+	case V4L2_PIX_FMT_SRGGB8:
+		/* SGBRG8 / RAW8*/
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_RAW8, DATAFORMAT_MASK);
+		break;
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_RAW10, DATAFORMAT_MASK);
+		break;
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+		/* SRGGB12, SGRBG12, SGBRG12, SBGGR12 / RAW-12 */
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_RAW12, DATAFORMAT_MASK);
+		fsd_csis_set_paralle_mode(ctx, FSD_CSIS_PARALLEL_MODE_OFF);
+		break;
+	case V4L2_PIX_FMT_SBGGR14P:
+	case V4L2_PIX_FMT_SGBRG14P:
+	case V4L2_PIX_FMT_SGRBG14P:
+	case V4L2_PIX_FMT_SRGGB14P:
+		/* SBGGR14, SGBRG14, SGRRBG14, SRGGB14 / RAW14 */
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_RAW14, DATAFORMAT_MASK);
+		break;
+	case V4L2_PIX_FMT_SGBRG16:
+	case V4L2_PIX_FMT_SGRBG16:
+	case V4L2_PIX_FMT_SRGGB16:
+		/* SGBRG16, SGRBG16, SRGGB16 / RAW16 */
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_RAW16, DATAFORMAT_MASK);
+		break;
+	case V4L2_PIX_FMT_JPEG:
+		/* JPEG */
+		isp_config_ch |= set_bits(ISP_DATA_FORMAT_USER_DEFINED_2, DATAFORMAT_MASK);
+		break;
+	default:
+		fsd_csis_ctx_err(ctx, "image format %x not supported\n", fourcc);
+		break;
+	}
+
+	isp_config_ch &= reset_bits(VIRTUAL_CHANNEL_MASK);
+	isp_config_ch |= set_bits(vc, VIRTUAL_CHANNEL_MASK);
+	writel(isp_config_ch, dev->base + ISP_CONFIG_CH0 + ISP_CH_OFFSET * vc);
+	fsd_csis_ctx_dbg(3, ctx, "format %x set\n", fourcc);
+}
+
+/*
+ * fsd_csis_set_resolution() - configure selected resolution for streaming
+ * @ctx: pointer to CSI context
+ * @vc: virtual channel id
+ * @width: horizontal image resolution
+ * @height: vertical image resolution
+ * Return: none
+ */
+static void fsd_csis_set_resolution(struct fsd_csis_ctx *ctx, unsigned int vc, unsigned int width,
+				    unsigned int height)
+{
+	u32 isp_resol_ch = 0;
+	struct fsd_csis_dev *dev = ctx->dev;
+
+	isp_resol_ch &= reset_bits((HRESOL_MASK | VRESOL_MASK));
+	isp_resol_ch |= set_bits(width, HRESOL_MASK);
+	isp_resol_ch |= set_bits(height, VRESOL_MASK);
+	writel(isp_resol_ch, dev->base + ISP_RESOL_CH0 + ISP_CH_OFFSET * vc);
+	fsd_csis_ctx_dbg(3, ctx, "resolution %08dx%08d set\n", width, height);
+}
+
+/*
+ * fsd_csis_format_size() - set image size for selected resolution
+ * @ctx: pointer to CSI context
+ * @fmt: image format
+ * @f: format whose size to be updated
+ * Return: 0
+ */
+static int fsd_csis_format_size(struct fsd_csis_ctx *ctx, const struct fsd_csis_fmt *fmt,
+				struct v4l2_format *f)
+{
+	if (!fmt) {
+		fsd_csis_ctx_err(ctx, "No format provided\n");
+		return -EINVAL;
+	}
+
+	v4l_bound_align_image(&f->fmt.pix.width, FSD_CSIS_WMIN, FSD_CSIS_WMAX, FSD_CSIS_WALIGN,
+			      &f->fmt.pix.height, FSD_CSIS_HMIN, FSD_CSIS_HMAX, FSD_CSIS_HALIGN,
+			      FSD_CSIS_SALIGN);
+
+	f->fmt.pix.bytesperline = bytes_per_line(f->fmt.pix.width, fmt->depth);
+	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+	fsd_csis_set_resolution(ctx, ctx->virtual_channel, f->fmt.pix.width, f->fmt.pix.height);
+
+	fsd_csis_ctx_dbg(3, ctx, "fourcc %s width %d height %d bpl %d img size %d set\n",
+			 fourcc_to_str(f->fmt.pix.pixelformat), f->fmt.pix.width, f->fmt.pix.height,
+			 f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+	return 0;
+}
+
+/*
+ * fsd_csis_set_hsync_lintv_timing() - set Hsync_Lintv value for CSI
+ * @ctx: pointer to CSI context
+ * @vc: virtual channel id
+ * @hsync_lintv: interval between last falling of DVALID and falling of HSYNC
+ * Return: none
+ */
+static void fsd_csis_set_hsync_lintv_timing(struct fsd_csis_ctx *ctx, unsigned int vc,
+					    unsigned int hsync_lintv)
+{
+	u32 isp_sync_ch;
+	struct fsd_csis_dev *dev = ctx->dev;
+
+	isp_sync_ch = readl(dev->base + ISP_SYNC_CH0 + ISP_CH_OFFSET * vc);
+	isp_sync_ch &= reset_bits(HSYNC_LINTV_MASK);
+	isp_sync_ch |= set_bits(hsync_lintv, HSYNC_LINTV_MASK);
+	writel(isp_sync_ch, dev->base + ISP_SYNC_CH0 + ISP_CH_OFFSET * vc);
+}
+
+/*
+ * fsd_csis_set_pack() - select DMA memory storing style
+ * @dev: pointer to fsd_csis_dev structure
+ * @vc: virtual channel id
+ * @dma_pack: 1: Memory storing style is 1 dimension/ 0: 2 Dimension
+ * Return: none
+ */
+static void fsd_csis_set_pack(struct fsd_csis_dev *dev, u32 vc, enum FSD_CSIS_DMA_PACK dma_pack)
+{
+	u32 dma_fmt;
+
+	dma_fmt = readl(dev->base + DMA0_FMT + vc * DMA_ADDR_OFFSET);
+	dma_fmt &= reset_bits(ACTIVE_DMA_PACK_MASK);
+	dma_fmt |= set_bits(dma_pack, ACTIVE_DMA_PACK_MASK);
+	writel(dma_fmt, dev->base + DMA0_FMT + vc * DMA_ADDR_OFFSET);
+}
+
+/*
+ * fsd_csis_set_dma_dump() - set DMA dump OTF output without realigning
+ * @dev: pointer to fsd_csis_dev structure
+ * @vc: virtual channel id
+ * @set_dump: boolean value enable = true/ disable = false
+ * Return: none
+ */
+static void fsd_csis_set_dma_dump(struct fsd_csis_dev *dev, unsigned int vc, bool set_dump)
+{
+	u32 dma_fmt;
+
+	dma_fmt = readl(dev->base + DMA0_FMT + vc * DMA_ADDR_OFFSET);
+	dma_fmt &= reset_bits(DMA_DUMP_MASK);
+
+	if (set_dump)
+		dma_fmt |= set_bits(DMA_DUMP_OTF, DMA_DUMP_MASK);
+
+	writel(dma_fmt, dev->base + DMA0_FMT + vc * DMA_ADDR_OFFSET);
+}
+
+/*
+ * fsd_csis_set_dma_dimension() - set DMA memory storing style
+ * @dev: pointer to fsd_csis_dev structure
+ * @vc: virtual channel id
+ * @set_dim: 0: Normal (2D DMA)/ 1: 1D DMA
+ * Return: none
+ */
+static void fsd_csis_set_dma_dimension(struct fsd_csis_dev *dev, unsigned int vc, bool set_dim)
+{
+	u32 dma_fmt;
+
+	dma_fmt = readl(dev->base + DMA0_FMT + vc * DMA_ADDR_OFFSET);
+	dma_fmt &= reset_bits(ACTIVE_DMA_DIM_MASK);
+
+	if (set_dim)
+		dma_fmt |= set_bits(DMA_DIM_1D, ACTIVE_DMA_DIM_MASK);
+
+	writel(dma_fmt, dev->base + DMA0_FMT + vc * DMA_ADDR_OFFSET);
+}
+
+/*
+ * fsd_csis_set_dma_format() - set DMA format based
+ * on selected image format
+ * @ctx: pointer to CSI context
+ * @fmt: image format
+ * Return: none
+ */
+static void fsd_csis_set_dma_format(struct fsd_csis_ctx *ctx, const struct fsd_csis_fmt *fmt)
+{
+	unsigned int fourcc = fmt->fourcc;
+
+	switch (fourcc) {
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+		fsd_csis_set_pack(ctx->dev, ctx->virtual_channel, DMA_PACK_10);
+		break;
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+		fsd_csis_set_pack(ctx->dev, ctx->virtual_channel, DMA_PACK_12);
+		break;
+	case V4L2_PIX_FMT_SBGGR14P:
+		fsd_csis_set_pack(ctx->dev, ctx->virtual_channel, DMA_PACK_14);
+		break;
+	case V4L2_PIX_FMT_BGR666:
+		fsd_csis_set_pack(ctx->dev, ctx->virtual_channel, DMA_PACK_18);
+		break;
+	default:
+		/* Default DMA_PACK_NORMAL will be used */
+		break;
+	}
+
+	fsd_csis_set_dma_dump(ctx->dev, ctx->virtual_channel, false);
+	fsd_csis_set_dma_dimension(ctx->dev, ctx->virtual_channel, false);
+}
+
+/*
+ * fsd_csis_dma_enable() - enable/disable DMA
+ * @ctx: pointer to CSI context
+ * @en_dma: boolean value enable = true/ disable = false
+ * Return: none
+ */
+static void fsd_csis_dma_enable(struct fsd_csis_ctx *ctx, bool en_dma)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int dma_ctrl, vc = ctx->virtual_channel;
+
+	dma_ctrl = readl(dev->base + DMA0_CTRL + DMA_CH_OFFSET * vc);
+	/* DMA disable = 'b1, enable = 'b0 */
+	dma_ctrl |= set_bits(DMA_DISABLE, DMA_DISABLE_MASK);
+
+	if (en_dma)
+		dma_ctrl &= reset_bits(DMA_DISABLE_MASK);
+	writel(dma_ctrl, dev->base + DMA0_CTRL + DMA_CH_OFFSET * vc);
+}
+
+/*
+ * fsd_csis_set_interleave_mode() - set interleaving mode
+ * @dev: pointer to fsd_csis_dev structure
+ * @fsd_csis_interleave_mode: interleave mode value
+ * Return: none
+ */
+static void fsd_csis_set_interleave_mode(struct fsd_csis_dev *dev,
+					 enum FSD_CSIS_INTERLEAVE csis_interleave_mode)
+{
+	u32 csis_cmn_ctrl;
+
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+	csis_cmn_ctrl &= reset_bits(INTERLEAVE_MODE_MASK);
+	csis_cmn_ctrl |= set_bits(csis_interleave_mode, INTERLEAVE_MODE_MASK);
+	writel(csis_cmn_ctrl, dev->base + CSIS_CMN_CTRL);
+}
+
+/*
+ * fsd_csis_enable_irqs_for_ctx() - enable interrupts for CSI context
+ * @ctx: pointer to CSI context
+ * Return: none
+ */
+static void fsd_csis_enable_irqs_for_ctx(struct fsd_csis_ctx *ctx)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int int_mask, vc = ctx->virtual_channel;
+
+	int_mask = readl(dev->base + CSIS_INT_MSK0);
+	int_mask |= set_bits(ERR_SOT_HS_ENABLE, ERR_SOT_HS_CH_MASK(vc));
+	int_mask |= set_bits(ERR_LOST_FS_ENABLE, ERR_LOST_FS_CH_MASK(vc));
+	int_mask |= set_bits(ERR_LOST_FE_ENABLE, ERR_LOST_FE_CH_MASK(vc));
+	writel(int_mask, dev->base + CSIS_INT_MSK0);
+
+	int_mask = readl(dev->base + CSIS_INT_MSK1);
+	int_mask |= set_bits(DMA_OTF_OVERLAP_ENABLE, DMA_OTF_OVERLAP_CH_MASK(vc));
+	int_mask |= set_bits(DMA_FRM_END_ENABLE, DMA_FRM_END_CH_MASK(vc));
+	int_mask |= set_bits(DMA_ABORT_ENABLE, DMA_ABORT_DONE_MASK);
+	int_mask |= set_bits(DMA_ERROR_ENABLE, DMA_ERROR_MASK);
+	writel(int_mask, dev->base + CSIS_INT_MSK1);
+}
+
+/*
+ * fsd_csis_disable_irqs_for_ctx() - disable interrupts for CSI context
+ * @ctx: pointer to CSI context
+ * Return: none
+ */
+static void fsd_csis_disable_irqs_for_ctx(struct fsd_csis_ctx *ctx)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int int_mask, vc = ctx->virtual_channel;
+
+	int_mask = readl(dev->base + CSIS_INT_MSK0);
+	int_mask &= reset_bits(FRAMESTART_CH_MASK(vc));
+	int_mask &= reset_bits(FRAMEEND_CH_MASK(vc));
+	int_mask &= reset_bits(ERR_SOT_HS_CH_MASK(vc));
+	int_mask &= reset_bits(ERR_LOST_FS_CH_MASK(vc));
+	int_mask &= reset_bits(ERR_LOST_FE_CH_MASK(vc));
+	writel(int_mask, dev->base + CSIS_INT_MSK0);
+
+	int_mask = readl(dev->base + CSIS_INT_MSK1);
+	int_mask &= reset_bits(DMA_OTF_OVERLAP_CH_MASK(vc));
+	int_mask &= reset_bits(DMA_FRM_END_CH_MASK(vc));
+	int_mask &= reset_bits(LINE_END_CH_MASK(vc));
+	int_mask &= reset_bits(DMA_ABORT_DONE_MASK);
+	int_mask &= reset_bits(DMA_ERROR_MASK);
+	writel(int_mask, dev->base + CSIS_INT_MSK1);
+}
+
+/*
+ * fsd_csis_dma_set_vid_base_addr() - set the DMA address for streaming
+ * @ctx: pointer to CSI context
+ * @frm_no: frame number for which DMA address to be set
+ * @addr: address to use by CSI DMA
+ * Return: none
+ */
+static void fsd_csis_dma_set_vid_base_addr(struct fsd_csis_ctx *ctx, int frm_no, unsigned long addr)
+{
+	struct fsd_csis_dev *dev = ctx->dev;
+	unsigned int vc = ctx->virtual_channel;
+	unsigned int dma_addr = 0;
+
+	dma_addr = DMA0_ADDR1 + DMA_CH_OFFSET * vc;
+	dma_addr = dma_addr + (frm_no * 4);
+	mutex_lock(&dev->mutex_csis_dma_reg);
+	writel(addr, dev->base + dma_addr);
+	mutex_unlock(&dev->mutex_csis_dma_reg);
+}
+
+/*
+ * fsd_csis_ip_configure() - configure CSI instance for streaming
+ * @ctx: pointer to fsd_csis_ctx structure
+ * Return: 0 on success. error value otherwise
+ */
+static void fsd_csis_ip_configure(struct fsd_csis_ctx *ctx)
+{
+	unsigned int i;
+	struct fsd_csis_dev *dev;
+
+	dev = ctx->dev;
+	/*
+	 * Caution: CSI is reset every time during configuration
+	 * as recommended by initialization sequence.
+	 * In multi-stream scenario, reset should be avoided and
+	 * only format related configuration should be done
+	 */
+	fsd_csis_dphy_reset(dev, true);
+	fsd_csis_sw_reset(dev);
+	fsd_csis_mipi_dphy_init(dev);
+	fsd_csis_set_vc_passing(ctx);
+
+	if (!dev->nb_data_lane)
+		dev->nb_data_lane = ctx->endpoint.bus.mipi_csi2.num_data_lanes;
+	fsd_csis_set_interleave_mode(dev, VC_DT_BOTH);
+	fsd_csis_set_update_shadow_ctrl(dev, true);
+
+	/* DeSkew logic is needed when data lane speed is above or equal to 1500Mbps */
+	if (dev->lane_speed >= 1500)
+		fsd_csis_enable_deskew_logic(dev, true);
+	fsd_csis_set_hs_settle(dev, S_HSSETTLECTL_VAL);
+	fsd_csis_setclk_settle_ctl(dev, S_CLKSETTLECTL_VAL);
+	fsd_csis_set_num_of_datalane(dev, dev->nb_data_lane);
+
+	for (i = 0; i < FSD_CSIS_MAX_VC; i++) {
+		if (dev->ctx[i]) {
+			fsd_csis_set_clkgate_en(dev->ctx[i], true);
+			fsd_csis_set_clkgate_trail(dev->ctx[i], CLKGATE_TRAIL_VAL);
+		}
+	}
+
+	fsd_csis_set_phy_on(dev, dev->nb_data_lane);
+
+	for (i = 0; i < FSD_CSIS_MAX_VC; i++) {
+		struct fsd_csis_ctx *temp_ctx = ctx->dev->ctx[i];
+
+		if (temp_ctx) {
+			fsd_csis_set_pixel_mode(temp_ctx, temp_ctx->virtual_channel, temp_ctx->fmt);
+			fsd_csis_set_img_fmt(temp_ctx, temp_ctx->virtual_channel, temp_ctx->fmt);
+			fsd_csis_format_size(temp_ctx, temp_ctx->fmt, &temp_ctx->v_fmt);
+			fsd_csis_set_hsync_lintv_timing(temp_ctx, temp_ctx->virtual_channel,
+							HSYNC_LINTV);
+			fsd_csis_set_dma_format(temp_ctx, temp_ctx->fmt);
+			fsd_csis_update_shadow_ctx(temp_ctx);
+			fsd_csis_dma_enable(temp_ctx, false);
+		}
+	}
+
+	fsd_csis_set_dma_clk(dev);
+	fsd_csis_dphy_reset(dev, false);
+	fsd_csis_clear_vid_irqs(dev);
+
+	for (i = 0; i < FSD_CSIS_MAX_VC; i++) {
+		struct fsd_csis_ctx *temp_ctx = ctx->dev->ctx[i];
+
+		if (temp_ctx && ctx_stream_enabled(temp_ctx))
+			fsd_csis_enable_irqs_for_ctx(temp_ctx);
+	}
+}
+
+/*
+ * fsd_csis_irq_handler() - interrupt handler for CSI instance
+ * @irq_csis: interrupt number of this CSI instance
+ * @data: device structure of the CSI instance
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t fsd_csis_irq_handler(int irq_csis, void *data)
+{
+	struct fsd_csis_dev *dev;
+	struct fsd_csis_ctx *ctx;
+	int vc;
+	unsigned int int_src0 = 0x0, int_src1 = 0x0;
+	unsigned int dma_frame_end = 0x0;
+	unsigned int dma_frame_end_vc = 0x0;
+	unsigned int int0_err = 0x0, int1_err = 0x0;
+	unsigned int dma_error_code = 0x0, dma_error_vc = 0;
+
+	dev = data;
+	int_src0 = readl(dev->base + CSIS_INT_SRC0);
+	int_src1 = readl(dev->base + CSIS_INT_SRC1);
+	int0_err = get_bits(int_src0, CSIS_INT_SRC0_ERR_ALL_MASK);
+	int1_err = get_bits(int_src1, CSIS_INT_SRC1_ERR_ALL_MASK);
+	dma_frame_end = get_bits(int_src1, DMA_FRM_END_MASK);
+
+	if (dma_frame_end || int1_err) {
+		for (vc = 0; vc < FSD_CSIS_MAX_VC; vc++) {
+			dma_frame_end_vc = (dma_frame_end >> vc) & 0x01;
+			ctx = dev->ctx[vc];
+
+			if (ctx) {
+				if (int1_err) {
+					dma_error_vc = get_bits(int_src1,
+								DMA_OTF_OVERLAP_CH_MASK(vc));
+					if (get_bits(int_src1, DMA_ERROR_MASK)) {
+						dma_error_code = get_bits(int_src1, DMA_ERR_CODE);
+						dma_error_vc |= get_bits(dma_error_code,
+									 (DMAFIFO_FULL_MASK |
+									  TRXFIFO_FULL_MASK));
+						dma_error_vc |= get_bits(dma_error_code,
+									 BRESP_ERROR_CH_MASK(vc));
+					}
+				}
+
+				if (dma_frame_end_vc || dma_error_vc) {
+					ctx->dma_error = dma_error_vc;
+					schedule_work(&ctx->csis_ctx_work);
+				}
+			}
+		}
+	}
+
+	if (int0_err)
+		fsd_csis_dbg(1, dev, "CSIS_INT_SRC0 ERRORS OCCURRED!: %08x\n", int0_err);
+
+	if (int1_err)
+		fsd_csis_dbg(1, dev, "DMA ERRORS OCCURRED!: %08x\n", int1_err);
+
+	/* clear the interrupts */
+	writel(int_src0, dev->base + CSIS_INT_SRC0);
+	writel(int_src1, dev->base + CSIS_INT_SRC1);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * fsd_csis_add_to_ring_buffer() - add vb2 buffer to DMA
+ * @ctx: pointer to CSI context
+ * @buf: pointer to fsd_csis_buffer structure
+ * @index: index of DMA buffer address
+ * Return: none
+ */
+static void fsd_csis_add_to_ring_buffer(struct fsd_csis_ctx *ctx,
+					struct fsd_csis_buffer *buf, u8 index)
+{
+	ctx->frame[index] = buf;
+	ctx->frame_addr[index] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+	fsd_csis_dma_set_vid_base_addr(ctx, index, ctx->frame_addr[index]);
+}
+
+/*
+ * fsd_csis_irq_worker() - worker thread processing receieved image in DMA
+ * @work: pointer to work_struct
+ * Return: none
+ */
+static void fsd_csis_irq_worker(struct work_struct *work)
+{
+	struct fsd_csis_ctx *ctx =
+		container_of(work, struct fsd_csis_ctx, csis_ctx_work);
+	struct fsd_csis_buffer *buf_from;
+	struct fsd_csis_buffer *buf_to;
+	struct fsd_csis_dmaqueue *vidq = &ctx->vidq;
+	unsigned int i;
+
+	if (atomic_read(&ctx->end_irq_worker) == 0)
+		return;
+
+	ctx->current_dma_ptr = fsd_csis_current_dma_ptr(ctx);
+	ctx->current_frame_counter = fsd_csis_current_frame_counter(ctx);
+
+	if (ctx->dma_error) {
+		ctx->prev_dma_ptr = ctx->current_dma_ptr;
+		goto update_prev_counters;
+	}
+
+	if (ctx->current_dma_ptr >= ctx->prev_dma_ptr)
+		ctx->number_of_ready_bufs = ctx->current_dma_ptr - ctx->prev_dma_ptr;
+	else
+		ctx->number_of_ready_bufs = FSD_CSIS_NB_DMA_OUT_CH - ctx->prev_dma_ptr +
+					    ctx->current_dma_ptr;
+
+	for (i = 0; i < ctx->number_of_ready_bufs; i++) {
+		ctx->prev_dma_ptr = (ctx->prev_dma_ptr + 1) % FSD_CSIS_NB_DMA_OUT_CH;
+
+		mutex_lock(&ctx->mutex_buf);
+
+		/*
+		 * Before dequeuing buffer from DMA at least
+		 * one buffer should be ready in vb2_queue
+		 */
+		if (list_empty(&vidq->active)) {
+			mutex_unlock(&ctx->mutex_buf);
+			fsd_csis_ctx_info(ctx, "active buffer queue empty\n");
+			ctx->prev_dma_ptr = ctx->current_dma_ptr;
+			goto update_prev_counters;
+
+		} else {
+			buf_from = list_entry(vidq->active.next, struct fsd_csis_buffer, list);
+			list_del(&buf_from->list);
+		}
+
+		mutex_unlock(&ctx->mutex_buf);
+		buf_to = ctx->frame[ctx->prev_dma_ptr];
+		fsd_csis_add_to_ring_buffer(ctx, buf_from, ctx->prev_dma_ptr);
+
+		if (buf_to) {
+			buf_to->vb.vb2_buf.timestamp = ktime_get_ns();
+			vb2_buffer_done(&buf_to->vb.vb2_buf, VB2_BUF_STATE_DONE);
+		} else {
+			fsd_csis_ctx_err(ctx, "DMA buffer pointer is not valid\n");
+		}
+	}
+
+update_prev_counters:
+	ctx->prev_frame_counter = ctx->current_frame_counter;
+}
+
+/*
+ * fsd_csis_ip_s_ctrl() - set new control value for CSI v4l2 device
+ * @ctrl: pointer to control value passed by user
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_ip_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct fsd_csis_dev *dev =
+		container_of(ctrl->handler, struct fsd_csis_dev, ctrl_handler);
+	int ret = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_USER_FSD_CSIS_NO_OF_LANE:
+
+		dev->nb_data_lane = ctrl->val;
+		if (!dev->stream_enabled)
+			ret = fsd_csis_set_num_of_datalane(dev, dev->nb_data_lane);
+		else
+			ret = -EBUSY;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * fsd_csis_enable() - enable CSI instance
+ * @dev: pointer to fsd_csis_dev structure
+ * Return: none
+ */
+static void fsd_csis_enable(struct fsd_csis_dev *dev)
+{
+	u32 csis_cmn_ctrl = 0;
+
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+	csis_cmn_ctrl |= set_bits(CSI_EN, CSI_EN_MASK);
+	writel(csis_cmn_ctrl, (dev->base + CSIS_CMN_CTRL));
+
+	fsd_csis_enable_vid_irqs(dev);
+}
+
+/*
+ * fsd_csis_disable() - disable CSI instance
+ * @dev: pointer to fsd_csis_dev structure
+ * Return: none
+ */
+static void fsd_csis_disable(struct fsd_csis_dev *dev)
+{
+	u32 csis_cmn_ctrl = 0, i;
+
+	for (i = 0; i < FSD_CSIS_MAX_VC; i++) {
+		if (dev->ctx[i])
+			fsd_csis_dma_enable(dev->ctx[i], false);
+	}
+
+	csis_cmn_ctrl = readl(dev->base + CSIS_CMN_CTRL);
+
+	/* Disable CSI */
+	csis_cmn_ctrl &= reset_bits(CSI_EN_MASK);
+	writel(csis_cmn_ctrl, (dev->base + CSIS_CMN_CTRL));
+}
+
+/*
+ * find_format_by_pix() - find matching fourcc value of
+ * context for given v4l2 pixel format
+ * @ctx: pointer to CSI context
+ * @pixelformat: pixel format to find
+ * Return: pointer to csi_fmt on success, NULL otherwise
+ */
+static const struct fsd_csis_fmt *find_format_by_pix(struct fsd_csis_ctx *ctx,
+						     unsigned int pixelformat)
+{
+	const struct fsd_csis_fmt *fmt;
+	unsigned int i;
+
+	for (i = 0; i < ctx->num_active_fmt; i++) {
+		fmt = ctx->active_fmt[i];
+
+		if (fmt->fourcc == pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+/*
+ * find_format_by_code() - find matching media bus code of
+ * context for given v4l2 pixel format
+ * @ctx: pointer to CSI context
+ * @pixelformat: pixel format to find
+ * Return: pointer to fsd_csis_fmt structure on success, NULL otherwise
+ */
+static const struct fsd_csis_fmt *find_format_by_code(struct fsd_csis_ctx *ctx,
+						      unsigned int pixelformat)
+{
+	const struct fsd_csis_fmt *fmt;
+	unsigned int i;
+
+	for (i = 0; i < ctx->num_active_fmt; i++) {
+		fmt = ctx->active_fmt[i];
+
+		if (fmt->code == pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+static inline struct fsd_csis_ctx *notifier_to_ctx(struct v4l2_async_notifier *n)
+{
+	return container_of(n, struct fsd_csis_ctx, notifier);
+}
+
+/*
+ * fsd_csis_subdev_get_format() - get the sensor sub device format
+ * @ctx: pointer to CSI context
+ * @frmfmt: out parameter filled with subdev format
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_subdev_get_format(struct fsd_csis_ctx *ctx, struct v4l2_mbus_framefmt *frmfmt)
+{
+	struct v4l2_subdev_format sd_fmt;
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+	int ret;
+
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
+
+	ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt);
+
+	if (ret)
+		return ret;
+	*frmfmt = *mbus_fmt;
+	fsd_csis_ctx_dbg(3, ctx, "%dx%d code:%04X\n", frmfmt->width, frmfmt->height, frmfmt->code);
+	return 0;
+}
+
+/*
+ * fsd_csis_subdev_set_format() - set the sensor sub device format
+ * @ctx: pointer to CSI context
+ * @frmfmt: subdev format to set
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_subdev_set_format(struct fsd_csis_ctx *ctx, struct v4l2_mbus_framefmt *frmfmt)
+{
+	struct v4l2_subdev_format sd_fmt;
+	struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+	int ret;
+
+	sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	sd_fmt.pad = 0;
+	*mbus_fmt = *frmfmt;
+
+	ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt);
+
+	if (ret)
+		return ret;
+	*frmfmt = *mbus_fmt;
+	return 0;
+}
+
+/*
+ * fsd_csis_querycap() - provide v4l2_capability information
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @cap: out parameter filled with driver information
+ * Return: 0
+ */
+static int fsd_csis_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+
+	strscpy(cap->driver, FSD_CSIS_MODULE_NAME, sizeof(cap->driver));
+	strscpy(cap->card, FSD_CSIS_MODULE_NAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", ctx->v4l2_dev->name);
+	return 0;
+}
+
+/*
+ * fsd_csis_enum_fmt_vid_cap() - enumerate v4l2 format information
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @f: out parameter with enumerated format information
+ * Return: 0
+ */
+static int fsd_csis_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+	const struct fsd_csis_fmt *fmt = NULL;
+
+	if (f->index >= ctx->num_active_fmt)
+		return -EINVAL;
+
+	fmt = ctx->active_fmt[f->index];
+	f->pixelformat = fmt->fourcc;
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	return 0;
+}
+
+/*
+ * fsd_csis_try_fmt_vid_cap() - try image format to set
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @f: format to try. Can be overwrittenwith driver supported values.
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+	const struct fsd_csis_fmt *fmt;
+	struct v4l2_subdev_frame_size_enum fse;
+	int ret, found;
+
+	fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
+
+	if (!fmt) {
+		fsd_csis_ctx_info(ctx,
+				  "Fourcc format 0x%08x not found, setting active format 0x%08x\n",
+				  f->fmt.pix.pixelformat, ctx->active_fmt[0]->fourcc);
+
+		/* Just get the first one enumerated */
+		fmt = ctx->active_fmt[0];
+		f->fmt.pix.pixelformat = fmt->fourcc;
+		f->fmt.pix.colorspace = fmt->colorspace;
+	}
+
+	f->fmt.pix.field = ctx->v_fmt.fmt.pix.field;
+
+	/* check for / find a valid width, height */
+	ret = 0;
+	found = false;
+	fse.pad = 0;
+	fse.code = fmt->code;
+	fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+	/* loop through supported frame sizes by sensor
+	 * if there are none -EINVAL is returned from the sub-device
+	 */
+	for (fse.index = 0; ; fse.index++) {
+		ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse);
+
+		if (ret)
+			break;
+
+		if (f->fmt.pix.width == fse.max_width && f->fmt.pix.height == fse.max_height) {
+			found = true;
+			break;
+		} else if (f->fmt.pix.width <= fse.max_width &&
+			    f->fmt.pix.height >= fse.min_height &&
+			    f->fmt.pix.height <= fse.min_height) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		fsd_csis_ctx_info(ctx, "Width %d Height %d not supported! Setting to %dx%d\n",
+				  f->fmt.pix.width, f->fmt.pix.height, ctx->v_fmt.fmt.pix.width,
+				  ctx->v_fmt.fmt.pix.height);
+		/* use existing values as default */
+		f->fmt.pix.width = ctx->v_fmt.fmt.pix.width;
+		f->fmt.pix.height = ctx->v_fmt.fmt.pix.height;
+	}
+
+	fsd_csis_format_size(ctx, fmt, f);
+	return 0;
+}
+
+/*
+ * fsd_csis_s_fmt_vid_cap() - set format to use
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @f: format to set
+ * Return: 0 on success. error value otherwisen
+ */
+static int fsd_csis_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+	struct vb2_queue *q = &ctx->vb_vidq;
+	const struct fsd_csis_fmt *fmt;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret;
+
+	if (vb2_is_busy(q)) {
+		fsd_csis_ctx_dbg(3, ctx, "device busy: %d\n", q->num_buffers);
+		return -EBUSY;
+	}
+
+	ret = fsd_csis_try_fmt_vid_cap(file, priv, f);
+
+	if (ret < 0) {
+		fsd_csis_ctx_err(ctx, "%x try format failed\n", f->fmt.pix.pixelformat);
+		return ret;
+	}
+
+	fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat);
+
+	if (!fmt) {
+		fsd_csis_ctx_err(ctx, "Fourcc format (0x%08x) not found\n", f->fmt.pix.pixelformat);
+		return -EINVAL;
+	}
+
+	v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code);
+
+	ret = fsd_csis_subdev_set_format(ctx, &mbus_fmt);
+
+	if (ret) {
+		fsd_csis_ctx_err(ctx, "%x not supported by subdev\n", f->fmt.pix.pixelformat);
+		return ret;
+	}
+
+	if (mbus_fmt.code != fmt->code) {
+		fsd_csis_ctx_dbg(3, ctx, "changed format! This should not happen.\n");
+		return -EINVAL;
+	}
+
+	v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
+	ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc;
+	ctx->v_fmt.fmt.pix.colorspace = fmt->colorspace;
+	ctx->fmt = fmt;
+	ctx->m_fmt = mbus_fmt;
+
+	fsd_csis_ip_configure(ctx);
+	*f = ctx->v_fmt;
+	return 0;
+}
+
+/*
+ * fsd_csis_g_fmt_vid_cap() - get current format in use
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @f: out parameter filled format information
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+
+	*f = ctx->v_fmt;
+
+	return 0;
+}
+
+/*
+ * fsd_csis_enum_framesizes() - enumerate frame sizes
+ * @file: pointer to file structure of v4l2 device
+ * @fh: pointer to file handle
+ * @fsize: enumerated frame sizes
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+	const struct fsd_csis_fmt *fmt;
+	struct v4l2_subdev_frame_size_enum fse;
+	int ret;
+
+	fmt = find_format_by_pix(ctx, fsize->pixel_format);
+
+	if (!fmt) {
+		fsd_csis_ctx_err(ctx, "Invalid pixel code: %x\n", fsize->pixel_format);
+		return -EINVAL;
+	}
+
+	fse.index = fsize->index;
+	fse.pad = 0;
+	fse.code = fmt->code;
+
+	ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse);
+
+	if (ret)
+		return ret;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+	fsize->discrete.width = fse.max_width;
+	fsize->discrete.height = fse.max_height;
+	return 0;
+}
+
+/*
+ * fsd_csis_enum_frameintervals() - enumerate frame intervals
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @fival: enumerated frame interval information
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_enum_frameintervals(struct file *file, void *priv,
+					struct v4l2_frmivalenum *fival)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+	const struct fsd_csis_fmt *fmt;
+	struct v4l2_subdev_frame_interval_enum fie = {
+		.index = fival->index,
+		.width = fival->width,
+		.height = fival->height,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	int ret;
+
+	fmt = find_format_by_pix(ctx, fival->pixel_format);
+
+	if (!fmt)
+		return -EINVAL;
+
+	fie.code = fmt->code;
+	ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval, NULL, &fie);
+
+	if (ret)
+		return ret;
+
+	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	fival->discrete = fie.interval;
+	return 0;
+}
+
+/*
+ * fsd_csis_enum_input() - enumerate video input information
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @inp: video input information
+ * Return: 0
+ */
+static int fsd_csis_enum_input(struct file *file, void *priv, struct v4l2_input *inp)
+{
+	if (inp->index >= FSD_CSIS_NB_INPUT)
+		return -EINVAL;
+
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+	snprintf(inp->name, sizeof(inp->name), "Camera %u\n", inp->index);
+	return 0;
+}
+
+/*
+ * fsd_csis_g_input() - get video input number
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @i: video input number
+ * Return: 0
+ */
+static int fsd_csis_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+
+	*i = ctx->input;
+
+	return 0;
+}
+
+/*
+ * fsd_csis_s_input() - select video input
+ * @file: pointer to file structure of v4l2 device
+ * @priv: file handle of v4l2 device
+ * @i: video input number
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct fsd_csis_ctx *ctx = video_drvdata(file);
+
+	if (i >= FSD_CSIS_NB_INPUT)
+		return -EINVAL;
+	ctx->input = i;
+	return 0;
+}
+
+/*
+ * fsd_csis_queue_setup() - sets up the number of buffers,
+ * planes and size required for selected image format
+ * @vq: vb2 bufffer queue in use
+ * Return: 0
+ */
+static int fsd_csis_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned int sizes[],
+				struct device *alloc_devs[])
+{
+	struct fsd_csis_ctx *ctx = vb2_get_drv_priv(vq);
+	unsigned int size = ctx->v_fmt.fmt.pix.sizeimage;
+
+	if (*nplanes) {
+		if (sizes[0] < size)
+			return -EINVAL;
+		size = sizes[0];
+	}
+
+	*nplanes = 1;
+	sizes[0] = size;
+	fsd_csis_ctx_dbg(3, ctx, "nbuffers %d size %d\n", *nbuffers, sizes[0]);
+	return 0;
+}
+
+/*
+ * fsd_csis_buffer_prepare() - initialize and validate
+ * the buffer size before queueing
+ * @vb: pointer to vb2_buffer in use
+ * Return: 0 or -EINVAL
+ */
+static int fsd_csis_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct fsd_csis_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct fsd_csis_buffer *buf = container_of(vb, struct fsd_csis_buffer,
+			vb.vb2_buf);
+	unsigned long size, plane_size = 0;
+
+	if (WARN_ON(!ctx->fmt))
+		return -EINVAL;
+
+	size = ctx->v_fmt.fmt.pix.sizeimage;
+	plane_size = vb2_plane_size(vb, 0);
+
+	if (plane_size < size) {
+		fsd_csis_ctx_err(ctx, "Data will not fit into plane (%lu < %lu)\n", plane_size,
+				 size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+
+	return 0;
+}
+
+/*
+ * fsd_csis_buffer_queue() - pass the buffer vb to CSI for streaming
+ * @vb: pointer to vb2_buffer in use
+ * Return: none
+ */
+static void fsd_csis_buffer_queue(struct vb2_buffer *vb)
+{
+	struct fsd_csis_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct fsd_csis_buffer *buf =
+		container_of(vb, struct fsd_csis_buffer, vb.vb2_buf);
+	struct fsd_csis_dmaqueue *vidq = &ctx->vidq;
+
+	mutex_lock(&ctx->mutex_buf);
+	list_add_tail(&buf->list, &vidq->active);
+	buf->sequence = ctx->sequence++;
+	mutex_unlock(&ctx->mutex_buf);
+}
+
+/*
+ * fsd_csis_start_streaming() - enter streaming for the CSI context
+ * @q: pointer to vb2_queue in use
+ * @count: number of already queued buffers
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct fsd_csis_ctx *ctx = vb2_get_drv_priv(q);
+	struct fsd_csis_dev *dev = ctx->dev;
+	struct fsd_csis_dmaqueue *vidq = &ctx->vidq;
+	struct fsd_csis_buffer *buf, *tmp;
+	int i, ret;
+	u64 t_stamp;
+
+	for (i = 0; i < FSD_CSIS_NB_DMA_OUT_CH; i++) {
+		mutex_lock(&ctx->mutex_buf);
+
+		if (list_empty(&vidq->active)) {
+			mutex_unlock(&ctx->mutex_buf);
+			fsd_csis_ctx_err(ctx, "Active buffer queue empty!\n");
+			return -EIO;
+		}
+
+		buf = list_entry(vidq->active.next, struct fsd_csis_buffer, list);
+		list_del(&buf->list);
+		fsd_csis_add_to_ring_buffer(ctx, buf, i);
+		mutex_unlock(&ctx->mutex_buf);
+	}
+
+	ret = pm_runtime_resume_and_get(dev->device);
+
+	if (ret < 0)
+		goto error_stop;
+	/*
+	 * save last frame counter and dma pointer location
+	 * just before enabling dma
+	 */
+	ctx->prev_dma_ptr = fsd_csis_current_dma_ptr(ctx);
+	ctx->prev_frame_counter = fsd_csis_current_frame_counter(ctx);
+	ctx->current_frame_counter = ctx->prev_frame_counter;
+	fsd_csis_clear_vid_irqs(dev);
+	fsd_csis_dma_enable(ctx, true);
+	dev->stream_enabled |= (1 << ctx->virtual_channel);
+
+	ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1);
+
+	if (ret) {
+		fsd_csis_ctx_err(ctx, "subdev start streaming failed! : %d\n", ret);
+		goto error_stop;
+	}
+	atomic_set(&ctx->end_irq_worker, 1);
+	fsd_csis_enable_irqs_for_ctx(ctx);
+	fsd_csis_enable(dev);
+	fsd_csis_ctx_info(ctx, "stream start vc %d\n", ctx->virtual_channel);
+
+	return 0;
+
+error_stop:
+	fsd_csis_dma_enable(ctx, false);
+	pm_runtime_put_sync(dev->device);
+	dev->stream_enabled &= (~(1 << ctx->virtual_channel));
+	t_stamp = ktime_get_ns();
+
+	list_for_each_entry_safe(buf, tmp, &vidq->active, list) {
+		list_del(&buf->list);
+		buf->vb.vb2_buf.timestamp = t_stamp;
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	}
+	return ret;
+}
+
+/*
+ * fsd_csis_stop_streaming() - stop streaming for CSI context
+ * @q: pointer to vb2_queue in use
+ * Return: none
+ */
+static void fsd_csis_stop_streaming(struct vb2_queue *q)
+{
+	struct fsd_csis_ctx *ctx = vb2_get_drv_priv(q);
+	struct fsd_csis_dev *dev = ctx->dev;
+	struct fsd_csis_dmaqueue *vidq = &ctx->vidq;
+	struct fsd_csis_buffer *buf, *tmp;
+	unsigned int timeout_cnt = 0;
+	int i;
+	void __iomem *dma_act_ctrl = 0;
+	u64 t_stamp;
+
+	fsd_csis_dma_enable(ctx, false);
+	dev->stream_enabled &= (~(1 << ctx->virtual_channel));
+	fsd_csis_disable(dev);
+	fsd_csis_disable_irqs_for_ctx(ctx);
+	atomic_set(&ctx->end_irq_worker, 0);
+
+	/* Wait for DMA Operation to finish */
+	dma_act_ctrl = dev->base + DMA0_ACT_CTRL + DMA_CH_OFFSET * ctx->virtual_channel;
+
+	while ((readl(dma_act_ctrl) & 0x1) == 0x0) {
+		if (timeout_cnt > 50) {
+			fsd_csis_warn(dev, "DMA did not finish in 500ms.\n");
+			break;
+		}
+		usleep_range(10000, 20000); /* Wait min 10ms, max 20ms */
+		timeout_cnt++;
+	}
+
+	/*
+	 * If DMA operation still exists after disabled IRQ, it will
+	 * update dma_done part in interrupt source register. For next
+	 * streaming session, this could be interpreted as current session's
+	 * first frame done. To prevent this incorrect dma_done receiving,
+	 * clear the interrupt source register here.
+	 */
+	fsd_csis_clear_vid_irqs(dev);
+
+	if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
+		fsd_csis_ctx_err(ctx, "Failed to disable streaming in subdev\n");
+	fsd_csis_ctx_info(ctx, "stream stop vc %d\n", ctx->virtual_channel);
+
+	pm_runtime_put_sync(dev->device);
+
+	/* Release all active buffers */
+	mutex_lock(&ctx->mutex_buf);
+
+	t_stamp = ktime_get_ns();
+	list_for_each_entry_safe(buf, tmp, &vidq->active, list) {
+		list_del(&buf->list);
+		buf->vb.vb2_buf.timestamp = t_stamp;
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	}
+	mutex_unlock(&ctx->mutex_buf);
+
+	for (i = 0; i < FSD_CSIS_NB_DMA_OUT_CH; i++) {
+		buf = ctx->frame[i];
+
+		if (buf) {
+			buf->vb.vb2_buf.timestamp = t_stamp;
+			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+		}
+	}
+}
+
+/*
+ * Videobuf operations
+ */
+static const struct vb2_ops fsd_csis_video_ops = {
+	.queue_setup		= fsd_csis_queue_setup,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+	.buf_prepare		= fsd_csis_buffer_prepare,
+	.start_streaming	= fsd_csis_start_streaming,
+	.stop_streaming		= fsd_csis_stop_streaming,
+	.buf_queue		= fsd_csis_buffer_queue,
+};
+
+static int fsd_csis_runtime_pm(struct fsd_csis_dev *dev, int on)
+{
+	int i, j, ret = 0;
+
+	if (on) {
+		if (!dev->ip_is_on) {
+			ret = pm_runtime_get_sync(dev->device);
+
+			for (i = 0; i < dev->nb_clocks; i++) {
+				ret = clk_prepare_enable(dev->clk[i]);
+
+				if (ret) {
+					fsd_csis_err(dev, "clock %d enable Failed\n", i);
+					for (j = 0; j < i; j++)
+						clk_disable(dev->clk[j]);
+					pm_runtime_put_sync(dev->device);
+					return ret;
+				}
+			}
+			enable_irq(dev->irq);
+			dev->ip_is_on = true;
+		}
+
+	} else {
+		if (!dev->stream_enabled && dev->ip_is_on) {
+			disable_irq(dev->irq);
+
+			for (i = 0; i < dev->nb_clocks; i++)
+				clk_disable(dev->clk[i]);
+			pm_runtime_put_sync(dev->device);
+			dev->ip_is_on = false;
+		}
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops fsd_csis_ip_ctrl_ops = {
+	.s_ctrl	= fsd_csis_ip_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config fsd_csis_ip_set_nb_lane = {
+	.ops	= &fsd_csis_ip_ctrl_ops,
+	.id	= V4L2_CID_USER_FSD_CSIS_NO_OF_LANE,
+	.name	= "Set number of lanes for CSIS Rx controller",
+	.type	= V4L2_CTRL_TYPE_INTEGER,
+	.min	= 1,
+	.max	= 4,
+	.step	= 1,
+	.def	= 4,
+};
+
+/*
+ * fsd_csis_ctrl_notify() - get notified of controls of video device
+ * @ctrl: pointer to control value passed by user
+ * @priv: private data (pointer to struct fsd_csis_dev instance)
+ * Return: None
+ */
+static void fsd_csis_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv)
+{
+	struct fsd_csis_dev *dev = priv;
+
+	switch (ctrl->id) {
+	case V4L2_CID_USER_FSD_CSIS_NO_OF_LANE:
+		dev->nb_data_lane = ctrl->val;
+		if (!dev->stream_enabled)
+			fsd_csis_set_num_of_datalane(dev, dev->nb_data_lane);
+		break;
+	}
+}
+
+/*
+ * fsd_csis_async_complete() - complete binding and register sensor sub device
+ * @notifier: v4l2 device notifier
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_async_complete(struct v4l2_async_notifier *notifier)
+{
+	struct fsd_csis_ctx *ctx = notifier_to_ctx(notifier);
+	const struct fsd_csis_fmt *fmt;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret;
+
+	ret = fsd_csis_subdev_get_format(ctx, &mbus_fmt);
+
+	if (ret) {
+		fsd_csis_ctx_err(ctx, "fsd_csis_subdev_get_format failed: %d\n", ret);
+		return ret;
+	}
+
+	fmt = find_format_by_code(ctx, mbus_fmt.code);
+
+	if (!fmt) {
+		fsd_csis_ctx_err(ctx, "mubs code 0x%08X not found\n", mbus_fmt.code);
+		return -EINVAL;
+	}
+
+	/* Save current subdev format */
+	v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt);
+	ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	ctx->v_fmt.fmt.pix.pixelformat  = fmt->fourcc;
+	ctx->v_fmt.fmt.pix.field = V4L2_FIELD_NONE;
+	ctx->v_fmt.fmt.pix.colorspace = fmt->colorspace;
+	ctx->v_fmt.fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	ctx->v_fmt.fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
+	ctx->v_fmt.fmt.pix.xfer_func = V4L2_XFER_FUNC_SRGB;
+	fsd_csis_format_size(ctx, fmt, &ctx->v_fmt);
+	ctx->fmt = fmt;
+	ctx->m_fmt = mbus_fmt;
+	return 0;
+}
+
+/*
+ * fsd_csis_fop_open() - open CSI v4l2 device
+ * @filp: pointer to file structure
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_fop_open(struct file *filp)
+{
+	struct fsd_csis_ctx *ctx;
+	int ret = -ENODEV;
+	struct vb2_queue *q;
+
+	ctx = video_drvdata(filp);
+
+	if (ctx) {
+		q = &ctx->vb_vidq;
+
+		if (vb2_is_busy(q)) {
+			fsd_csis_ctx_dbg(3, ctx, "device busy\n");
+			return -EBUSY;
+		}
+		ret = v4l2_fh_open(filp);
+
+		if (ret)
+			return ret;
+		ret = fsd_csis_runtime_pm(ctx->dev, 1);
+	}
+	return ret;
+}
+
+/*
+ * fsd_csis_fop_release() - release the file pertaining to CSI v4l2 device
+ * @filp: pointer to file structure
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_fop_release(struct file *filp)
+{
+	struct fsd_csis_ctx *ctx;
+	int ret;
+
+	ret = vb2_fop_release(filp);
+
+	if (ret)
+		return ret;
+	ctx = video_drvdata(filp);
+	ret = fsd_csis_runtime_pm(ctx->dev, 0);
+	return ret;
+}
+
+/*
+ * Video device ioctls
+ */
+static const struct v4l2_ioctl_ops fsd_csis_ioctl_ops = {
+	/* VIDIOC_QUERYCAP handler */
+	.vidioc_querycap      = fsd_csis_querycap,
+
+	/* VIDIOC_ENUM_FMT handlers */
+	.vidioc_enum_fmt_vid_cap  = fsd_csis_enum_fmt_vid_cap,
+
+	/* VIDIOC_G_FMT handlers */
+	.vidioc_g_fmt_vid_cap     = fsd_csis_g_fmt_vid_cap,
+
+	/* VIDIOC_S_FMT handlers */
+	.vidioc_s_fmt_vid_cap     = fsd_csis_s_fmt_vid_cap,
+
+	/* VIDIOC_TRY_FMT handlers */
+	.vidioc_try_fmt_vid_cap   = fsd_csis_try_fmt_vid_cap,
+
+	/* Buffer handlers */
+	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
+	.vidioc_querybuf      = vb2_ioctl_querybuf,
+	.vidioc_qbuf          = vb2_ioctl_qbuf,
+	.vidioc_expbuf	      = vb2_ioctl_expbuf,
+	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
+	.vidioc_create_bufs   = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
+
+	/* Stream on/off */
+	.vidioc_streamon      = vb2_ioctl_streamon,
+	.vidioc_streamoff     = vb2_ioctl_streamoff,
+
+	/* Input handling */
+	.vidioc_enum_input    = fsd_csis_enum_input,
+	.vidioc_g_input       = fsd_csis_g_input,
+	.vidioc_s_input       = fsd_csis_s_input,
+
+	/* Sliced VBI cap */
+	.vidioc_log_status    = v4l2_ctrl_log_status,
+
+	/* Debugging ioctls */
+	.vidioc_enum_framesizes   = fsd_csis_enum_framesizes,
+	.vidioc_enum_frameintervals = fsd_csis_enum_frameintervals,
+
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * V4L2 File operations
+ */
+static const struct v4l2_file_operations fsd_csis_fops = {
+	.owner		= THIS_MODULE,
+	.read           = vb2_fop_read,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap           = vb2_fop_mmap,
+	.open           = fsd_csis_fop_open,
+	.release        = fsd_csis_fop_release,
+};
+
+static struct video_device fsd_csis_videodev = {
+	.fops		= &fsd_csis_fops,
+	.device_caps	= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE,
+	.name		= FSD_CSIS_MODULE_NAME,
+	.minor		= -1,
+	.release	= video_device_release_empty,
+	.ioctl_ops	= &fsd_csis_ioctl_ops,
+};
+
+/*
+ * fsd_csis_complete_ctx() -
+ * @ctx: pointer to CSI context
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_complete_ctx(struct fsd_csis_ctx *ctx)
+{
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	int ret;
+
+	ret = v4l2_device_register_subdev_nodes(ctx->v4l2_dev);
+
+	if (ret)
+		v4l2_warn(ctx->v4l2_dev, "V4L2 register subdev nodes failed: %d\n", ret);
+
+	ctx->timesperframe = fsd_csis_tpf_default;
+
+	/* initialize locks */
+	mutex_init(&ctx->mutex);
+	mutex_init(&ctx->mutex_buf);
+
+	/* initialize vb2_queue */
+	q = &ctx->vb_vidq;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ | VB2_USERPTR;
+	q->drv_priv = ctx;
+	q->buf_struct_size = sizeof(struct fsd_csis_buffer);
+	q->ops = &fsd_csis_video_ops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->lock = &ctx->mutex;
+	q->min_buffers_needed = FSD_CSIS_NB_MIN_CH;
+	q->dev = ctx->dev->device;
+	dma_set_coherent_mask(ctx->dev->device, DMA_BIT_MASK(FSD_CSIS_DMA_COHERENT_MASK_SIZE));
+
+	ret = vb2_queue_init(q);
+
+	if (ret)
+		return ret;
+
+	/* initialize video DMA queue */
+	INIT_LIST_HEAD(&ctx->vidq.active);
+
+	vdev = &ctx->vdev;
+	*vdev = fsd_csis_videodev;
+	vdev->v4l2_dev = ctx->v4l2_dev;
+	vdev->queue = q;
+	video_set_drvdata(vdev, ctx);
+
+	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	ret = video_register_device(vdev, VFL_TYPE_VIDEO, video_nr);
+
+	if (ret)
+		return ret;
+
+	v4l2_info(ctx->v4l2_dev, "Video device registered as %s\n", video_device_node_name(vdev));
+	return ret;
+}
+
+/*
+ * fsd_csis_async_bound() -
+ * @notifier:
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_async_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev,
+				struct v4l2_async_subdev *asd)
+{
+	struct fsd_csis_dev *dev = NULL;
+	struct fsd_csis_ctx *ctx = notifier_to_ctx(notifier);
+	const struct fsd_csis_fmt *fmt;
+	struct v4l2_subdev_mbus_code_enum mbus_code;
+	int i, j, k, ret = 0;
+
+	dev = ctx->dev;
+
+	/* each of dev->ctx have their own asd and sensor subdevs */
+	if (ctx->asd.match.fwnode ==
+			of_fwnode_handle(subdev->dev->of_node)) {
+		ctx->sensor = subdev;
+	} else {
+		fsd_csis_ctx_err(ctx, "No matching sensor node for found!\n");
+		return -ENODEV;
+	}
+
+	v4l2_set_subdev_hostdata(subdev, ctx);
+
+	v4l2_info(ctx->v4l2_dev, "Hooked sensor subdevice: %s to parent\n", subdev->name);
+
+	/* Enumerate subdevice formates and enable matching csis formats */
+	ctx->num_active_fmt = 0;
+
+	for (i = 0, j = 0; ret != -EINVAL; ++j) {
+		memset(&mbus_code, 0, sizeof(mbus_code));
+		mbus_code.index = j;
+		mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &mbus_code);
+
+		if (ret)
+			continue;
+
+		for (k = 0; k < ARRAY_SIZE(fsd_csis_formats); k++) {
+			fmt = &fsd_csis_formats[k];
+
+			if (mbus_code.code == fmt->code) {
+				ctx->active_fmt[i] = fmt;
+				ctx->num_active_fmt = ++i;
+				break;
+			}
+		}
+	}
+
+	if (!i)
+		fsd_csis_ctx_err(ctx, "No matching format found by subdev %s\n", subdev->name);
+	ret = fsd_csis_complete_ctx(ctx);
+
+	if (ret) {
+		fsd_csis_ctx_err(ctx, "Failed to register video device for csis%d-%d\n",
+				 dev->id, ctx->virtual_channel);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_async_notifier_operations fsd_csis_async_notifier_ops = {
+	.bound = fsd_csis_async_bound,
+	.complete = fsd_csis_async_complete,
+};
+
+/*
+ * of_get_next_port() -
+ * @parent: struct device_node
+ * Return: pointer to the device node on success, NULL value otherwise
+ */
+static struct device_node *of_get_next_port(const struct device_node *parent,
+					    struct device_node *prev)
+{
+	struct device_node *port = NULL;
+
+	if (!parent)
+		return NULL;
+
+	if (!prev) {
+		struct device_node *ports;
+		/*
+		 * It's the first csis, we have to find a port subnode
+		 * within this node or within an optional 'ports' node.
+		 */
+		ports = of_get_child_by_name(parent, "ports");
+
+		if (ports)
+			parent = ports;
+
+		port = of_get_child_by_name(parent, "port");
+		/* release the 'ports' node */
+		of_node_put(ports);
+	} else {
+		struct device_node *ports;
+
+		ports = of_get_parent(prev);
+
+		if (!ports)
+			return NULL;
+
+		do {
+			port = of_get_next_child(ports, prev);
+
+			if (!port) {
+				of_node_put(ports);
+				return NULL;
+			}
+			prev = port;
+		} while (!of_node_name_eq(port, "port"));
+		of_node_put(ports);
+	}
+	return port;
+}
+
+/*
+ * of_get_next_endpoint() -
+ * @parent: pointer to struct device_node
+ * Return: pointer to the device node on success, NULL value otherwise
+ */
+static struct device_node *of_get_next_endpoint(const struct device_node *parent,
+						struct device_node *prev)
+{
+	struct device_node *ep = NULL;
+
+	if (!parent)
+		return NULL;
+
+	do {
+		ep = of_get_next_child(parent, prev);
+
+		if (!ep)
+			return NULL;
+		prev = ep;
+	} while (!of_node_name_eq(ep, "endpoint"));
+
+	return ep;
+}
+
+/*
+ * of_create_fsd_csis_context() - Parse the device node for local (csis port)
+ * and remote endpoint (sensor node) properties.
+ * Fill the sensor node properties into V4L2 endpoint descriptor
+ * for later use
+ * @ctx: pointer to CSI context
+ * @inst: CSI instance virtual channel ID for which CSI context is to be
+ * created
+ * Return: 0 on success. error value otherwise
+ */
+static int of_create_fsd_csis_context(struct fsd_csis_ctx *ctx, int inst)
+{
+	struct device *device = ctx->dev->device;
+	struct device_node *parent_node = NULL, *port = NULL, *ep_node = NULL,
+			   *remote_ep = NULL, *sensor_node = NULL;
+	struct v4l2_fwnode_endpoint *endpoint;
+	struct v4l2_async_subdev *asd;
+	int ret = 0, i;
+	unsigned int regval = 0x0;
+	bool found_port = false;
+
+	parent_node = device->of_node;
+	endpoint = &ctx->endpoint;
+
+	for (i = 0; i < FSD_CSIS_MAX_VC; i++) {
+		port = of_get_next_port(parent_node, port);
+
+		if (!port) {
+			ret = -ENODEV;
+			goto cleanup_exit;
+		}
+
+		of_property_read_u32(port, "reg", &regval);
+
+		if (regval == inst) {
+			found_port = true;
+			break;
+		}
+	}
+
+	if (!found_port) {
+		ret = -ENODEV;
+		fsd_csis_dbg(2, ctx->dev, "no matching port %d found\n", inst);
+		goto cleanup_exit;
+	}
+
+	ep_node = of_get_next_endpoint(port, ep_node);
+
+	if (!ep_node) {
+		fsd_csis_err(ctx->dev, "get endpoint failed: %ld\n", PTR_ERR(port));
+		ret = -ENODEV;
+		goto cleanup_exit;
+	}
+
+	sensor_node = of_graph_get_remote_port_parent(ep_node);
+
+	if (!sensor_node) {
+		fsd_csis_err(ctx->dev, "get sensor node failed: %ld\n", PTR_ERR(sensor_node));
+		ret = -ENODEV;
+		goto cleanup_exit;
+	}
+
+	remote_ep = of_parse_phandle(ep_node, "remote-endpoint", 0);
+
+	if (!remote_ep) {
+		fsd_csis_err(ctx->dev, "get remote endpoint failed %ld\n", PTR_ERR(remote_ep));
+		ret = -ENODEV;
+		goto cleanup_exit;
+	}
+
+	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(remote_ep), endpoint);
+
+	if (ret) {
+		fsd_csis_err(ctx->dev, "parse endpoint failed: %ld\n", PTR_ERR(remote_ep));
+		ret = -ENODEV;
+		goto cleanup_exit;
+	}
+
+	/* Store virtual channel id */
+	ctx->virtual_channel = inst;
+
+	asd = &ctx->asd;
+	asd->match_type = V4L2_ASYNC_MATCH_FWNODE;
+	asd->match.fwnode = of_fwnode_handle(sensor_node);
+
+	v4l2_async_nf_init(&ctx->notifier);
+
+	ret = __v4l2_async_nf_add_subdev(&ctx->notifier, asd);
+
+	if (ret) {
+		fsd_csis_err(ctx->dev, "add asd to notifier fail: %d", ret);
+		goto cleanup_exit;
+	}
+
+	sensor_node = NULL;
+
+cleanup_exit:
+
+	if (!remote_ep)
+		of_node_put(remote_ep);
+
+	if (!sensor_node)
+		of_node_put(sensor_node);
+
+	if (!ep_node)
+		of_node_put(ep_node);
+
+	if (!port)
+		of_node_put(port);
+	return ret;
+}
+
+/*
+ * fsd_csis_create_context() - create CSI context for virtual channel
+ * @dev: pointer to fsd_csis_dev structure
+ * @inst: value of virtual channel
+ * Return: pointer to CSI context structure on success, NULL value otherwise
+ */
+static struct fsd_csis_ctx *fsd_csis_create_context(struct fsd_csis_dev *dev, int inst)
+{
+	struct fsd_csis_ctx *ctx;
+	int ret;
+
+	ctx = devm_kzalloc(dev->device, sizeof(*ctx), GFP_KERNEL);
+
+	if (!ctx)
+		return NULL;
+	ctx->dev = dev;
+	ret = of_create_fsd_csis_context(ctx, inst);
+
+	if (ret)
+		goto free_ctx;
+
+	ctx->v4l2_dev = &dev->v4l2_dev;
+	ctx->notifier.ops = &fsd_csis_async_notifier_ops;
+	ret = v4l2_async_nf_register(ctx->v4l2_dev, &ctx->notifier);
+
+	if (ret < 0) {
+		fsd_csis_ctx_err(ctx, "async notifer register failed: %d\n", ret);
+		v4l2_async_nf_cleanup(&ctx->notifier);
+		goto unregister_device;
+	}
+
+	ctx->dev->stream_enabled &= (~(1 << ctx->virtual_channel));
+	ctx->sequence = 0;
+	return ctx;
+
+unregister_device:
+	v4l2_device_unregister(ctx->v4l2_dev);
+
+free_ctx:
+	devm_kfree(dev->device, ctx);
+	return NULL;
+}
+
+/*
+ * fsd_csis_delete_context() - delete the contextx instances
+ * @dev: pointer to fds_csis_dev structure
+ * Return: None
+ */
+static void fsd_csis_delete_context(struct fsd_csis_dev *dev)
+{
+	int i;
+	struct fsd_csis_ctx *ctx;
+
+	for (i = 0; i < FSD_CSIS_MAX_VC; i++) {
+		ctx = dev->ctx[i];
+
+		if (ctx) {
+			fsd_csis_ctx_dbg(3, ctx, "unregistering %s\n",
+					 video_device_node_name(&ctx->vdev));
+			v4l2_async_nf_unregister(&ctx->notifier);
+			video_unregister_device(&ctx->vdev);
+			cancel_work_sync(&dev->ctx[i]->csis_ctx_work);
+			mutex_destroy(&ctx->mutex);
+			mutex_destroy(&ctx->mutex_buf);
+			devm_kfree(dev->device, ctx);
+		}
+		dev->ctx[i] = NULL;
+	}
+}
+
+/*
+ * fsd_csis_probe() - CSI driver probe method
+ * @pdev: pointer to platform_device structure for CSI driver
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_probe(struct platform_device *pdev)
+{
+	struct fsd_csis_dev *dev;
+	int i, ret = 0;
+	unsigned int irq;
+	char name[24];
+	struct resource *res;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+
+	if (!dev)
+		return -ENOMEM;
+
+	/* save struct device information */
+	dev->device = &pdev->dev;
+	dev->id = of_alias_get_id(pdev->dev.of_node, "csis");
+	dev->info = of_device_get_match_data(dev->device);
+
+	/* Get Register and DMA resources, IRQ */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!res) {
+		dev_err(dev->device, "get register base failed\n");
+		return -ENODEV;
+	}
+	dev->base = devm_ioremap_resource(dev->device, res);
+
+	if (IS_ERR(dev->base))
+		return PTR_ERR(dev->base);
+
+	dev->sysreg_map = syscon_regmap_lookup_by_phandle(dev->device->of_node, "sysreg_csi");
+
+	if (IS_ERR(dev->sysreg_map)) {
+		ret = PTR_ERR(dev->sysreg_map);
+		dev_err(&pdev->dev, "sysreg map failed: %d\n", ret);
+		return ret;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+
+	if (irq < 0)
+		return irq;
+
+	ret = devm_request_irq(dev->device, irq, fsd_csis_irq_handler, 0,
+			       dev_name(dev->device), dev);
+
+	if (ret) {
+		dev_err(dev->device, "IRQ %d get failed: %d\n", irq, ret);
+		return ret;
+	}
+
+	for (i = 0; i < dev->info->nb_clocks; i++) {
+		snprintf(name, sizeof(name), "csis-%s", dev->info->clk_names[i]);
+		dev->clk[i] = devm_clk_get(dev->device, name);
+
+		if (IS_ERR(dev->clk[i])) {
+			ret = PTR_ERR(dev->clk[i]);
+			dev_err(dev->device, "Clock %s get failed: %d\n", name, ret);
+			return ret;
+		}
+		dev->nb_clocks++;
+		pr_debug("%s clock added\n", name);
+	}
+
+	platform_set_drvdata(pdev, dev);
+	mutex_init(&dev->mutex_csis_dma_reg);
+
+	/* set pseudo v4l2 device name for use in printk */
+	v4l2_device_set_name(&dev->v4l2_dev, FSD_CSIS_MODULE_NAME, &drv_instance);
+	ret = v4l2_device_register(dev->device, &dev->v4l2_dev);
+
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "register v4l2_device failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, 1);
+
+	if (ret)
+		v4l2_err(&dev->v4l2_dev, "control handler init failed: %d\n", ret);
+
+	v4l2_ctrl_new_custom(&dev->ctrl_handler, &fsd_csis_ip_set_nb_lane, NULL);
+
+	if (dev->ctrl_handler.error) {
+		ret = dev->ctrl_handler.error;
+		v4l2_err(&dev->v4l2_dev, "add control for setting CSIS Rx lanes failed: %d\n", ret);
+		goto unregister_device;
+	}
+
+	v4l2_ctrl_notify(v4l2_ctrl_find(&dev->ctrl_handler, V4L2_CID_USER_FSD_CSIS_NO_OF_LANE),
+			 fsd_csis_ctrl_notify, dev);
+	dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler;
+	v4l2_ctrl_handler_setup(&dev->ctrl_handler);
+
+	for (i = 0; i < FSD_CSIS_MAX_VC; i++) {
+		dev->ctx[i] = fsd_csis_create_context(dev, i);
+
+		if (dev->ctx[i])
+			INIT_WORK(&dev->ctx[i]->csis_ctx_work, fsd_csis_irq_worker);
+	}
+
+	dev->ip_is_on = false;
+	dev->lane_speed = FSD_CSIS_RX_BW;
+	pm_runtime_enable(dev->device);
+	ret = pm_runtime_resume_and_get(dev->device);
+
+	if (ret)
+		goto runtime_disable;
+	pm_runtime_put_sync(dev->device);
+	return 0;
+
+runtime_disable:
+	pm_runtime_disable(dev->device);
+
+unregister_device:
+	v4l2_device_unregister(&dev->v4l2_dev);
+	fsd_csis_delete_context(dev);
+
+	return ret;
+}
+
+/*
+ * fsd_csis_remove() - CSI device remove mothod
+ * @pdev: pointer to platform_device structure
+ * Return: 0 on success. error value otherwise
+ */
+static int fsd_csis_remove(struct platform_device *pdev)
+{
+	struct fsd_csis_dev *dev =
+		(struct fsd_csis_dev *)platform_get_drvdata(pdev);
+	int ret;
+
+	fsd_csis_disable(dev);
+	ret = pm_runtime_resume_and_get(dev->device);
+
+	v4l2_ctrl_handler_free(&dev->ctrl_handler);
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	fsd_csis_delete_context(dev);
+	mutex_destroy(&dev->mutex_csis_dma_reg);
+
+	if (ret >= 0)
+		pm_runtime_put_sync(dev->device);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static void fsd_csis_shutdown(struct platform_device *pdev)
+{
+	struct fsd_csis_dev *dev =
+		(struct fsd_csis_dev *)platform_get_drvdata(pdev);
+
+	fsd_csis_disable_irqs(dev);
+	fsd_csis_disable(dev);
+}
+
+static struct fsd_csis_dev_info fsd_csis_dev_info_v4_3 = {
+	.version	= FSD_CSIS_VERSION_4_3,
+	.nb_clocks	= 1,
+	.clk_names	= { "aclk" },
+};
+
+static const struct of_device_id fsd_csis_of_match[] = {
+	{
+		.compatible = "tesla,fsd-csis",
+		.data = &fsd_csis_dev_info_v4_3,
+	},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, fsd_csis_of_match);
+
+static struct platform_driver fsd_csis_driver = {
+	.probe		= fsd_csis_probe,
+	.remove		= fsd_csis_remove,
+	.shutdown	= fsd_csis_shutdown,
+	.driver		= {
+		.name	= FSD_CSIS_MODULE_NAME,
+		.of_match_table	= of_match_ptr(fsd_csis_of_match),
+	},
+};
+
+module_platform_driver(fsd_csis_driver);
+
+MODULE_DESCRIPTION("FSD CSIS Driver");
+MODULE_AUTHOR("Sathyakam M, <sathya@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(FSD_CSIS_MODULE_VERSION);
diff --git a/drivers/media/platform/fsd/fsd-csis.h b/drivers/media/platform/fsd/fsd-csis.h
new file mode 100644
index 000000000000..b990da903f87
--- /dev/null
+++ b/drivers/media/platform/fsd/fsd-csis.h
@@ -0,0 +1,785 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * FSD CSIS camera interface driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#ifndef _FSD_CSIS_H
+#define _FSD_CSIS_H
+
+/* Select D-PHY control values for FSD CSI Rx controller */
+#if defined(CONFIG_FSD_CSI_2100_MEGA_BITS_PER_SEC)
+
+/* PHY control values for 2100 Mbps */
+#define S_CLKSETTLECTL_VAL			0x00
+#define S_HSSETTLECTL_VAL			0x2e
+#define FSD_CSIS_RX_BW				2100
+
+#elif defined(CONFIG_FSD_CSI_1600_MEGA_BITS_PER_SEC)
+
+/* PHY control values 1600 Mbps */
+#define S_CLKSETTLECTL_VAL			0x00
+#define S_HSSETTLECTL_VAL			0x23
+#define FSD_CSIS_RX_BW				1600
+
+#elif defined(CONFIG_FSD_CSI_1500_MEGA_BITS_PER_SEC)
+
+/* PHY control values for 1500 Mbps */
+#define S_CLKSETTLECTL_VAL			0x00
+#define S_HSSETTLECTL_VAL			0x21
+#define FSD_CSIS_RX_BW				1500
+
+#elif defined(CONFIG_FSD_CSI_1000_MEGA_BITS_PER_SEC)
+
+/* PHY control values for 1000 Mbps */
+#define S_CLKSETTLECTL_VAL			0x00
+#define S_HSSETTLECTL_VAL			0x16
+#define FSD_CSIS_RX_BW				1000
+
+#else
+
+/* PHY control values for 800 Mbps and below */
+#define S_CLKSETTLECTL_VAL			0x00
+#define S_HSSETTLECTL_VAL			0x11
+#define FSD_CSIS_RX_BW				800
+
+#endif
+
+#define HSYNC_LINTV				0x20
+#define CLKGATE_TRAIL_VAL			0x07
+#define DMA_CLK_GATE_TRAIL			0x07
+
+/* SYSREG_CSI offsets */
+#define SW_RESETEN_DPHY				0x40c
+
+#define CSIS_SW_RESETEN_DPHY			0x1
+#define CSIS_SW_RESETEN_DPHY_MASK(phy)		BIT_MASK(phy)
+
+/*
+ * CSI register offsets
+ * (Refer to sf_csis_v6p00 sheet of SFR doc)
+ */
+#define CSIS_VERSION				0x0000
+#define CSIS_CMN_CTRL				0x0004
+#define CSIS_CLK_CTRL				0x0008
+#define CSIS_INT_MSK0				0x0010
+#define CSIS_INT_SRC0				0x0014
+#define CSIS_INT_MSK1				0x0018
+#define CSIS_INT_SRC1				0x001c
+#define PHY_STATUS				0x0020
+#define PHY_CMN_CTRL				0x0024
+#define PHY_BCTRL_L				0x0030
+#define PHY_BCTRL_H				0x0034
+#define PHY_SCTRL_L				0x0038
+#define PHY_SCTRL_H				0x003c
+#define ISP_CONFIG_CH0				0x0040
+#define ISP_RESOL_CH0				0x0044
+#define ISP_SYNC_CH0				0x0048
+#define ISP_CONFIG_CH1				0x0050
+#define ISP_RESOL_CH1				0x0054
+#define ISP_SYNC_CH1				0x0058
+#define ISP_CONFIG_CH2				0x0060
+#define ISP_RESOL_CH2				0x0064
+#define ISP_SYNC_CH2				0x0068
+#define ISP_CONFIG_CH3				0x0070
+#define ISP_RESOL_CH3				0x0074
+#define ISP_SYNC_CH3				0x0078
+#define SDW_CONFIG_CH0				0x0080
+#define SDW_RESOL_CH0				0x0084
+#define SDW_SYNC_CH0				0x0088
+#define SDW_CONFIG_CH1				0x0090
+#define SDW_RESOL_CH1				0x0094
+#define SDW_SYNC_CH1				0x0098
+#define SDW_CONFIG_CH2				0x00a0
+#define SDW_RESOL_CH2				0x00a4
+#define SDW_SYNC_CH2				0x00a8
+#define SDW_CONFIG_CH3				0x00b0
+#define SDW_RESOL_CH3				0x00b4
+#define SDW_SYNC_CH3				0x00b8
+#define FRM_CNT_CH0				0x0100
+#define FRM_CNT_CH1				0x0104
+#define FRM_CNT_CH2				0x0108
+#define FRM_CNT_CH3				0x010c
+#define LINE_INTR_CH0				0x0110
+#define LINE_INTR_CH1				0x0114
+#define LINE_INTR_CH2				0x0118
+#define LINE_INTR_CH3				0x011c
+#define VC_PASSING				0x0120
+#define DMA0_CTRL				0x1000
+#define DMA0_FMT				0x1004
+#define DMA0_SKIP				0x1008
+#define DMA0_ADDR1				0x1010
+#define DMA0_ADDR2				0x1014
+#define DMA0_ADDR3				0x1018
+#define DMA0_ADDR4				0x101c
+#define DMA0_ADDR5				0x1020
+#define DMA0_ADDR6				0x1024
+#define DMA0_ADDR7				0x1028
+#define DMA0_ADDR8				0x102c
+#define DMA0_ACT_CTRL				0x1030
+#define DMA0_ACT_FMT				0x1034
+#define DMA0_ACT_SKIP				0x1038
+#define DMA0_BYTE_CNT				0x1040
+#define DMA1_CTRL				0x1100
+#define DMA1_FMT				0x1104
+#define DMA1_SKIP				0x1108
+#define DMA1_ADDR1				0x1110
+#define DMA1_ADDR2				0x1114
+#define DMA1_ADDR3				0x1118
+#define DMA1_ADDR4				0x111c
+#define DMA1_ADDR5				0x1120
+#define DMA1_ADDR6				0x1124
+#define DMA1_ADDR7				0x1128
+#define DMA1_ADDR8				0x112c
+#define DMA1_ACT_CTRL				0x1130
+#define DMA1_ACT_FMT				0x1134
+#define DMA1_BYTE_CNT				0x1140
+#define DMA2_CTRL				0x1200
+#define DMA2_FMT				0x1204
+#define DMA2_SKIP				0x1208
+#define DMA2_ADDR1				0x1210
+#define DMA2_ADDR2				0x1214
+#define DMA2_ADDR3				0x1218
+#define DMA2_ADDR4				0x121c
+#define DMA2_ADDR5				0x1220
+#define DMA2_ADDR6				0x1224
+#define DMA2_ADDR7				0x1228
+#define DMA2_ADDR8				0x122c
+#define DMA2_ACT_CTRL				0x1230
+#define DMA2_ACT_FMT				0x1234
+#define DMA2_ACT_SKIP				0x1238
+#define DMA2_BYTE_CNT				0x1240
+#define DMA3_CTRL				0x1300
+#define DMA3_FMT				0x1304
+#define DMA3_SKIP				0x1308
+#define DMA3_ADDR1				0x1310
+#define DMA3_ADDR2				0x1314
+#define DMA3_ADDR3				0x1318
+#define DMA3_ADDR4				0x131c
+#define DMA3_ADDR5				0x1320
+#define DMA3_ADDR6				0x1324
+#define DMA3_ADDR7				0x1328
+#define DMA3_ADDR8				0x132c
+#define DMA3_ACT_CTRL				0x1330
+#define DMA3_ACT_FMT				0x1334
+#define DMA3_ACT_SKIP				0x1338
+#define DMA3_BYTE_CNT				0x1340
+#define DMA_CMN_CTRL				0x1400
+#define DMA_ERR_CODE				0x1404
+#define DMA_CLK_CTRL				0x1408
+#define DMA_AWUSER				0x140c
+#define DBG_AXIM_INFO				0x1440
+#define DBG_TRXFIFO_INFO			0x1444
+#define DBG_DMAFIFO_INFO			0x1448
+
+/*
+ * Register bit mask and set values
+ * Mask is defined for each register field from most to lower significant bits
+ * Register field set values are expressed in hex values
+ */
+/* CSIS_VERSION */
+#define CSIS_VERSION_MASK			GENMASK(31, 0)
+
+/* FSD CSI controller version 4.3 */
+#define FSD_CSIS_VERSION_4_3			(0x04030002)
+
+/* CSIS_CMN_CTRL (CSIS Common Control) */
+#define UPDATE_SHADOW_CH_MASK(ch)		BIT_MASK(16 + (ch))
+#define DESKEW_LEVEL_MASK			GENMASK(15, 13)
+#define DESKEW_ENABLE_MASK			BIT_MASK(12)
+#define INTERLEAVE_MODE_MASK			GENMASK(11, 10)
+#define LANE_NUMBER_MASK			GENMASK(9, 8)
+#define UPDATE_SHADOW_CTRL_MASK			BIT_MASK(2)
+#define SW_RESET_MASK				BIT_MASK(1)
+#define CSI_EN_MASK				BIT_MASK(0)
+
+#define UPDATE_SHADOW				0x1
+#define DESKEW_LEVEL				0x2
+#define DESKEW_ENABLE				0x1
+#define UPDATE_SHADOW_CTRL			0x1
+#define SW_RESET				0x1
+#define CSI_EN					0x1U
+
+/* CSIS_CLK_CTRL (CSIS Clock Control) */
+#define CLKGATE_TRAIL_MASK(ch)			GENMASK(19 + 4 * (ch), 16 + 4 * (ch))
+#define CLKGATE_EN_MASK(ch)			BIT_MASK(4 + (ch))
+
+#define CLKGATE_EN				0x1
+
+/* CSIS_INT_MSK0 (Interrupt Mask register 0) */
+#define FRAMESTART_MASK				GENMASK(27, 24)
+#define FRAMEEND_MASK				GENMASK(23, 20)
+#define ERR_SOT_HS_MASK				GENMASK(19, 16)
+#define ERR_LOST_FS_MASK			GENMASK(15, 12)
+#define ERR_LOST_FE_MASK			GENMASK(11, 8)
+#define ERR_OVER_MASK				BIT_MASK(4)
+#define ERR_WRONG_CFG_MASK			BIT_MASK(3)
+#define ERR_ECC_MASK				BIT_MASK(2)
+#define ERR_CRC_MASK				BIT_MASK(1)
+#define ERR_ID_MASK				BIT_MASK(0)
+#define CSIS_INT_MSK0_ALL_MASK			GENMASK(27, 0)
+
+#define FRAMESTART_CH_MASK(ch)			BIT_MASK((ch) + 24)
+#define FRAMEEND_CH_MASK(ch)			BIT_MASK((ch) + 20)
+#define ERR_SOT_HS_CH_MASK(ch)			BIT_MASK((ch) + 16)
+#define ERR_LOST_FS_CH_MASK(ch)			BIT_MASK((ch) + 12)
+#define ERR_LOST_FE_CH_MASK(ch)			BIT_MASK((ch) + 8)
+
+#define FRAMESTART_ENABLE			0x1
+#define FRAMEEND_ENABLE				0x1
+#define ERR_SOT_HS_ENABLE			0x1
+#define ERR_LOST_FS_ENABLE			0x1
+#define ERR_LOST_FE_ENABLE			0x1
+
+/*
+ * Writing 1 will enable interrupt (Unmask)
+ * Writing 0 will disable interrupt (mask)
+ */
+#define CSIS_INT_MSK0_ENABLE_ALL		(~0)
+#define CSIS_INT_MSK0_MASK_ALL			(0)
+
+/* CSIS_INT_SRC0 (Interrupt Source register 0) */
+#define CSIS_INT_SRC0_ERR_ALL_MASK		(GENMASK(19, 8) | GENMASK(4, 0))
+
+/*
+ * CSIS_INT_SRC1 (Interrupt Source register 1)
+ * CSIS_INT_MSK1 (Interrupt Mask register 1)
+ */
+#define DMA_OTF_OVERLAP_MASK			GENMASK(17, 14)
+#define DMA_ABORT_DONE_MASK			BIT_MASK(13)
+#define DMA_ERROR_MASK				BIT_MASK(12)
+#define DMA_FRM_END_MASK			GENMASK(11, 8)
+#define DMA_FRM_START_MASK			GENMASK(7, 4)
+#define LINE_END_MASK				GENMASK(3, 0)
+
+#define DMA_OTF_OVERLAP_CH_MASK(ch)		BIT_MASK((ch) + 14)
+#define DMA_FRM_END_CH_MASK(ch)			BIT_MASK((ch) + 8)
+#define DMA_FRM_START_CH_MASK(ch)		BIT_MASK((ch) + 4)
+#define LINE_END_CH_MASK(ch)			BIT_MASK(ch)
+
+#define DMA_ABORT_ENABLE			0x1
+#define DMA_ERROR_ENABLE			0x1
+#define DMA_OTF_OVERLAP_ENABLE			0x1
+#define DMA_FRM_END_ENABLE			0x1
+#define DMA_FRM_START_ENABLE			0x1
+#define LINE_END_CH_ENABLE			0x1
+
+#define CSIS_INT_SRC1_ERR_ALL_MASK		GENMASK(17, 12)
+
+/*
+ * Writing 1 will enable interrupt (Unmask)
+ * Writing 0 will disable interrupt (mask)
+ */
+#define CSIS_INT_MASK_ENABLE			0x1
+#define CSIS_INT_MSK1_ENABLE_ALL		(~0)
+#define CSIS_INT_MSK1_MASK_ALL			(0)
+
+/* PHY_STATUS */
+#define PHY_STATUS_ULPSDAT_MASK			GENMASK(11, 8)
+#define PHY_STATUS_STOPSTATEDAT_MASK		GENMASK(7, 4)
+#define PHY_STATUS_ULPSCLK_MASK			BIT_MASK(1)
+#define PHY_STATUS_STOPSTATECLK_MASK		BIT_MASK(0)
+
+/* PHY_CMN_CTRL (PHY common control) */
+#define HSSETTLE_MASK				GENMASK(31, 24)
+#define S_CLKSETTLE_MASK			GENMASK(23, 22)
+#define S_BYTE_CLK_ENABLE_MASK			BIT_MASK(21)
+#define S_DPDN_SWAP_CLK_MASK			BIT_MASK(6)
+#define S_DPDN_SWAP_DAT_MASK			BIT_MASK(5)
+#define ENABLE_DAT_MASK				GENMASK(4, 1)
+#define ENABLE_CLK_MASK				BIT_MASK(0)
+
+/* PHY BCTRL_L */
+#define PHY_BCTRL_L_BPHYCTRL_MASK		GENMASK(31, 0)
+
+/* PHY BCTRL_H */
+#define PHY_BCTRL_H_BPHYCTRL_MASK		GENMASK(31, 0)
+
+/* PHY SCTRL_L */
+#define PHY_SCTRL_L_SPHYCTRL_MASK		GENMASK(31, 0)
+
+/* PHY SCTRL_H */
+#define SKEW_CAL_MAX_SKEW_CODE_CTRL_MASK	GENMASK(7, 2)
+#define SKEW_CAL_EN_MASK			BIT_MASK(1)
+
+#define SKEW_CAL_MAX_SKEW_CODE_CTRL		0x24
+#define SKEW_CAL_EN				0x1
+
+/*
+ * ISP_CONFIG_CH0~3 (ISP configuration register CH0~3)
+ * SDW_CONFIG_CH0~3 (Shadow configuration register of CH0~3)
+ */
+#define PIXEL_MODE_MASK				GENMASK(13, 12)
+#define PARALLEL_MODE_MASK			BIT_MASK(11)
+#define RGB_SWAP_MASK				BIT_MASK(10)
+#define DATAFORMAT_MASK				GENMASK(7, 2)
+#define VIRTUAL_CHANNEL_MASK			GENMASK(1, 0)
+
+#define ISP_CONFIG_CH_OFFSET			0x10
+
+/*
+ * ISP_RESOL_CH0~3 (ISP Resolution register CH0~3)
+ * SDW_RESOL_CH0~3 (Shadow resolution register of CH0~3)
+ */
+#define VRESOL_MASK				GENMASK(31, 16)
+#define HRESOL_MASK				GENMASK(15, 0)
+
+/*
+ * ISP_SYNC_CH0!3 (ISP Sync register CH0~3)
+ * SDW_SYNC_CH0~31 Shadow Sync register CH0~3
+ */
+#define HSYNC_LINTV_MASK			GENMASK(23, 18)
+
+/* FRM_CNT_CH0~3 (Frame counter of CH0~3) */
+#define FRM_CNT_CH_MASK				GENMASK(31, 0)
+#define FRM_CNT_CH_OFFSET			0x4
+
+/* LINE_INTR_CH0~3 (Line interrupt configuration CH0~3) */
+#define LINE_INTR_CH_MASK			GENMASK(31, 0)
+#define LINE_INTR_CH_MUL			0x4
+
+/* VC_PASSING (VC Passing configuration) */
+#define VC_PASSING_MASK				GENMASK(9, 8)
+#define VC_PASSING_ENABLE_MASK			BIT_MASK(7)
+#define VC_PASSING_ENABLE			0x1
+
+#define DMA_ADDR_OFFSET				0x100
+
+/* DMA_CTRL (DMA0~3 Control) */
+#define DMA_UPDT_SKIPPTR_MASK			GENMASK(7, 5)
+#define DMA_UPDT_FRAMEPTR_MASK			GENMASK(4, 2)
+#define DMA_UPDT_PTR_EN_MASK			BIT_MASK(1)
+#define DMA_DISABLE_MASK			BIT_MASK(0)
+
+#define DMA_DISABLE				0x1
+
+/* DMA_FMT  (DMA0~3 Output Format) */
+#define DMA_PACK_MASK				GENMASK(17, 16)
+#define DMA_DIM_MASK				BIT_MASK(15)
+#define DMA_DUMP_MASK				BIT_MASK(13)
+#define DMA_BYTESWAP_MASK			BIT_MASK(12)
+
+enum FSD_CSIS_DMA_PACK {
+	DMA_PACK_NORMAL,
+	DMA_PACK_10,
+	DMA_PACK_12,
+	DMA_PACK_14,
+	DMA_PACK_18,
+	DMA_PACK_20,
+};
+
+#define DMA_DIM_1D				0x1
+#define DMA_DIM_2D				0x0
+#define DMA_DUMP_OTF				0x1
+#define DMA_DUMP_NORMAL				0x0
+#define DMA_BYTESWAP_REVERSE			0x1
+#define DMA_BYTESWAP_REGULAR			0x0
+
+/* DMA_SKIP (DMA0~3 skip) */
+#define DMA_SKIP_EN_MASK			BIT_MASK(31)
+#define DMA_SKIP_TURNPTR_MASK			GENMASK(18, 16)
+#define DMA_SKIP_SEQ_MASK			GENMASK(7, 0)
+
+#define DMA_SKIP_ENABLE				0x1
+
+/* DMA_ADDR (DMA0~3 Address) */
+#define DMA_ADDR1_MASK				GENMASK(31, 0)
+
+/* DMA_ACT_CTRL (DMA_0_3 ACT control) */
+#define ACTIVE_DMA_ABORTED_MASK			BIT_MASK(8)
+#define ACTIVE_DMA_SKIPPTR_MASK			GENMASK(7, 5)
+#define ACTIVE_DMA_FRAMEPTR_MASK		GENMASK(4, 2)
+#define ACTIVE_DMA_DISABLE_MASK			BIT_MASK(0)
+
+/* DMA_ACT_FMT (DMA0~3 ACT format) */
+#define ACTIVE_DMA_PACK_MASK			GENMASK(17, 16)
+#define ACTIVE_DMA_DIM_MASK			BIT_MASK(15)
+#define ACTIVE_DMA_DUMP_MASK			BIT_MASK(13)
+#define ACTIVE_DMA_BYTESWAP_MASK		BIT_MASK(12)
+
+/* DMA_ACT_SKIP (DMA0~3 ACT skip) */
+#define ACTIVE_DMA_SKIP_EN_MASK			BIT_MASK(31)
+#define ACTIVE_DMA_SKIP_TURNPTR_MASK		GENMASK(18, 16)
+#define ACTIVE_DMA_SKIP_SEQ_MASK		GENMASK(7, 0)
+
+/* DMA_FRM_BYTE_CNT (DMA0~3 Frame byte count) */
+#define DMA_FRM_BYTE_CNT_MASK			GENMASK(31, 0)
+
+/* DMA_CMN_CTRL (DMA Common control) */
+#define DMA_ABORT_REQ_MASK			BIT_MASK(0)
+#define DMA_FRM_LOCK_EN				1
+
+/* DMA_ERR_CODE (DMA Error code) */
+#define DMAFIFO_FULL_MASK			BIT_MASK(5)
+#define TRXFIFO_FULL_MASK			BIT_MASK(4)
+#define BRESP_ERROR_CH_MASK(ch)			BIT_MASK(ch)
+
+/* DMA_CLK_CTRL (DMA Clock control) */
+#define DMA_CLK_GATE_TRAIL_MASK			GENMASK(4, 1)
+#define DMA_CLK_GATE_EN_MASK			BIT_MASK(0)
+
+#define DMA_CLK_GATE_ENABLE			0x1
+
+/* DMA_AWUSER (DMA AWUSER) */
+#define DMA_AWUSER_MASK				GENMASK(3, 0)
+
+/* DBG_AXIM_INFO (Debug AXIM Info) */
+#define DBG_AXIM_WCNT_MASK			GENMASK(11, 7)
+#define DBG_AXIM_AWCNT_MASK			GENMASK(6, 2)
+#define DBG_AXIM_STATE_MASK			GENMASK(1, 0)
+
+/* DBG_TRXFIFO_INFO (Debug TRXFIFO Info) */
+#define TRXFIFO_MAX_WCNT_MASK			GENMASK(31, 16)
+#define TRXFIFO_CUR_WCNT_MASK			GENMASK(15, 0)
+
+/* DBG_DMAFIFO_INFO (Debug DMA FIFO Info) */
+#define DMAFIFO_MAX_WCNT_MASK			GENMASK(31, 16)
+#define DMAFIFO_CUR_WCNT_MASK			GENMASK(15, 0)
+
+#define DMA_CLK_GATE_ENABLE			0x1
+
+#define FSD_CSIS_NB_CSI_PER_PHY			4
+#define FSD_CSIS_MAX_VC				4
+#define FSD_CSIS_NB_CLOCK			1
+#define FSD_CSIS_DMA_COHERENT_MASK_SIZE		32
+
+#define FSD_CSIS_WMIN				48
+#define FSD_CSIS_WMAX				1920
+#define FSD_CSIS_HMIN				32
+#define FSD_CSIS_HMAX				1200
+#define FSD_CSIS_WALIGN				2
+#define FSD_CSIS_HALIGN				0
+#define FSD_CSIS_SALIGN				0
+
+#define FSD_CSIS_NB_INPUT			1
+#define FSD_CSIS_NB_MIN_CH			1
+#define FSD_CSIS_NB_DMA_OUT_CH			8
+
+/* There are ACLK, PCLK clocks for each CSI block */
+#define MAX_FSD_CSIS_CLOKCS			2
+
+#define DPHYON_DATA3				BIT(DATALANE3)
+#define DPHYON_DATA2				BIT(DATALANE2)
+#define DPHYON_DATA1				BIT(DATALANE1)
+#define DPHYON_DATA0				BIT(DATALANE0)
+
+/* PHY Common control registers */
+#define S_BYTE_CLK_ENABLE			0x1
+#define S_DPDN_SWAP_CLK_ENABLE			0x1
+#define S_DPDN_SWAP_DAT_ENABLE			0x1
+#define ENABLE_DAT(nb)				((1 << (nb)) - 1)
+#define ENABLE_CLK				0x1
+
+/*
+ * DMA Channel registers
+ */
+#define DMA_CH_OFFSET				0x100
+#define DMA_FRAME_ADDR_OFFSET			0x4
+
+/*
+ * Frame Counter registers
+ */
+#define FRM_CNT_CH_OFFSET			0x4
+
+/*
+ * ISP configuration related registers
+ */
+#define ISP_CH_OFFSET				0x10
+
+#define ISP_PIXEL_MODE_SINGLE			0x0
+#define ISP_PIXEL_MODE_DUAL			0x1
+#define ISP_PIXEL_MODE_QUAD			0x0
+#define ISP_PIXEL_MODE_OCTA			0x3
+#define ISP_CONFIG_RGB_SWAP			0x1
+#define ISP_DATA_FORMAT_YUV420_8		0x18
+#define ISP_DATA_FORMAT_YUV420_10		0x19
+#define ISP_DATA_FORMAT_YUV420_8_LEGACY		0x1A
+#define ISP_DATA_FORMAT_YUV420_8_CSPS		0x1C
+#define ISP_DATA_FORMAT_YUV420_10_CSPS		0x1D
+#define ISP_DATA_FORMAT_YUV422_8		0x1E
+#define ISP_DATA_FORMAT_YUV422_10		0x1F
+#define ISP_DATA_FORMAT_RGB565			0x22
+#define ISP_DATA_FORMAT_RGB666			0x23
+#define ISP_DATA_FORMAT_RGB888			0x24
+#define ISP_DATA_FORMAT_RAW6			0x28
+#define ISP_DATA_FORMAT_RAW7			0x29
+#define ISP_DATA_FORMAT_RAW8			0x2A
+#define ISP_DATA_FORMAT_RAW10			0x2B
+#define ISP_DATA_FORMAT_RAW12			0x2C
+#define ISP_DATA_FORMAT_RAW14			0x2D
+#define ISP_DATA_FORMAT_RAW16			0x2E
+#define ISP_DATA_FORMAT_RAW20			0x2F
+#define ISP_DATA_FORMAT_USER_DEFINED_1		0x30
+#define ISP_DATA_FORMAT_USER_DEFINED_2		0x31
+#define ISP_DATA_FORMAT_USER_DEFINED_3		0x32
+#define ISP_DATA_FORMAT_USER_DEFINED_4		0x33
+#define ISP_DATA_FORMAT_USER_DEFINED_5		0x34
+#define ISP_DATA_FORMAT_USER_DEFINED_6		0x35
+#define ISP_DATA_FORMAT_USER_DEFINED_7		0x36
+#define ISP_DATA_FORMAT_USER_DEFINED_8		0x37
+
+/*
+ * fsd_csis_fmt - structure holding the formats supported in CSI instance
+ * @name: string indicating name of format
+ * @fourcc: fourcc value of this format
+ * @colorspace: v4l2 colorspace for this format
+ * @code: media bus code for this format
+ * @depth: bits per pixel used for thsi format
+ */
+struct fsd_csis_fmt {
+	char name[32];
+	u32 fourcc;
+	u32 colorspace;
+	u32 code;
+	u32 depth;
+};
+
+#define FSD_CSIS_MAX_FORMATS			20
+
+/*
+ * fsd_csis_buffer - buffer for one video frame
+ * @vb: video buffer information for v4l2
+ * @list: list of buffers to be used in VB2 operations
+ * @fmt: image format being used for this buffer
+ * @sequence: number indicating sequence in stream
+ */
+struct fsd_csis_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct vb2_v4l2_buffer vb;
+	struct list_head list;
+	const struct fsd_csis_fmt *fmt;
+	unsigned long sequence;
+};
+
+/*
+ * csis_dmaqueue - DMA buffer queue of avalailable buffers for streaming
+ * @active: list of buffers avalailable for DMA
+ */
+struct fsd_csis_dmaqueue {
+	struct list_head active;
+};
+
+enum {
+	DPHY_MODE,
+	CPHY_MODE
+};
+
+enum FSD_CSIS_DATA {
+	DATALANE0 = 0,
+	DATALANE1,
+	DATALANE2,
+	DATALANE3
+};
+
+enum FSD_CSIS_INTERLEAVE {
+	VC0_ONLY = 0,
+	DT_ONLY,
+	VC_ONLY,
+	VC_DT_BOTH
+};
+
+enum FSD_CSIS_PIXEL_MODE {
+	SINGLE_PIXEL_MODE,
+	DUAL_PIXEL_MODE,
+	QUAD_PIXEL_MODE,
+	OCTA_PIXEL_MODE
+};
+
+enum FSD_CSIS_PARALLEL_MODE {
+	FSD_CSIS_PARALLEL_MODE_OFF,
+	FSD_CSIS_PARALLEL_MODE_32_BIT,
+	FSD_CSIS_PARALLEL_MODE_64_BIT,
+	FSD_CSIS_PARALLEL_MODE_128_BIT
+};
+
+/*
+ * fsd_csis_dev - CSI device structure. One for each CSI instance
+ * @device: pointer to core device structure provided by platform_device
+ * @info: device specific information (e.g. version)
+ * @ctx: CSIS context describing the individual stream and device properties.
+ * There is one context per virtual channel
+ * @clk: CSIS clocks that need to be set for streaming
+ * @v4l2_dev: V4L2 device instance for this CSIS I/F
+ * @ctrl_handler: Control handler to set Number of lanes, and lane configuration
+ * @mutex_csis_dma_reg: synchronization lock to update DMA addresses
+ * @id: this CSI device id
+ * @nb_data_lane: number of CSI data lanes in use for this CSI instance
+ * @nb_clocks: number of clocks to be prepared for CSI enable
+ * @base: base address of this CSI instance SFR
+ * @phy_base: base address of DC-PHY interface of this CSI instance
+ * @lane_speed: data rate at which CSI Rx lane is operating (in Mbps for D-PHY, Msps for C-PHY)
+ * @irq: interrupt number for this CSI instance
+ * @ip_is_on: boolean value indicating CSI instance is turned on
+ * @csis_sysreg_base: SYSREG_CSI base to set DC-PHY reset
+ * @stream_enabled: indicates if streaming is in progress
+ */
+struct fsd_csis_dev {
+	struct device *device;
+	const struct fsd_csis_dev_info *info;
+	struct fsd_csis_ctx *ctx[FSD_CSIS_MAX_VC];
+	struct clk *clk[FSD_CSIS_NB_CLOCK];
+	struct v4l2_device v4l2_dev;
+	struct v4l2_ctrl_handler ctrl_handler;
+	/* lock for adding VB2 buffers for DMA */
+	struct mutex mutex_csis_dma_reg;
+	unsigned int id;
+	unsigned int nb_data_lane;
+	unsigned int nb_clocks;
+	void __iomem *base;
+	void __iomem *phy_base;
+	struct regmap *sysreg_map;
+	unsigned int lane_speed;
+	int irq;
+	bool ip_is_on;
+	unsigned int stream_enabled;
+};
+
+/*
+ * fsd_csis_ctx - CSI context information for stream in use
+ * @dev: pointer to parent device structure containing this context
+ * @mutex: VB2 Queue lock
+ * @mutex_buf: synchrnization lock used between VB2 buffer operations and the DMA queue
+ * @end_irq_worker: flag to allow IRQ worker thread to process stream buffers
+ * @input: input number to use VIDIOC_S_INPUT/VIDIOC_G_INPUT ioctls
+ * @v4l2_dev: v4l2 device instance for this context
+ * @sensor: Sub device to interface with sensor (1 for each CSIS I/F Channel)
+ * @vdev: video device node representing this stream
+ * @endpoint: fwnode graph endpoint for this CSI port
+ * @fh: handle for v4l2 file operations
+ * @timesperframe: minimum and maximum fps
+ * @vb_vidq: vb2 queue for this context
+ * @asd: Asynchronous sub device instances to bind
+ * @notifier: Notifier to bind sub device nodes
+ * @virtual_channel: CSI Virtual Channel ID in use
+ * @fmt: image format in use for this context
+ * @v_fmt: Used to store current pixel format
+ * @m_fmt: Used to store current mbus frame format
+ * @active_fmt: array of formats as supported by CSI and image sensor
+ * @num_active_fmt: number of active formats as given in active_fmt
+ * @vidq: video buffer queue being used by CSI DMA
+ * @frame: array of CSI buffers
+ * @frame_addr: array of DMA addresses of the CSI buffers
+ * @num_reqbufs: number of buffers as requested by user
+ * @prev_dma_ptr: previous DMA frame counter value
+ * @current_dma_ptr: present DMA frame counter value
+ * @number_of_ready_bufs: number of vb2 buffers available to be added to active list
+ * @prev_frame_counter: previous CSI frame counter value
+ * @current_frame_counter: current CSI frame counter value
+ * @csis_ctx_work: bottom half work queue structure used between
+ * CSI interrupt handler and streaming operations
+ * @sequence: number indicating sequence in stream
+ */
+struct fsd_csis_ctx {
+	struct fsd_csis_dev *dev;
+	/* lock for vb2_queue buffers */
+	struct mutex mutex;
+	/**
+	 * lock to synchronize buffer access between worker thread
+	 * and buffer add/delete operations
+	 */
+	struct mutex mutex_buf;
+	atomic_t end_irq_worker;
+	unsigned int input;
+	struct v4l2_device *v4l2_dev;
+	struct v4l2_subdev *sensor;
+	struct video_device vdev;
+	struct v4l2_fwnode_endpoint endpoint;
+	struct v4l2_fh fh;
+	struct v4l2_fract timesperframe;
+	struct vb2_queue vb_vidq;
+	struct v4l2_async_subdev asd;
+	struct v4l2_async_notifier notifier;
+	unsigned int virtual_channel;
+	const struct fsd_csis_fmt *fmt;
+	struct v4l2_format v_fmt;
+	struct v4l2_mbus_framefmt m_fmt;
+	const struct fsd_csis_fmt *active_fmt[FSD_CSIS_MAX_FORMATS];
+	unsigned int num_active_fmt;
+	struct fsd_csis_dmaqueue vidq;
+	struct fsd_csis_buffer *frame[FSD_CSIS_NB_DMA_OUT_CH];
+	u64 frame_addr[FSD_CSIS_NB_DMA_OUT_CH];
+	u8 prev_dma_ptr;
+	u8 current_dma_ptr;
+	u8 number_of_ready_bufs;
+	u32 prev_frame_counter;
+	u32 current_frame_counter;
+	unsigned long sequence;
+	u32 dma_error;
+	struct work_struct csis_ctx_work;
+};
+
+/*
+ * fsd_csis_dev_info - CSIS device information
+ * @version: FSD CSIS IP version
+ * @nb_clocks: number of clocks needed for the driver
+ * @clk_names: clock names
+ */
+struct fsd_csis_dev_info {
+	unsigned int	version;
+	unsigned int	nb_clocks;
+	const char	*clk_names[MAX_FSD_CSIS_CLOKCS];
+};
+
+static inline unsigned int get_bits(unsigned int val, unsigned int mask)
+{
+	u32 value = val;
+
+	value &= mask;
+	value >>= (ffs(mask) - 1);
+	return value;
+}
+
+static inline unsigned int set_bits(unsigned int val, unsigned int mask)
+{
+	u32 value = val;
+
+	value <<= (ffs(mask) - 1);
+	value &= mask;
+	return value;
+}
+
+#define reset_bits(mask)			(~(mask))
+
+static inline unsigned char fsd_csis_current_dma_ptr(struct fsd_csis_ctx *ctx)
+{
+	unsigned int dma_act_ctrl = 0;
+
+	dma_act_ctrl = readl(ctx->dev->base + DMA0_ACT_CTRL + DMA_CH_OFFSET * ctx->virtual_channel);
+	return get_bits(dma_act_ctrl, ACTIVE_DMA_FRAMEPTR_MASK);
+}
+
+static inline unsigned int fsd_csis_current_frame_counter(struct fsd_csis_ctx *ctx)
+{
+	return readl(ctx->dev->base + FRM_CNT_CH0 + FRM_CNT_CH_OFFSET * ctx->virtual_channel);
+}
+
+#define ctx_stream_enabled(ctx)			((ctx)->dev->stream_enabled & \
+							(1 << (ctx)->virtual_channel))
+
+#define fsd_csis_dbg(level, dev, fmt, arg...)   \
+		v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg)
+
+#define fsd_csis_warn(dev, fmt, arg...) \
+		v4l2_warn(&(dev)->v4l2_dev, fmt, ##arg)
+
+#define fsd_csis_info(dev, fmt, arg...) \
+		v4l2_info(&(dev)->v4l2_dev, fmt, ##arg)
+
+#define fsd_csis_err(dev, fmt, arg...)  \
+		v4l2_err(&(dev)->v4l2_dev, fmt, ##arg)
+
+#define fsd_csis_ctx_dbg(level, ctx, fmt, arg...)	\
+		v4l2_dbg(level, debug, (ctx)->v4l2_dev, fmt, ##arg)
+
+#define fsd_csis_ctx_info(ctx, fmt, arg...)      \
+		v4l2_info((ctx)->v4l2_dev, fmt, ##arg)
+
+#define fsd_csis_ctx_err(ctx, fmt, arg...)       \
+		v4l2_err((ctx)->v4l2_dev, fmt, ##arg)
+
+#define bytes_per_line(width, bpp)	DIV_ROUND_UP((width) * (bpp), 8)
+
+#endif /* _FSD_CSIS_H */
diff --git a/include/uapi/linux/fsd-csis.h b/include/uapi/linux/fsd-csis.h
new file mode 100644
index 000000000000..ea90f805ad96
--- /dev/null
+++ b/include/uapi/linux/fsd-csis.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * FSD MIPI CSI2 Rx controller - User-space API
+ */
+#ifndef __LINUX_FSD_CSIS_H_
+#define __LINUX_FSD_CSIS_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/v4l2-controls.h>
+
+/*
+ * Custom controls
+ *
+ * V4L2_CID_USER_FSD_CSIS_NO_OF_LANE: Set number of D-PHY data lanes (1~4)
+ */
+#define V4L2_CID_USER_FSD_CSIS_NO_OF_LANE	(V4L2_CID_USER_FSD_CSIS_BASE + 0)
+
+#endif /* __LINUX_FSD_CSIS_H_ */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index b5e7d082b8ad..e9b1dc242cb1 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -231,6 +231,11 @@ enum v4l2_colorfx {
  */
 #define V4L2_CID_USER_DW100_BASE		(V4L2_CID_USER_BASE + 0x1190)
 
+/* The base for the fsd CSI driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_FSD_CSIS_BASE		(V4L2_CID_USER_BASE + 0x10a0)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
