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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxx>
> Cc: Mauro Carvalho Chehab <mchehab@xxxxxxxxxx>
> Cc: Sathyakam M <sathya@xxxxxxxxxxx>
> Cc: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
> Cc: Hans Verkuil <hverkuil-cisco@xxxxxxxxx>
> Cc: Jernej Skrabec <jernej.skrabec@xxxxxxxxx>
> Cc: Ming Qian <ming.qian@xxxxxxx>
> Cc: Dmitry Osipenko <digetx@xxxxxxxxx>
> Cc: Jacopo Mondi <jacopo@xxxxxxxxxx>
> Cc: Pankaj Kumar Dubey <pankaj.dubey@xxxxxxxxxxx>
> Cc: linux-media@xxxxxxxxxxxxxxx
> Cc: linux-kernel@xxxxxxxxxxxxxxx
> ---
>  .../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@xxxxxxxxxxx>
>  L:	linux-media@xxxxxxxxxxxxxxx
>  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@xxxxxxxxxx>
> 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@xxxxxxxxxxx>");
> +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 */

-- 
Regards,

Laurent Pinchart



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux