[4/5] media: fsd: add MIPI CSI2 Rx controller driver

Message ID 7e7832c16925386b771ddb7e00e08661115aa0ea.1668963790.git.sathya@samsung.com
State New
Headers
Series media: add FSD MIPI-CSI2 Rx controller driver |

Commit Message

Sathyakam M Nov. 21, 2022, 4:53 a.m. UTC
  The FSD MIPI CSI2 Rx controller is compliant to MIPI CSI2 v1.3 and
D-PHY v1.2 specifications.

There are up to maximum 4 data lanes (default).
Controls are provided for User to change number of lanes if needed.

Both the video and v4l-subdev instances are exposed to the user
under /dev directory.

The driver can be built as a loadable module or as a platform_driver.

Signed-off-by: Sathyakam M <sathya@samsung.com>
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
Cc: Sathyakam M <sathya@samsung.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Cc: Jernej Skrabec <jernej.skrabec@gmail.com>
Cc: Ming Qian <ming.qian@nxp.com>
Cc: Dmitry Osipenko <digetx@gmail.com>
Cc: Jacopo Mondi <jacopo@jmondi.org>
Cc: Pankaj Kumar Dubey <pankaj.dubey@samsung.com>
Cc: linux-media@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
 .../media/drivers/fsd-csis-uapi.rst           |   78 +
 MAINTAINERS                                   |    1 +
 drivers/media/platform/Kconfig                |    1 +
 drivers/media/platform/Makefile               |    1 +
 drivers/media/platform/fsd/Kconfig            |   73 +
 drivers/media/platform/fsd/Makefile           |    1 +
 drivers/media/platform/fsd/fsd-csis.c         | 2664 +++++++++++++++++
 drivers/media/platform/fsd/fsd-csis.h         |  785 +++++
 include/uapi/linux/fsd-csis.h                 |   19 +
 include/uapi/linux/v4l2-controls.h            |    5 +
 10 files changed, 3628 insertions(+)
 create mode 100644 Documentation/userspace-api/media/drivers/fsd-csis-uapi.rst
 create mode 100644 drivers/media/platform/fsd/Kconfig
 create mode 100644 drivers/media/platform/fsd/Makefile
 create mode 100644 drivers/media/platform/fsd/fsd-csis.c
 create mode 100644 drivers/media/platform/fsd/fsd-csis.h
 create mode 100644 include/uapi/linux/fsd-csis.h
  

Comments

Laurent Pinchart Nov. 21, 2022, 11:05 a.m. UTC | #1
Hi Sathyakam,

Thank you for the patch.

On Mon, Nov 21, 2022 at 10:23:59AM +0530, Sathyakam M wrote:
> The FSD MIPI CSI2 Rx controller is compliant to MIPI CSI2 v1.3 and
> D-PHY v1.2 specifications.
> 
> There are up to maximum 4 data lanes (default).
> Controls are provided for User to change number of lanes if needed.
> 
> Both the video and v4l-subdev instances are exposed to the user
> under /dev directory.
> 
> The driver can be built as a loadable module or as a platform_driver.
> 
> Signed-off-by: Sathyakam M <sathya@samsung.com>
> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
> Cc: Sathyakam M <sathya@samsung.com>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> Cc: Jernej Skrabec <jernej.skrabec@gmail.com>
> Cc: Ming Qian <ming.qian@nxp.com>
> Cc: Dmitry Osipenko <digetx@gmail.com>
> Cc: Jacopo Mondi <jacopo@jmondi.org>
> Cc: Pankaj Kumar Dubey <pankaj.dubey@samsung.com>
> Cc: linux-media@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> ---
>  .../media/drivers/fsd-csis-uapi.rst           |   78 +
>  MAINTAINERS                                   |    1 +
>  drivers/media/platform/Kconfig                |    1 +
>  drivers/media/platform/Makefile               |    1 +
>  drivers/media/platform/fsd/Kconfig            |   73 +
>  drivers/media/platform/fsd/Makefile           |    1 +
>  drivers/media/platform/fsd/fsd-csis.c         | 2664 +++++++++++++++++
>  drivers/media/platform/fsd/fsd-csis.h         |  785 +++++
>  include/uapi/linux/fsd-csis.h                 |   19 +
>  include/uapi/linux/v4l2-controls.h            |    5 +
>  10 files changed, 3628 insertions(+)
>  create mode 100644 Documentation/userspace-api/media/drivers/fsd-csis-uapi.rst
>  create mode 100644 drivers/media/platform/fsd/Kconfig
>  create mode 100644 drivers/media/platform/fsd/Makefile
>  create mode 100644 drivers/media/platform/fsd/fsd-csis.c
>  create mode 100644 drivers/media/platform/fsd/fsd-csis.h
>  create mode 100644 include/uapi/linux/fsd-csis.h
> 
> 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.

Looking at the driver, this also bundles a DMA engine. Furthermore, the
registers show that the CSI-2 RX is a Samsung CSIS, which is already
supported by drivers/media/platform/nxp/imx-mipi-csis.c. This driver
should be split in two, with this driver handling the DMA engine only,
using the imx-mipi-csis.c driver to handle the CSIS and expose it as a
subdev.

> +
> +
> +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 ioctls require an open-source userspace to showcase their usage.
Implementation of a test tool is not enough, this must be part of a real
userspace framework. libcamera is a good option, but other options may
be possible.

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

Why should this be set from userspace, and not from within the kernel ?
Does the value need to change depending on the configuration of the
CSI-2 source ? If so, you could use a subdev operation to query the
number of lanes used by the sensor when starting streaming.

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

I don't see this file in mainline, which tree is this patch based on ? I
also don't see patches 1-3 and 5 on the list, have I missed something ?

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

This shouldn't be a compile-time option, but a runtime option (possibly
coming from DT).

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

Use the printk %p4CC format specifier instead.

> +
> +/*
> + *  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,

No READWRITE please, that's deprecated for this kind of use case.

> +	.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;

Same here, and no USERPTR either.

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

This seems unused.

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

Let's not reinvent the wheel, and use dev_dbg(), dev_info(), ...
explicitly in the code instead.

> +
> +#define bytes_per_line(width, bpp)	DIV_ROUND_UP((width) * (bpp), 8)
> +

Any reason for all these functions and macros to be in the header
instead of the .c file ?

> +#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 */
  
Krzysztof Kozlowski Nov. 21, 2022, 11:48 a.m. UTC | #2
On 21/11/2022 05:53, Sathyakam M wrote:
> The FSD MIPI CSI2 Rx controller is compliant to MIPI CSI2 v1.3 and
> D-PHY v1.2 specifications.
> 
> There are up to maximum 4 data lanes (default).
> Controls are provided for User to change number of lanes if needed.
> 
> Both the video and v4l-subdev instances are exposed to the user
> under /dev directory.
> 
> The driver can be built as a loadable module or as a platform_driver.
> 
> Signed-off-by: Sathyakam M <sathya@samsung.com>
> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
> Cc: Sathyakam M <sathya@samsung.com>
> Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> Cc: Jernej Skrabec <jernej.skrabec@gmail.com>
> Cc: Ming Qian <ming.qian@nxp.com>
> Cc: Dmitry Osipenko <digetx@gmail.com>
> Cc: Jacopo Mondi <jacopo@jmondi.org>
> Cc: Pankaj Kumar Dubey <pankaj.dubey@samsung.com>
> Cc: linux-media@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> ---
>  .../media/drivers/fsd-csis-uapi.rst           |   78 +
>  MAINTAINERS                                   |    1 +
>  drivers/media/platform/Kconfig                |    1 +
>  drivers/media/platform/Makefile               |    1 +
>  drivers/media/platform/fsd/Kconfig            |   73 +
>  drivers/media/platform/fsd/Makefile           |    1 +
>  drivers/media/platform/fsd/fsd-csis.c         | 2664 +++++++++++++++++
>  drivers/media/platform/fsd/fsd-csis.h         |  785 +++++

drivers/media/platform/samsung/

Plus we have drivers for it, so please integrate with existing ones.
Samsung is known of heavily reusing its IP core designs, so they are
usually similar.

Best regards,
Krzysztof
  

Patch

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 */