linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera
@ 2020-08-21 14:59 Kévin L'hôpital
  2020-08-21 14:59 ` [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats Kévin L'hôpital
                   ` (7 more replies)
  0 siblings, 8 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-21 14:59 UTC (permalink / raw)
  To: linux-media
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	Kévin L'hôpital, linux-kernel, mripard,
	paul.kocialkowski, wens, robh+dt, yong.deng, mchehab,
	linux-arm-kernel


Kévin L'hôpital (7):
  media: sun6i-csi: Fix the bpp for 10-bit bayer formats
  dt-bindings: media: i2c: Add documentation for ov8865
  media: i2c: Add support for the OV8865 image sensor
  media: sunxi: sun6i-csi: Move the sun6i_csi_dev structure to the
    common header
  media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T
  ARM: dts: sun8i: a83t: Add support for the MIPI CSI-2 in CSI node
  [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865
    camera

 .../devicetree/bindings/media/i2c/ov8865.txt  |   51 +
 arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts  |   99 +
 arch/arm/boot/dts/sun8i-a83t.dtsi             |   11 +-
 drivers/media/i2c/Kconfig                     |   12 +
 drivers/media/i2c/Makefile                    |    1 +
 drivers/media/i2c/ov8865.c                    | 2540 +++++++++++++++++
 .../media/platform/sunxi/sun6i-csi/Makefile   |    2 +-
 .../platform/sunxi/sun6i-csi/sun6i_csi.c      |   94 +-
 .../platform/sunxi/sun6i-csi/sun6i_csi.h      |   14 +-
 .../sunxi/sun6i-csi/sun8i_a83t_dphy.c         |   20 +
 .../sunxi/sun6i-csi/sun8i_a83t_dphy.h         |   16 +
 .../sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h     |   15 +
 .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c    |  249 ++
 .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h    |   16 +
 .../sun6i-csi/sun8i_a83t_mipi_csi2_reg.h      |   42 +
 15 files changed, 3148 insertions(+), 34 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov8865.txt
 create mode 100644 drivers/media/i2c/ov8865.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h

-- 
2.17.1


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

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

* [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats
  2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
@ 2020-08-21 14:59 ` Kévin L'hôpital
  2020-08-24 16:55   ` Maxime Ripard
  2020-08-25 15:50   ` Chen-Yu Tsai
  2020-08-21 14:59 ` [PATCH 2/7] dt-bindings: media: i2c: Add documentation for ov8865 Kévin L'hôpital
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-21 14:59 UTC (permalink / raw)
  To: linux-media
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	Kévin L'hôpital, linux-kernel, mripard,
	paul.kocialkowski, wens, robh+dt, yong.deng, mchehab,
	linux-arm-kernel

10-bit bayer formats are aligned to 16 bits in memory, so this is what
needs to be used as bpp for calculating the size of the buffers to
allocate.

Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
---
 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index c626821aaedb..8b83d15de0d0 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -100,7 +100,7 @@ static inline int sun6i_csi_get_bpp(unsigned int pixformat)
 	case V4L2_PIX_FMT_SGBRG10:
 	case V4L2_PIX_FMT_SGRBG10:
 	case V4L2_PIX_FMT_SRGGB10:
-		return 10;
+		return 16;
 	case V4L2_PIX_FMT_SBGGR12:
 	case V4L2_PIX_FMT_SGBRG12:
 	case V4L2_PIX_FMT_SGRBG12:
-- 
2.17.1


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

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

* [PATCH 2/7] dt-bindings: media: i2c: Add documentation for ov8865
  2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
  2020-08-21 14:59 ` [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats Kévin L'hôpital
@ 2020-08-21 14:59 ` Kévin L'hôpital
  2020-08-24 16:59   ` Maxime Ripard
  2020-08-21 14:59 ` [PATCH 3/7] media: i2c: Add support for the OV8865 image sensor Kévin L'hôpital
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-21 14:59 UTC (permalink / raw)
  To: linux-media
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	Kévin L'hôpital, linux-kernel, mripard,
	paul.kocialkowski, wens, robh+dt, yong.deng, mchehab,
	linux-arm-kernel

Add a documentation for the sensor ov8865 from Omnivision.

Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
---
 .../devicetree/bindings/media/i2c/ov8865.txt  | 51 +++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov8865.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/ov8865.txt b/Documentation/devicetree/bindings/media/i2c/ov8865.txt
new file mode 100644
index 000000000000..ac5a662288de
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov8865.txt
@@ -0,0 +1,51 @@
+* Omnivision OV8865 MIPI CSI-2
+
+Required Properties:
+- compatible: should be "ovti,ov8865"
+- clocks: reference to the xclk input clock.
+- clock-names: should be "xclk".
+- DOVDD-supply: Digital I/O voltage supply, 2.8 volts
+- AVDD-supply: Analog voltage supply, 2.8 volts
+- AFVDD-supply: Analog voltage supply, 2.8 volts
+- DVDD-supply: Digital core voltage supply, 1.2 volts
+- reset-gpios: reference to the GPIO connected to the reset pin.
+	       This is an active low signal to the OV8865.
+- powerdown-gpios: reference to the GPIO connected to the powerdown pin.
+		   This is an active low signal to the OV8865.
+- rotation: as defined in
+	    Documentation/devicetree/bindings/media/video-interfaces.txt,
+	    valid values are 0 (sensor mounted upright) and 180 (sensor
+	    mounted upside down).
+- remote-endpoint: a phandle to the bus receiver's endpoint node.
+- clock-lanes: should be set to <0> (clock lane on hardware lane 0).
+- data-lanes: should be set to <4> (four CSI-2 lanes supported).
+
+The device node must contain one 'port' child node for its digital output video
+port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+&i2c2 {
+	ov8865: camera@36 {
+                 compatible = "ovti,ov8865";
+                 reg = <0x36>;
+                 clocks = <&ccu CLK_CSI_MCLK>;
+                 clock-names ="xclk";
+                 AVDD-supply = <&reg_ov8865_avdd>;
+                 DOVDD-supply = <&reg_ov8865_dovdd>;
+                 VDD2-supply = <&reg_ov8865_vdd2>;
+                 AFVDD-supply = <&reg_ov8865_afvdd>;
+                 powerdown-gpios = <&pio 4 17 GPIO_ACTIVE_LOW>; /* PE17 */
+                 reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */
+                 rotation = <180>;
+
+                 port {
+                         ov8865_to_mipi_csi2: endpoint {
+                                 remote-endpoint = <&mipi_csi2_from_ov8865>;
+                                 data-lanes = <1 2 3 4>;
+                                 clock-lanes = <0>;
+                                 bus-type = <4>; /* V4L2_FWNODE_BUS_TYPE_CSI2_DPHY */
+                         };
+		};
+	};
+};
-- 
2.17.1


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

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

* [PATCH 3/7] media: i2c: Add support for the OV8865 image sensor
  2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
  2020-08-21 14:59 ` [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats Kévin L'hôpital
  2020-08-21 14:59 ` [PATCH 2/7] dt-bindings: media: i2c: Add documentation for ov8865 Kévin L'hôpital
@ 2020-08-21 14:59 ` Kévin L'hôpital
  2020-08-21 14:59 ` [PATCH 4/7] media: sunxi: sun6i-csi: Move the sun6i_csi_dev structure to the common header Kévin L'hôpital
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-21 14:59 UTC (permalink / raw)
  To: linux-media
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	Kévin L'hôpital, linux-kernel, mripard,
	paul.kocialkowski, wens, robh+dt, yong.deng, mchehab,
	linux-arm-kernel

The ov8865 sensor from the Omnivision supports up to 3264x2448,
a 10 bits output format and MIPI CSI2 interface.

The following driver adds support of all the resolutions at 30
and 60 fps as well as the adjustement of the exposure, the gain and
the rotation of the image.

Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
---
 drivers/media/i2c/Kconfig  |   12 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/ov8865.c | 2540 ++++++++++++++++++++++++++++++++++++
 3 files changed, 2553 insertions(+)
 create mode 100644 drivers/media/i2c/ov8865.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index c68e002d26ea..6b5eff1ec3a3 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -819,6 +819,18 @@ config VIDEO_OV8856
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov8856.
 
+config VIDEO_OV8865
+	tristate "OmniVision OV8865 sensor support"
+	depends on OF
+	depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor driver for OmniVision
+	  OV8865 camera sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov8856.
+
 config VIDEO_OV9640
 	tristate "OmniVision OV9640 sensor support"
 	depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index c147bb9d28db..f7779483a86a 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
 obj-$(CONFIG_VIDEO_OV772X) += ov772x.o
 obj-$(CONFIG_VIDEO_OV7740) += ov7740.o
 obj-$(CONFIG_VIDEO_OV8856) += ov8856.o
+obj-$(CONFIG_VIDEO_OV8865) += ov8865.o
 obj-$(CONFIG_VIDEO_OV9640) += ov9640.o
 obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
 obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
new file mode 100644
index 000000000000..d99d8c1164f0
--- /dev/null
+++ b/drivers/media/i2c/ov8865.c
@@ -0,0 +1,2540 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OV8865 MIPI Camera Subdev Driver
+ * Copyright (C) 2020 Kévin L'hôpital.
+ * Based on the ov5640 driver and an out of tree ov8865 driver by Allwinner.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* System */
+
+#define OV8865_SW_STANDBY_REG		0x0100
+#define OV8865_SW_STANDBY_STANDBY_N	BIT(0)
+
+#define OV8865_SW_RESET_REG		0x0103
+
+#define OV8865_PLL_CTRL2_REG		0x0302
+#define OV8865_PLL_CTRL3_REG		0x0303
+#define OV8865_PLL_CTRL4_REG		0x0304
+#define OV8865_PLL_CTRLE_REG		0x030e
+#define OV8865_PLL_CTRLF_REG		0x030f
+#define OV8865_PLL_CTRL12_REG		0x0312
+#define OV8865_PLL_CTRL1E_REG		0x031e
+
+#define OV8865_SLAVE_ID_REG		0x3004
+#define OV8865_SLAVE_ID_DEFAULT		0x36
+
+#define OV8865_PUMP_CLK_DIV_REG		0x3015
+
+#define OV8865_MIPI_CTRL_REG		0x3018
+#define OV8865_CLOCK_SEL_REG		0x3020
+#define OV8865_MIPI_SC_CTRL_REG		0X3022
+
+#define OV8865_CHIP_ID_REG		0x300a
+#define OV8865_CHIP_ID			0x008865
+
+/* Exposure/gain/banding */
+
+#define OV8865_EXPOSURE_CTRL_HH_REG	0x3500
+#define OV8865_EXPOSURE_CTRL_H_REG	0x3501
+#define OV8865_EXPOSURE_CTRL_L_REG	0x3502
+#define OV8865_MANUAL_CTRL_REG		0x3503
+#define OV8865_GAIN_CTRL_H_REG		0x3508
+#define OV8865_GAIN_CTRL_L_REG		0x3509
+
+#define OV8865_ASP_CTRL41_REG		0x3641
+#define OV8865_ASP_CTRL46_REG		0x3646
+#define OV8865_ASP_CTRL47_REG		0x3647
+#define OV8865_ASP_CTRL50_REG		0x364a
+
+/* Timing control */
+#define OV8865_X_ADDR_START_H_REG	0x3800
+#define OV8865_X_ADDR_START_L_REG	0x3801
+#define OV8865_Y_ADDR_START_H_REG	0x3802
+#define OV8865_Y_ADDR_START_L_REG	0x3803
+#define OV8865_X_ADDR_END_H_REG		0x3804
+#define OV8865_X_ADDR_END_L_REG		0x3805
+#define OV8865_Y_ADDR_END_H_REG		0x3806
+#define OV8865_Y_ADDR_END_L_REG		0x3807
+#define OV8865_X_OUTPUT_SIZE_REG	0x3808
+#define OV8865_Y_OUTPUT_SIZE_REG	0x380a
+#define OV8865_HTS_REG			0x380c
+#define OV8865_VTS_REG			0x380e
+#define OV8865_ISP_X_WIN_H_REG		0x3810
+#define OV8865_ISP_X_WIN_L_REG		0x3811
+#define OV8865_ISP_Y_WIN_L_REG		0x3813
+#define OV8865_X_INC_ODD_REG		0x3814
+#define OV8865_X_INC_EVEN_REG		0x3815
+#define OV8865_FORMAT1_REG		0x3820
+#define OV8865_FORMAT1_MIRROR_ARR	BIT(1)
+#define OV8865_FORMAT1_MIRROR_DIG	BIT(2)
+#define OV8865_FORMAT2_REG		0x3821
+#define OV8865_FORMAT2_MIRROR_ARR	BIT(1)
+#define OV8865_FORMAT2_MIRROR_DIG	BIT(2)
+#define OV8865_Y_INC_ODD_REG		0x382a
+#define OV8865_Y_INC_EVEN_REG		0x382b
+#define OV8865_BLC_NUM_OPTION_REG	0x3830
+#define OV8865_ZLINE_NUM_OPTION_REG	0x3836
+#define OV8865_RGBC_REG			0x3837
+#define OV8865_AUTO_SIZE_CTRL0_REG	0x3841
+#define OV8865_BOUNDARY_PIX_NUM_REG	0x3846
+
+/* OTP */
+
+#define OV8865_OTP_REG			0x3d85
+#define OV8865_OTP_SETT_STT_ADDR_H_REG	0x3d8c
+#define OV8865_OTP_SETT_STT_ADDR_L_REG	0x3d8d
+
+/* Black Level */
+
+#define OV8865_BLC_CTRL0_REG		0x4000
+#define OV8865_BLC_CTRL1_REG		0x4001
+#define OV8865_BLC_CTRL5_REG		0x4005
+#define OV8865_BLC_CTRLB_REG            0x400b
+#define OV8865_BLC_CTRLD_REG            0x400d
+#define OV8865_BLC_CTRL1B_REG           0x401b
+#define OV8865_BLC_CTRL1D_REG           0x401d
+#define OV8865_BLC_CTRL1F_REG		0x401f
+#define OV8865_ANCHOR_LEFT_START_H_REG	0x4020
+#define OV8865_ANCHOR_LEFT_START_L_REG	0x4021
+#define OV8865_ANCHOR_LEFT_END_H_REG	0x4022
+#define OV8865_ANCHOR_LEFT_END_L_REG	0x4023
+#define OV8865_ANCHOR_RIGHT_START_H_REG	0x4024
+#define OV8865_ANCHOR_RIGHT_START_L_REG	0x4025
+#define OV8865_ANCHOR_RIGHT_END_H_REG	0x4026
+#define OV8865_ANCHOR_RIGHT_END_L_REG	0x4027
+#define OV8865_TOP_ZLINE_ST_REG		0x4028
+#define OV8865_TOP_ZLINE_NUM_REG	0x4029
+#define OV8865_TOP_BKLINE_ST_REG	0x402a
+#define OV8865_TOP_BKLINE_NUM_REG	0x402b
+#define OV8865_BOT_ZLINE_ST_REG		0x402c
+#define OV8865_BOT_ZLINE_NUM_REG	0x402d
+#define OV8865_BOT_BLKLINE_ST_REG	0x402e
+#define OV8865_BOT_BLKLINE_NUM_REG	0x402f
+#define OV8865_BLC_OFFSET_LIMIT_REG	0x4034
+
+/* Format Control */
+
+#define OV8865_CLIP_MAX_HI_REG		0x4300
+#define OV8865_CLIP_MIN_HI_REG		0x4301
+#define OV8865_CLIP_LO_REG		0x4302
+
+#define OV8865_R_VFIFO_READ_START_REG	0x4601
+
+/* MIPI Control */
+
+#define OV8865_MIPI_CTRL13_REG		0x4813
+#define OV8865_CLK_PREPARE_MIN_REG	0x481f
+#define OV8865_PCLK_PERIOD_REG		0x4837
+#define OV8865_LANE_SEL01_REG		0x4850
+#define OV8865_LANE_SEL23_REG		0x4851
+
+/* LVDS Control */
+
+#define OV8865_LVDS_R0_REG		0x4b00
+#define OV8865_LVDS_BLK_TIMES_H_REG	0x4b0c
+#define OV8865_LVDS_BLK_TIMES_L_REG	0x4b0d
+
+/* DSP Control */
+
+#define OV8865_ISP_CTRL0_REG		0x5000
+#define OV8865_ISP_CTRL1_REG		0x5001
+#define OV8865_ISP_CTRL2_REG		0x5002
+
+#define OV8865_AVG_READOUT_REG		0x568a
+
+/* Pre DSP Control */
+
+#define OV8865_PRE_CTRL0		0x5e00
+#define OV8865_PRE_CTRL1		0x5e01
+
+/* OTP DPC Control */
+
+#define OV8865_OTP_CTRL0		0x5b00
+#define OV8865_OTP_CTRL1		0x5b01
+#define OV8865_OTP_CTRL2		0x5b02
+#define OV8865_OTP_CTRL3		0x5b03
+#define OV8865_OTP_CTRL5		0x5b05
+
+/* LENC Control */
+
+#define OV8865_LENC_G0_REG		0x5800
+#define OV8865_LENC_G1_REG		0x5801
+#define OV8865_LENC_G2_REG		0x5802
+#define OV8865_LENC_G3_REG		0x5803
+#define OV8865_LENC_G4_REG		0x5804
+#define OV8865_LENC_G5_REG		0x5805
+#define OV8865_LENC_G10_REG		0x5806
+#define OV8865_LENC_G11_REG		0x5807
+#define OV8865_LENC_G12_REG		0x5808
+#define OV8865_LENC_G13_REG		0x5809
+#define OV8865_LENC_G14_REG		0x580a
+#define OV8865_LENC_G15_REG		0x580b
+#define OV8865_LENC_G20_REG		0x580c
+#define OV8865_LENC_G21_REG		0x580d
+#define OV8865_LENC_G22_REG		0x580e
+#define OV8865_LENC_G23_REG		0x580f
+#define OV8865_LENC_G24_REG		0x5810
+#define OV8865_LENC_G25_REG		0x5811
+#define OV8865_LENC_G30_REG		0x5812
+#define OV8865_LENC_G31_REG		0x5813
+#define OV8865_LENC_G32_REG		0x5814
+#define OV8865_LENC_G33_REG		0x5815
+#define OV8865_LENC_G34_REG		0x5816
+#define OV8865_LENC_G35_REG		0x5817
+#define OV8865_LENC_G40_REG		0x5818
+#define OV8865_LENC_G41_REG		0x5819
+#define OV8865_LENC_G42_REG		0x581a
+#define OV8865_LENC_G43_REG		0x581b
+#define OV8865_LENC_G44_REG		0x581c
+#define OV8865_LENC_G45_REG		0x581d
+#define OV8865_LENC_G50_REG		0x581e
+#define OV8865_LENC_G51_REG		0x581f
+#define OV8865_LENC_G52_REG		0x5820
+#define OV8865_LENC_G53_REG		0x5821
+#define OV8865_LENC_G54_REG		0x5822
+#define OV8865_LENC_G55_REG		0x5823
+#define OV8865_LENC_BR0_REG		0x5824
+#define OV8865_LENC_BR1_REG		0x5825
+#define OV8865_LENC_BR2_REG		0x5826
+#define OV8865_LENC_BR3_REG		0x5827
+#define OV8865_LENC_BR4_REG		0x5828
+#define OV8865_LENC_BR10_REG		0x5829
+#define OV8865_LENC_BR11_REG		0x582a
+#define OV8865_LENC_BR12_REG		0x582b
+#define OV8865_LENC_BR13_REG		0x582c
+#define OV8865_LENC_BR14_REG		0x582d
+#define OV8865_LENC_BR20_REG		0x582e
+#define OV8865_LENC_BR21_REG		0x582f
+#define OV8865_LENC_BR22_REG		0x5830
+#define OV8865_LENC_BR23_REG		0x5831
+#define OV8865_LENC_BR24_REG		0x5832
+#define OV8865_LENC_BR30_REG		0x5833
+#define OV8865_LENC_BR31_REG		0x5834
+#define OV8865_LENC_BR32_REG		0x5835
+#define OV8865_LENC_BR33_REG		0x5836
+#define OV8865_LENC_BR34_REG		0x5837
+#define OV8865_LENC_BR40_REG		0x5838
+#define OV8865_LENC_BR41_REG		0x5839
+#define OV8865_LENC_BR42_REG		0x583a
+#define OV8865_LENC_BR43_REG		0x583b
+#define OV8865_LENC_BR44_REG		0x583c
+#define OV8865_LENC_BROFFSET_REG	0x583d
+
+enum ov8865_mode_id {
+	OV8865_MODE_QUXGA_3264_2448 = 0,
+	OV8865_MODE_6M_3264_1836,
+	OV8865_MODE_1080P_1920_1080,
+	OV8865_MODE_720P_1280_720,
+	OV8865_MODE_UXGA_1600_1200,
+	OV8865_MODE_SVGA_800_600,
+	OV8865_MODE_VGA_640_480,
+	OV8865_NUM_MODES,
+};
+
+
+enum ov8865_frame_rate {
+	OV8865_30_FPS = 0,
+	OV8865_90_FPS,
+	OV8865_NUM_FRAMERATES,
+};
+
+static const int ov8865_framerates[] = {
+	[OV8865_30_FPS] = 30,
+	[OV8865_90_FPS] = 90,
+};
+
+struct ov8865_pixfmt {
+	u32 code;
+	u32 colorspace;
+};
+
+static const struct ov8865_pixfmt ov8865_formats[] = {
+	{ MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_RAW, },
+};
+
+/* regulator supplies */
+static const char * const ov8865_supply_names[] = {
+	"AVDD",  /* Analog (2.8V) supply */
+	"DOVDD", /* Digital I/O (1,8V/2.8V) supply */
+	"VDD2",  /* Digital Core (1.2V) supply */
+	"AFVDD",
+};
+
+#define OV8865_NUM_SUPPLIES ARRAY_SIZE(ov8865_supply_names)
+
+struct reg_value {
+	u16 reg_addr;
+	u8 val;
+	u32 delay_ms;
+};
+
+struct ov8865_mode_info {
+	enum ov8865_mode_id id;
+	u32 hact;
+	u32 htot;
+	u32 vact;
+	u32 vtot;
+	const struct reg_value *reg_data;
+	u32 reg_data_size;
+};
+
+struct ov8865_ctrls {
+	struct v4l2_ctrl_handler handler;
+	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *gain;
+	struct v4l2_ctrl *hflip;
+	struct v4l2_ctrl *vflip;
+};
+
+struct ov8865_dev {
+	struct i2c_client *i2c_client;
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	struct v4l2_fwnode_endpoint ep;
+	struct clk *xclk;
+	u32 xclk_freq;
+
+	struct regulator_bulk_data supplies[OV8865_NUM_SUPPLIES];
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *pwdn_gpio;
+	bool upside_down;
+
+	struct mutex lock;
+
+	int power_count;
+
+	struct v4l2_mbus_framefmt fmt;
+
+	const struct ov8865_mode_info *current_mode;
+	const struct ov8865_mode_info *last_mode;
+	enum ov8865_frame_rate current_fr;
+	struct v4l2_fract frame_interval;
+	struct ov8865_ctrls ctrls;
+
+	bool streaming;
+};
+
+static inline struct ov8865_dev *to_ov8865_dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov8865_dev, sd);
+}
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct ov8865_dev,
+			     ctrls.handler)->sd;
+}
+
+static const struct reg_value ov8865_init_setting_QUXGA[] = {
+	{ OV8865_SW_RESET_REG, 0x01, 16 },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+	{ 0x3638, 0xff },
+	{ OV8865_PUMP_CLK_DIV_REG, 0x01 },
+	{ OV8865_MIPI_SC_CTRL_REG, 0x01 },
+	{ 0x3031, 0x0a },
+	{ 0x3305, 0xf1 },
+	{ 0x3308, 0x00 },
+	{ 0x3309, 0x28 },
+	{ 0x330a, 0x00 },
+	{ 0x330b, 0x20 },
+	{ 0x330c, 0x00 },
+	{ 0x330d, 0x00 },
+	{ 0x330e, 0x00 },
+	{ 0x330f, 0x40 },
+	{ 0x3307, 0x04 },
+	{ 0x3604, 0x04 },
+	{ 0x3602, 0x30 },
+	{ 0x3605, 0x00 },
+	{ 0x3607, 0x20 },
+	{ 0x3608, 0x11 },
+	{ 0x3609, 0x68 },
+	{ 0x360a, 0x40 },
+	{ 0x360c, 0xdd },
+	{ 0x360e, 0x0c },
+	{ 0x3610, 0x07 },
+	{ 0x3612, 0x86 },
+	{ 0x3613, 0x58 },
+	{ 0x3614, 0x28 },
+	{ 0x3617, 0x40 },
+	{ 0x3618, 0x5a },
+	{ 0x3619, 0x9b },
+	{ 0x361c, 0x00 },
+	{ 0x361d, 0x60 },
+	{ 0x3631, 0x60 },
+	{ 0x3633, 0x10 },
+	{ 0x3634, 0x10 },
+	{ 0x3635, 0x10 },
+	{ 0x3636, 0x10 },
+	{ OV8865_ASP_CTRL41_REG, 0x55 },
+	{ OV8865_ASP_CTRL46_REG, 0x86 },
+	{ OV8865_ASP_CTRL47_REG, 0x27 },
+	{ OV8865_ASP_CTRL50_REG, 0x1b },
+	{ OV8865_EXPOSURE_CTRL_HH_REG, 0x00 },
+	{ OV8865_EXPOSURE_CTRL_H_REG, 0x4c },
+	{ OV8865_EXPOSURE_CTRL_L_REG, 0x00 },
+	{ OV8865_MANUAL_CTRL_REG, 0x00 },
+	{ OV8865_GAIN_CTRL_H_REG, 0x02 },
+	{ OV8865_GAIN_CTRL_L_REG, 0x00 },
+	{ 0x3700, 0x24 },
+	{ 0x3701, 0x0c },
+	{ 0x3702, 0x28 },
+	{ 0x3703, 0x19 },
+	{ 0x3704, 0x14 },
+	{ 0x3705, 0x00 },
+	{ 0x3706, 0x38 },
+	{ 0x3707, 0x04 },
+	{ 0x3708, 0x24 },
+	{ 0x3709, 0x40 },
+	{ 0x370a, 0x00 },
+	{ 0x370b, 0xb8 },
+	{ 0x370c, 0x04 },
+	{ 0x3718, 0x12 },
+	{ 0x3719, 0x31 },
+	{ 0x3712, 0x42 },
+	{ 0x3714, 0x12 },
+	{ 0x371e, 0x19 },
+	{ 0x371f, 0x40 },
+	{ 0x3720, 0x05 },
+	{ 0x3721, 0x05 },
+	{ 0x3724, 0x02 },
+	{ 0x3725, 0x02 },
+	{ 0x3726, 0x06 },
+	{ 0x3728, 0x05 },
+	{ 0x3729, 0x02 },
+	{ 0x372a, 0x03 },
+	{ 0x372b, 0x53 },
+	{ 0x372c, 0xa3 },
+	{ 0x372d, 0x53 },
+	{ 0x372e, 0x06 },
+	{ 0x372f, 0x10 },
+	{ 0x3730, 0x01 },
+	{ 0x3731, 0x06 },
+	{ 0x3732, 0x14 },
+	{ 0x3733, 0x10 },
+	{ 0x3734, 0x40 },
+	{ 0x3736, 0x20 },
+	{ 0x373a, 0x02 },
+	{ 0x373b, 0x0c },
+	{ 0x373c, 0x0a },
+	{ 0x373e, 0x03 },
+	{ 0x3755, 0x40 },
+	{ 0x3758, 0x00 },
+	{ 0x3759, 0x4c },
+	{ 0x375a, 0x06 },
+	{ 0x375b, 0x13 },
+	{ 0x375c, 0x40 },
+	{ 0x375d, 0x02 },
+	{ 0x375e, 0x00 },
+	{ 0x375f, 0x14 },
+	{ 0x3767, 0x1c },
+	{ 0x3768, 0x04 },
+	{ 0x3769, 0x20 },
+	{ 0x376c, 0xc0 },
+	{ 0x376d, 0xc0 },
+	{ 0x376a, 0x08 },
+	{ 0x3761, 0x00 },
+	{ 0x3762, 0x00 },
+	{ 0x3763, 0x00 },
+	{ 0x3766, 0xff },
+	{ 0x376b, 0x42 },
+	{ 0x3772, 0x23 },
+	{ 0x3773, 0x02 },
+	{ 0x3774, 0x16 },
+	{ 0x3775, 0x12 },
+	{ 0x3776, 0x08 },
+	{ 0x37a0, 0x44 },
+	{ 0x37a1, 0x3d },
+	{ 0x37a2, 0x3d },
+	{ 0x37a3, 0x01 },
+	{ 0x37a4, 0x00 },
+	{ 0x37a5, 0x08 },
+	{ 0x37a6, 0x00 },
+	{ 0x37a7, 0x44 },
+	{ 0x37a8, 0x58 },
+	{ 0x37a9, 0x58 },
+	{ 0x3760, 0x00 },
+	{ 0x376f, 0x01 },
+	{ 0x37aa, 0x44 },
+	{ 0x37ab, 0x2e },
+	{ 0x37ac, 0x2e },
+	{ 0x37ad, 0x33 },
+	{ 0x37ae, 0x0d },
+	{ 0x37af, 0x0d },
+	{ 0x37b0, 0x00 },
+	{ 0x37b1, 0x00 },
+	{ 0x37b2, 0x00 },
+	{ 0x37b3, 0x42 },
+	{ 0x37b4, 0x42 },
+	{ 0x37b5, 0x33 },
+	{ 0x37b6, 0x00 },
+	{ 0x37b7, 0x00 },
+	{ 0x37b8, 0x00 },
+	{ 0x37b9, 0xff },
+	{ OV8865_OTP_REG, 0x06 },
+	{ OV8865_OTP_SETT_STT_ADDR_H_REG, 0x75 },
+	{ OV8865_OTP_SETT_STT_ADDR_L_REG, 0xef },
+	{ 0x3f08, 0x0b },
+	{ OV8865_CLIP_MAX_HI_REG, 0xff },
+	{ OV8865_CLIP_MIN_HI_REG, 0x00 },
+	{ OV8865_CLIP_LO_REG, 0x0f },
+	{ 0x4500, 0x40 },
+	{ 0x4503, 0x10 },
+	{ OV8865_R_VFIFO_READ_START_REG, 0x74 },
+	{ OV8865_CLK_PREPARE_MIN_REG, 0x32 },
+	{ OV8865_PCLK_PERIOD_REG, 0x16 },
+	{ OV8865_LANE_SEL01_REG, 0x10 },
+	{ OV8865_LANE_SEL23_REG, 0x32 },
+	{ OV8865_LVDS_R0_REG, 0x2a },
+	{ OV8865_LVDS_BLK_TIMES_L_REG, 0x00 },
+	{ 0x4d00, 0x04 },
+	{ 0x4d01, 0x18 },
+	{ 0x4d02, 0xc3 },
+	{ 0x4d03, 0xff },
+	{ 0x4d04, 0xff },
+	{ 0x4d05, 0xff },
+	{ OV8865_ISP_CTRL0_REG, 0x96 },
+	{ OV8865_ISP_CTRL1_REG, 0x01 },
+	{ OV8865_ISP_CTRL2_REG, 0x08 },
+	{ 0x5901, 0x00 },
+	{ OV8865_PRE_CTRL0, 0x00 },
+	{ OV8865_PRE_CTRL1, 0x41 },
+	{ OV8865_SW_STANDBY_REG, OV8865_SW_STANDBY_STANDBY_N },
+	{ OV8865_OTP_CTRL0, 0x02 },
+	{ OV8865_OTP_CTRL1, 0xd0 },
+	{ OV8865_OTP_CTRL2, 0x03 },
+	{ OV8865_OTP_CTRL3, 0xff },
+	{ OV8865_OTP_CTRL5, 0x6c },
+	{ 0x5780, 0xfc },
+	{ 0x5781, 0xdf },
+	{ 0x5782, 0x3f },
+	{ 0x5783, 0x08 },
+	{ 0x5784, 0x0c },
+	{ 0x5786, 0x20 },
+	{ 0x5787, 0x40 },
+	{ 0x5788, 0x08 },
+	{ 0x5789, 0x08 },
+	{ 0x578a, 0x02 },
+	{ 0x578b, 0x01 },
+	{ 0x578c, 0x01 },
+	{ 0x578d, 0x0c },
+	{ 0x578e, 0x02 },
+	{ 0x578f, 0x01 },
+	{ 0x5790, 0x01 },
+	{ OV8865_LENC_G0_REG, 0x1d },
+	{ OV8865_LENC_G1_REG, 0x0e },
+	{ OV8865_LENC_G2_REG, 0x0c },
+	{ OV8865_LENC_G3_REG, 0x0c },
+	{ OV8865_LENC_G4_REG, 0x0f },
+	{ OV8865_LENC_G5_REG, 0x22 },
+	{ OV8865_LENC_G10_REG, 0x0a },
+	{ OV8865_LENC_G11_REG, 0x06 },
+	{ OV8865_LENC_G12_REG, 0x05 },
+	{ OV8865_LENC_G13_REG, 0x05 },
+	{ OV8865_LENC_G14_REG, 0x07 },
+	{ OV8865_LENC_G15_REG, 0x0a },
+	{ OV8865_LENC_G20_REG, 0x06 },
+	{ OV8865_LENC_G21_REG, 0x02 },
+	{ OV8865_LENC_G22_REG, 0x00 },
+	{ OV8865_LENC_G23_REG, 0x00 },
+	{ OV8865_LENC_G24_REG, 0x03 },
+	{ OV8865_LENC_G25_REG, 0x07 },
+	{ OV8865_LENC_G30_REG, 0x06 },
+	{ OV8865_LENC_G31_REG, 0x02 },
+	{ OV8865_LENC_G32_REG, 0x00 },
+	{ OV8865_LENC_G33_REG, 0x00 },
+	{ OV8865_LENC_G34_REG, 0x03 },
+	{ OV8865_LENC_G35_REG, 0x07 },
+	{ OV8865_LENC_G40_REG, 0x09 },
+	{ OV8865_LENC_G41_REG, 0x06 },
+	{ OV8865_LENC_G42_REG, 0x04 },
+	{ OV8865_LENC_G43_REG, 0x04 },
+	{ OV8865_LENC_G44_REG, 0x06 },
+	{ OV8865_LENC_G45_REG, 0x0a },
+	{ OV8865_LENC_G50_REG, 0x19 },
+	{ OV8865_LENC_G51_REG, 0x0d },
+	{ OV8865_LENC_G52_REG, 0x0b },
+	{ OV8865_LENC_G53_REG, 0x0b },
+	{ OV8865_LENC_G54_REG, 0x0e },
+	{ OV8865_LENC_G55_REG, 0x22 },
+	{ OV8865_LENC_BR0_REG, 0x23 },
+	{ OV8865_LENC_BR1_REG, 0x28 },
+	{ OV8865_LENC_BR2_REG, 0x29 },
+	{ OV8865_LENC_BR3_REG, 0x27 },
+	{ OV8865_LENC_BR4_REG, 0x13 },
+	{ OV8865_LENC_BR10_REG, 0x26 },
+	{ OV8865_LENC_BR11_REG, 0x33 },
+	{ OV8865_LENC_BR12_REG, 0x32 },
+	{ OV8865_LENC_BR13_REG, 0x33 },
+	{ OV8865_LENC_BR14_REG, 0x16 },
+	{ OV8865_LENC_BR20_REG, 0x14 },
+	{ OV8865_LENC_BR21_REG, 0x30 },
+	{ OV8865_LENC_BR22_REG, 0x31 },
+	{ OV8865_LENC_BR23_REG, 0x30 },
+	{ OV8865_LENC_BR24_REG, 0x15 },
+	{ OV8865_LENC_BR30_REG, 0x26 },
+	{ OV8865_LENC_BR31_REG, 0x23 },
+	{ OV8865_LENC_BR32_REG, 0x21 },
+	{ OV8865_LENC_BR33_REG, 0x23 },
+	{ OV8865_LENC_BR34_REG, 0x05 },
+	{ OV8865_LENC_BR40_REG, 0x36 },
+	{ OV8865_LENC_BR41_REG, 0x27 },
+	{ OV8865_LENC_BR42_REG, 0x28 },
+	{ OV8865_LENC_BR43_REG, 0x26 },
+	{ OV8865_LENC_BR44_REG, 0x24 },
+	{ OV8865_LENC_BROFFSET_REG, 0xdf },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+};
+
+static const struct reg_value ov8865_setting_QUXGA[] = {
+	{ OV8865_SW_STANDBY_REG, 0x00, 5 },
+	{ 0x3501, 0x98 },
+	{ 0x3502, 0x60 },
+	{ 0x3700, 0x48 },
+	{ 0x3701, 0x18 },
+	{ 0x3702, 0x50 },
+	{ 0x3703, 0x32 },
+	{ 0x3704, 0x28 },
+	{ 0x3706, 0x70 },
+	{ 0x3707, 0x08 },
+	{ 0x3708, 0x48 },
+	{ 0x3709, 0x80 },
+	{ 0x370a, 0x01 },
+	{ 0x370b, 0x70 },
+	{ 0x370c, 0x07 },
+	{ 0x3718, 0x14 },
+	{ 0x3712, 0x44 },
+	{ 0x371e, 0x31 },
+	{ 0x371f, 0x7f },
+	{ 0x3720, 0x0a },
+	{ 0x3721, 0x0a },
+	{ 0x3724, 0x04 },
+	{ 0x3725, 0x04 },
+	{ 0x3726, 0x0c },
+	{ 0x3728, 0x0a },
+	{ 0x3729, 0x03 },
+	{ 0x372a, 0x06 },
+	{ 0x372b, 0xa6 },
+	{ 0x372c, 0xa6 },
+	{ 0x372d, 0xa6 },
+	{ 0x372e, 0x0c },
+	{ 0x372f, 0x20 },
+	{ 0x3730, 0x02 },
+	{ 0x3731, 0x0c },
+	{ 0x3732, 0x28 },
+	{ 0x3736, 0x30 },
+	{ 0x373a, 0x04 },
+	{ 0x373b, 0x18 },
+	{ 0x373c, 0x14 },
+	{ 0x373e, 0x06 },
+	{ 0x375a, 0x0c },
+	{ 0x375b, 0x26 },
+	{ 0x375d, 0x04 },
+	{ 0x375f, 0x28 },
+	{ 0x3767, 0x1e },
+	{ 0x3772, 0x46 },
+	{ 0x3773, 0x04 },
+	{ 0x3774, 0x2c },
+	{ 0x3775, 0x13 },
+	{ 0x3776, 0x10 },
+	{ 0x37a0, 0x88 },
+	{ 0x37a1, 0x7a },
+	{ 0x37a2, 0x7a },
+	{ 0x37a3, 0x02 },
+	{ 0x37a5, 0x09 },
+	{ 0x37a7, 0x88 },
+	{ 0x37a8, 0xb0 },
+	{ 0x37a9, 0xb0 },
+	{ 0x37aa, 0x88 },
+	{ 0x37ab, 0x5c },
+	{ 0x37ac, 0x5c },
+	{ 0x37ad, 0x55 },
+	{ 0x37ae, 0x19 },
+	{ 0x37af, 0x19 },
+	{ 0x37b3, 0x84 },
+	{ 0x37b4, 0x84 },
+	{ 0x37b5, 0x66 },
+	{ 0x3f08, 0x16 },
+	{ 0x4500, 0x68 },
+	{ OV8865_R_VFIFO_READ_START_REG, 0x10 },
+	{ OV8865_ISP_CTRL2_REG, 0x08 },
+	{ 0x5901, 0x00 },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+};
+
+static const struct reg_value ov8865_setting_6M[] = {
+	{ OV8865_SW_STANDBY_REG, 0x00, 5 },
+	{ 0x3501, 0x72 },
+	{ 0x3502, 0x20 },
+	{ 0x3700, 0x48 },
+	{ 0x3701, 0x18 },
+	{ 0x3702, 0x50 },
+	{ 0x3703, 0x32 },
+	{ 0x3704, 0x28 },
+	{ 0x3706, 0x70 },
+	{ 0x3707, 0x08 },
+	{ 0x3708, 0x48 },
+	{ 0x3709, 0x80 },
+	{ 0x370a, 0x01 },
+	{ 0x370b, 0x70 },
+	{ 0x370c, 0x07 },
+	{ 0x3718, 0x14 },
+	{ 0x3712, 0x44 },
+	{ 0x371e, 0x31 },
+	{ 0x371f, 0x7f },
+	{ 0x3720, 0x0a },
+	{ 0x3721, 0x0a },
+	{ 0x3724, 0x04 },
+	{ 0x3725, 0x04 },
+	{ 0x3726, 0x0c },
+	{ 0x3728, 0x0a },
+	{ 0x3729, 0x03 },
+	{ 0x372a, 0x06 },
+	{ 0x372b, 0xa6 },
+	{ 0x372c, 0xa6 },
+	{ 0x372d, 0xa6 },
+	{ 0x372e, 0x0c },
+	{ 0x372f, 0x20 },
+	{ 0x3730, 0x02 },
+	{ 0x3731, 0x0c },
+	{ 0x3732, 0x28 },
+	{ 0x3736, 0x30 },
+	{ 0x373a, 0x04 },
+	{ 0x373b, 0x18 },
+	{ 0x373c, 0x14 },
+	{ 0x373e, 0x06 },
+	{ 0x375a, 0x0c },
+	{ 0x375b, 0x26 },
+	{ 0x375d, 0x04 },
+	{ 0x375f, 0x28 },
+	{ 0x3767, 0x1e },
+	{ 0x3772, 0x46 },
+	{ 0x3773, 0x04 },
+	{ 0x3774, 0x2c },
+	{ 0x3775, 0x13 },
+	{ 0x3776, 0x10 },
+	{ 0x37a0, 0x88 },
+	{ 0x37a1, 0x7a },
+	{ 0x37a2, 0x7a },
+	{ 0x37a3, 0x02 },
+	{ 0x37a5, 0x09 },
+	{ 0x37a7, 0x88 },
+	{ 0x37a8, 0xb0 },
+	{ 0x37a9, 0xb0 },
+	{ 0x37aa, 0x88 },
+	{ 0x37ab, 0x5c },
+	{ 0x37ac, 0x5c },
+	{ 0x37ad, 0x55 },
+	{ 0x37ae, 0x19 },
+	{ 0x37af, 0x19 },
+	{ 0x37b3, 0x84 },
+	{ 0x37b4, 0x84 },
+	{ 0x37b5, 0x66 },
+	{ 0x3f08, 0x16 },
+	{ 0x4500, 0x68 },
+	{ OV8865_R_VFIFO_READ_START_REG, 0x10 },
+	{ OV8865_ISP_CTRL2_REG, 0x08 },
+	{ 0x5901, 0x00 },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+};
+
+
+static const struct reg_value ov8865_setting_UXGA[] = {
+	{ OV8865_SW_STANDBY_REG, 0x00, 5 },
+	{ 0x3501, 0x4c },
+	{ 0x3502, 0x00 },
+	{ 0x3700, 0x24 },
+	{ 0x3701, 0x0c },
+	{ 0x3702, 0x28 },
+	{ 0x3703, 0x19 },
+	{ 0x3704, 0x14 },
+	{ 0x3706, 0x38 },
+	{ 0x3707, 0x04 },
+	{ 0x3708, 0x24 },
+	{ 0x3709, 0x40 },
+	{ 0x370a, 0x00 },
+	{ 0x370b, 0xb8 },
+	{ 0x370c, 0x04 },
+	{ 0x3718, 0x12 },
+	{ 0x3712, 0x42 },
+	{ 0x371e, 0x19 },
+	{ 0x371f, 0x40 },
+	{ 0x3720, 0x05 },
+	{ 0x3721, 0x05 },
+	{ 0x3724, 0x02 },
+	{ 0x3725, 0x02 },
+	{ 0x3726, 0x06 },
+	{ 0x3728, 0x05 },
+	{ 0x3729, 0x02 },
+	{ 0x372a, 0x03 },
+	{ 0x372b, 0x53 },
+	{ 0x372c, 0xa3 },
+	{ 0x372d, 0x53 },
+	{ 0x372e, 0x06 },
+	{ 0x372f, 0x10 },
+	{ 0x3730, 0x01 },
+	{ 0x3731, 0x06 },
+	{ 0x3732, 0x14 },
+	{ 0x3736, 0x20 },
+	{ 0x373a, 0x02 },
+	{ 0x373b, 0x0c },
+	{ 0x373c, 0x0a },
+	{ 0x373e, 0x03 },
+	{ 0x375a, 0x06 },
+	{ 0x375b, 0x13 },
+	{ 0x375d, 0x02 },
+	{ 0x375f, 0x14 },
+	{ 0x3767, 0x1c },
+	{ 0x3772, 0x23 },
+	{ 0x3773, 0x02 },
+	{ 0x3774, 0x16 },
+	{ 0x3775, 0x12 },
+	{ 0x3776, 0x08 },
+	{ 0x37a0, 0x44 },
+	{ 0x37a1, 0x3d },
+	{ 0x37a2, 0x3d },
+	{ 0x37a3, 0x01 },
+	{ 0x37a5, 0x08 },
+	{ 0x37a7, 0x44 },
+	{ 0x37a8, 0x58 },
+	{ 0x37a9, 0x58 },
+	{ 0x37aa, 0x44 },
+	{ 0x37ab, 0x2e },
+	{ 0x37ac, 0x2e },
+	{ 0x37ad, 0x33 },
+	{ 0x37ae, 0x0d },
+	{ 0x37af, 0x0d },
+	{ 0x37b3, 0x42 },
+	{ 0x37b4, 0x42 },
+	{ 0x37b5, 0x33 },
+	{ 0x3f08, 0x0b },
+	{ 0x4500, 0x40 },
+	{ OV8865_R_VFIFO_READ_START_REG, 0x74 },
+	{ OV8865_ISP_CTRL2_REG, 0x08 },
+	{ 0x5901, 0x00 },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+};
+
+static const struct reg_value ov8865_setting_SVGA[] = {
+	{ OV8865_SW_STANDBY_REG, 0x00, 5 },
+	{ 0x3501, 0x26 },
+	{ 0x3502, 0x00 },
+	{ 0x3700, 0x24 },
+	{ 0x3701, 0x0c },
+	{ 0x3702, 0x28 },
+	{ 0x3703, 0x19 },
+	{ 0x3704, 0x14 },
+	{ 0x3706, 0x38 },
+	{ 0x3707, 0x04 },
+	{ 0x3708, 0x24 },
+	{ 0x3709, 0x40 },
+	{ 0x370a, 0x00 },
+	{ 0x370b, 0xb8 },
+	{ 0x370c, 0x04 },
+	{ 0x3718, 0x12 },
+	{ 0x3712, 0x42 },
+	{ 0x371e, 0x19 },
+	{ 0x371f, 0x40 },
+	{ 0x3720, 0x05 },
+	{ 0x3721, 0x05 },
+	{ 0x3724, 0x02 },
+	{ 0x3725, 0x02 },
+	{ 0x3726, 0x06 },
+	{ 0x3728, 0x05 },
+	{ 0x3729, 0x02 },
+	{ 0x372a, 0x03 },
+	{ 0x372b, 0x53 },
+	{ 0x372c, 0xa3 },
+	{ 0x372d, 0x53 },
+	{ 0x372e, 0x06 },
+	{ 0x372f, 0x10 },
+	{ 0x3730, 0x01 },
+	{ 0x3731, 0x06 },
+	{ 0x3732, 0x14 },
+	{ 0x3736, 0x20 },
+	{ 0x373a, 0x02 },
+	{ 0x373b, 0x0c },
+	{ 0x373c, 0x0a },
+	{ 0x373e, 0x03 },
+	{ 0x375a, 0x06 },
+	{ 0x375b, 0x13 },
+	{ 0x375d, 0x02 },
+	{ 0x375f, 0x14 },
+	{ 0x3767, 0x18 },
+	{ 0x3772, 0x23 },
+	{ 0x3773, 0x02 },
+	{ 0x3774, 0x16 },
+	{ 0x3775, 0x12 },
+	{ 0x3776, 0x08 },
+	{ 0x37a0, 0x44 },
+	{ 0x37a1, 0x3d },
+	{ 0x37a2, 0x3d },
+	{ 0x37a3, 0x01 },
+	{ 0x37a5, 0x08 },
+	{ 0x37a7, 0x44 },
+	{ 0x37a8, 0x58 },
+	{ 0x37a9, 0x58 },
+	{ 0x37aa, 0x44 },
+	{ 0x37ab, 0x2e },
+	{ 0x37ac, 0x2e },
+	{ 0x37ad, 0x33 },
+	{ 0x37ae, 0x0d },
+	{ 0x37af, 0x0d },
+	{ 0x37b3, 0x42 },
+	{ 0x37b4, 0x42 },
+	{ 0x37b5, 0x33 },
+	{ 0x3f08, 0x0b },
+	{ 0x4500, 0x40 },
+	{ OV8865_R_VFIFO_READ_START_REG, 0x50 },
+	{ OV8865_ISP_CTRL2_REG, 0x0c },
+	{ 0x5901, 0x04 },
+	{ OV8865_SW_STANDBY_REG, 0x00 },
+};
+
+static const struct ov8865_mode_info ov8865_mode_init_data = {
+	.id = 0,
+	.hact = 3264,
+	.htot = 1944,
+	.vact = 2448,
+	.vtot = 2470,
+	.reg_data = ov8865_init_setting_QUXGA,
+	.reg_data_size = ARRAY_SIZE(ov8865_init_setting_QUXGA),
+};
+
+static const struct ov8865_mode_info ov8865_mode_data[OV8865_NUM_MODES] = {
+	{
+		.id = OV8865_MODE_QUXGA_3264_2448,
+		.hact = 3264,
+		.htot = 1944,
+		.vact = 2448,
+		.vtot = 2470,
+		.reg_data = ov8865_setting_QUXGA,
+		.reg_data_size = ARRAY_SIZE(ov8865_setting_QUXGA)
+	},
+	{
+		.id = OV8865_MODE_6M_3264_1836,
+		.hact = 3264,
+		.htot = 2582,
+		.vact = 1836,
+		.vtot = 1858,
+		.reg_data = ov8865_setting_6M,
+		.reg_data_size = ARRAY_SIZE(ov8865_setting_6M)
+	},
+	{
+		.id = OV8865_MODE_1080P_1920_1080,
+		.hact = 1920,
+		.htot = 2582,
+		.vact = 1080,
+		.vtot = 1858,
+		.reg_data = ov8865_setting_6M,
+		.reg_data_size = ARRAY_SIZE(ov8865_setting_6M)
+	},
+	{
+		.id = OV8865_MODE_720P_1280_720,
+		.hact = 1280,
+		.htot = 1923,
+		.vact = 720,
+		.vtot = 1248,
+		.reg_data = ov8865_setting_UXGA,
+		.reg_data_size = ARRAY_SIZE(ov8865_setting_UXGA)
+	},
+	{
+		.id = OV8865_MODE_UXGA_1600_1200,
+		.hact = 1600,
+		.htot = 1923,
+		.vact = 1200,
+		.vtot = 1248,
+		.reg_data = ov8865_setting_UXGA,
+		.reg_data_size = ARRAY_SIZE(ov8865_setting_UXGA)
+	},
+	{
+		.id = OV8865_MODE_SVGA_800_600,
+		.hact = 800,
+		.htot = 1250,
+		.vact = 600,
+		.vtot = 640,
+		.reg_data = ov8865_setting_SVGA,
+		.reg_data_size = ARRAY_SIZE(ov8865_setting_SVGA)
+	},
+	{
+		.id = OV8865_MODE_VGA_640_480,
+		.hact = 640,
+		.htot = 2582,
+		.vact = 480,
+		.vtot = 1858,
+		.reg_data = ov8865_setting_6M,
+		.reg_data_size = ARRAY_SIZE(ov8865_setting_6M)
+	},
+};
+
+static int ov8865_write_reg(struct ov8865_dev *sensor, u16 reg, u8 val)
+{
+	struct i2c_client *client = sensor->i2c_client;
+	struct i2c_msg msg = { 0 };
+	u8 buf[3];
+	int ret;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	buf[2] = val;
+
+	msg.addr = client->addr;
+	msg.flags = client->flags;
+	msg.buf = buf;
+	msg.len = sizeof(buf);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
+			__func__, reg, val);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ov8865_write_reg16(struct ov8865_dev *sensor, u16 reg, u16 val)
+{
+	int ret;
+
+	ret = ov8865_write_reg(sensor, reg, val >> 8);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, reg + 1, val & 0xff);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ov8865_read_reg(struct ov8865_dev *sensor, u16 reg, u8 *val)
+{
+	struct i2c_client *client = sensor->i2c_client;
+	struct i2c_msg msg[2]  = { 0 };
+	u8 buf[2];
+	int ret = 0;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = client->flags;
+	msg[0].buf = buf;
+	msg[0].len = sizeof(buf);
+
+	msg[1].addr = client->addr;
+	/* Read data from the sensor to the controller */
+	msg[1].flags =  I2C_M_RD;
+	msg[1].buf = buf;
+	msg[1].len = 1;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: error: reg=%x\n", __func__, reg);
+		return ret;
+	}
+
+	*val = buf[0];
+
+	return 0;
+}
+
+static int ov8865_read_reg16(struct ov8865_dev *sensor, u16 reg, u16 *val)
+{
+	u8 hi, lo;
+	int ret;
+
+	ret = ov8865_read_reg(sensor, reg, &hi);
+	if (ret)
+		return ret;
+
+	ret = ov8865_read_reg(sensor, reg + 1, &lo);
+	if (ret)
+		return ret;
+
+	*val = ((u16)hi << 8) | (u16)lo;
+
+	return 0;
+}
+
+static int ov8865_mod_reg(struct ov8865_dev *sensor, u16 reg, u8 mask, u8 val)
+{
+	u8 readval;
+	int ret;
+
+	ret = ov8865_read_reg(sensor, reg, &readval);
+	if (ret)
+		return ret;
+
+	readval &= ~mask;
+	val &= mask;
+	val |= readval;
+
+	ret = ov8865_write_reg(sensor, reg, val);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ov8865_set_timings(struct ov8865_dev *sensor,
+			      const struct ov8865_mode_info *mode)
+{
+	int ret;
+	u8 isp_y_win_l, x_inc_odd, format2, y_inc_odd,
+	   y_inc_even, blc_num_option, zline_num_option,
+	   boundary_pix_num;
+
+	ret = ov8865_write_reg(sensor, OV8865_X_ADDR_START_H_REG, 0x00);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_X_ADDR_START_L_REG, 0x0c);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_Y_ADDR_START_H_REG, 0x00);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_Y_ADDR_START_L_REG, 0x0c);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_X_ADDR_END_H_REG, 0x0c);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_X_ADDR_END_L_REG, 0xd3);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_Y_ADDR_END_H_REG, 0x09);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_Y_ADDR_END_L_REG, 0xa3);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg16(sensor, OV8865_X_OUTPUT_SIZE_REG, mode->hact);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg16(sensor, OV8865_Y_OUTPUT_SIZE_REG, mode->vact);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg16(sensor, OV8865_HTS_REG, mode->htot);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg16(sensor, OV8865_VTS_REG, mode->vtot);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ISP_X_WIN_H_REG, 0x00);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ISP_X_WIN_L_REG, 0x04);
+	if (ret)
+		return ret;
+
+	if ((mode->id == OV8865_MODE_720P_1280_720) ||
+	    (mode->id == OV8865_MODE_UXGA_1600_1200) ||
+	    (mode->id == OV8865_MODE_SVGA_800_600)) {
+		isp_y_win_l = 0x04;
+		x_inc_odd = 0x03;
+		blc_num_option = 0x08;
+		zline_num_option = 0x02;
+		boundary_pix_num = 0x88;
+
+	} else {
+		isp_y_win_l = 0x02;
+		x_inc_odd = 0x01;
+		blc_num_option = 0x04;
+		zline_num_option = 0x01;
+		boundary_pix_num = 0x48;
+	}
+
+	ret = ov8865_write_reg(sensor, OV8865_ISP_Y_WIN_L_REG, isp_y_win_l);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_X_INC_ODD_REG, x_inc_odd);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_X_INC_EVEN_REG, 0x01);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_FORMAT1_REG, 0x00);
+	if (ret)
+		return ret;
+
+	if ((mode->id == OV8865_MODE_720P_1280_720) ||
+	    (mode->id == OV8865_MODE_UXGA_1600_1200)) {
+		format2 = 0x67;
+		y_inc_odd = 0x03;
+	} else if (mode->id == OV8865_MODE_SVGA_800_600) {
+		format2 = 0x6f;
+		y_inc_odd = 0x05;
+	} else {
+		format2 = 0x46;
+		y_inc_odd = 0x01;
+	}
+
+	ret = ov8865_write_reg(sensor, OV8865_FORMAT2_REG, format2);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_Y_INC_ODD_REG, y_inc_odd);
+	if (ret)
+		return ret;
+
+	if (mode->id == OV8865_MODE_SVGA_800_600)
+		y_inc_even = 0x03;
+	else
+		y_inc_even = 0x01;
+
+	ret = ov8865_write_reg(sensor, OV8865_Y_INC_EVEN_REG, y_inc_even);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_NUM_OPTION_REG,
+			       blc_num_option);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ZLINE_NUM_OPTION_REG,
+			       zline_num_option);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_RGBC_REG, 0x18);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_AUTO_SIZE_CTRL0_REG, 0xff);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BOUNDARY_PIX_NUM_REG,
+			       boundary_pix_num);
+
+	return 0;
+}
+
+static int ov8865_get_hts(struct ov8865_dev *sensor)
+{
+	u16 hts;
+	int ret;
+
+	ret = ov8865_read_reg16(sensor, OV8865_HTS_REG, &hts);
+	if (ret)
+		return ret;
+	return hts;
+}
+
+static int ov8865_load_regs(struct ov8865_dev *sensor,
+			     const struct ov8865_mode_info *mode)
+{
+	const struct reg_value *regs = mode->reg_data;
+	unsigned int i;
+	u32 delay_ms = 0;
+	u16 reg_addr;
+	u8 val;
+	int ret = 0;
+
+	for (i = 0; i < mode->reg_data_size; i++, regs++) {
+		delay_ms = regs->delay_ms;
+		reg_addr = regs->reg_addr;
+		val = regs->val;
+
+		ret = ov8865_write_reg(sensor, reg_addr, val);
+		if (ret)
+			return ret;
+
+		if (delay_ms)
+			usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
+	}
+
+	return 0;
+}
+
+static const struct ov8865_mode_info *
+ov8865_find_mode(struct ov8865_dev *sensor, enum ov8865_frame_rate fr,
+		 int width, int height, bool nearest)
+{
+	const struct ov8865_mode_info *mode;
+
+	mode = v4l2_find_nearest_size(ov8865_mode_data,
+				      ARRAY_SIZE(ov8865_mode_data),
+				      hact, vact, width, height);
+
+	if (!mode || (!nearest && (mode->hact != width || mode->vact !=
+				   height)))
+		return NULL;
+
+	/* Only SVGA can operate 90 fps. */
+	if (fr == OV8865_90_FPS && !(mode->hact == 800 && mode->vact == 600))
+		return NULL;
+
+	return mode;
+}
+
+static u64 ov8865_calc_pixel_rate(struct ov8865_dev *sensor)
+{
+	u64 rate;
+
+	rate = sensor->current_mode->vtot * sensor->current_mode->htot;
+	rate *= ov8865_framerates[sensor->current_fr];
+
+	return rate;
+}
+
+static int ov8865_set_mode_direct(struct ov8865_dev *sensor,
+			      const struct ov8865_mode_info *mode)
+{
+	int ret;
+
+	if (!mode->reg_data)
+		return -EINVAL;
+
+	/*Write capture setting*/
+	ret = ov8865_load_regs(sensor, mode);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ov8865_set_black_level(struct ov8865_dev *sensor)
+{
+	const struct ov8865_mode_info *mode = sensor->current_mode;
+	int ret;
+	u8 blc_ctrl1, left_start_h, left_start_l, left_end_h,
+	   left_end_l, right_start_h, right_start_l,
+	   right_end_h, right_end_l, bkline_num, bkline_st,
+	   zline_st, zline_num, blkline_st;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_CTRL0_REG, 0xf1);
+	if (ret)
+		return ret;
+
+	if ((mode->id == OV8865_MODE_QUXGA_3264_2448) ||
+	    (mode->id == OV8865_MODE_6M_3264_1836) ||
+	    (mode->id == OV8865_MODE_1080P_1920_1080) ||
+	    (mode->id == OV8865_MODE_VGA_640_480))
+		blc_ctrl1 = 0x04;
+	else
+		blc_ctrl1 = 0x14;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_CTRL1_REG, blc_ctrl1);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_CTRL5_REG, 0x10);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_CTRLB_REG, 0x0c);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_CTRLD_REG, 0x10);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_CTRL1B_REG, 0x00);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_CTRL1D_REG, 0x00);
+	if (ret)
+		return ret;
+
+	if ((mode->id  == OV8865_MODE_QUXGA_3264_2448) ||
+	    (mode->id  == OV8865_MODE_6M_3264_1836) ||
+	    (mode->id == OV8865_MODE_1080P_1920_1080) ||
+	    (mode->id == OV8865_MODE_VGA_640_480)) {
+		left_start_h = 0x02;
+		left_start_l = 0x40;
+		left_end_h = 0x03;
+		left_end_l = 0x3f;
+		right_start_h = 0x07;
+		right_start_l = 0xc0;
+		right_end_h = 0x08;
+		right_end_l = 0xbf;
+	} else {
+		left_start_h = 0x01;
+		left_start_l = 0x20;
+		left_end_h = 0x01;
+		left_end_l = 0x9f;
+		right_start_h = 0x03;
+		right_start_l = 0xe0;
+		right_end_h = 0x04;
+		right_end_l = 0x5f;
+	}
+
+	ret = ov8865_write_reg(sensor, OV8865_ANCHOR_LEFT_START_H_REG,
+			       left_start_h);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ANCHOR_LEFT_START_L_REG,
+			       left_start_l);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ANCHOR_LEFT_END_H_REG,
+			       left_end_h);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ANCHOR_LEFT_END_L_REG,
+			       left_end_l);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ANCHOR_RIGHT_START_H_REG,
+			       right_start_h);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ANCHOR_RIGHT_START_L_REG,
+			       right_start_l);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ANCHOR_RIGHT_END_H_REG,
+			       right_end_h);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_ANCHOR_RIGHT_END_L_REG,
+			       right_end_l);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_TOP_ZLINE_ST_REG, 0x00);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_TOP_ZLINE_NUM_REG, 0x02);
+	if (ret)
+		return ret;
+
+	if (mode->id == OV8865_MODE_SVGA_800_600) {
+		bkline_st = 0x02;
+		bkline_num = 0x02;
+		zline_st = 0x00;
+		zline_num = 0x00;
+		blkline_st = 0x04;
+	} else {
+		bkline_st = 0x04;
+		bkline_num = 0x04;
+		zline_st = 0x02;
+		zline_num = 0x02;
+		blkline_st = 0x08;
+	}
+	ret = ov8865_write_reg(sensor, OV8865_TOP_BKLINE_ST_REG, bkline_st);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_TOP_BKLINE_NUM_REG, bkline_num);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BOT_ZLINE_ST_REG, zline_st);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BOT_ZLINE_NUM_REG, zline_num);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BOT_BLKLINE_ST_REG, blkline_st);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BOT_BLKLINE_NUM_REG, 0x02);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_CTRL1F_REG, 0x00);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_BLC_OFFSET_LIMIT_REG, 0x3f);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ov8865_set_pclk(struct ov8865_dev *sensor)
+{
+	int ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_PLL_CTRL2_REG, 0x1e);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_PLL_CTRL3_REG, 0x00);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_PLL_CTRL4_REG, 0x03);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_CLOCK_SEL_REG, 0x93);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ov8865_get_pclk(struct ov8865_dev *sensor)
+{
+	int ret;
+	u8 pll1_mult, m_div, mipi_div_r, mipi_div, pclk_div_r, pclk_div;
+	int ref_clk = sensor->xclk_freq / 1000000;
+
+	ret = ov8865_read_reg(sensor, OV8865_PLL_CTRL2_REG, &pll1_mult);
+	if (ret)
+		return ret;
+
+	ret = ov8865_read_reg(sensor, OV8865_PLL_CTRL3_REG, &m_div);
+	if (ret)
+		return ret;
+
+	m_div = m_div & 0x07;
+	ret = ov8865_read_reg(sensor, OV8865_PLL_CTRL4_REG, &mipi_div_r);
+	if (ret)
+		return ret;
+
+	mipi_div_r = mipi_div_r & 0x03;
+
+	if (mipi_div_r == 0x00)
+		mipi_div = 4;
+
+	if (mipi_div_r == 0x01)
+		mipi_div = 5;
+
+	if (mipi_div_r == 0x02)
+		mipi_div = 6;
+
+	if (mipi_div_r == 0x03)
+		mipi_div = 8;
+
+	ret = ov8865_read_reg(sensor,  OV8865_CLOCK_SEL_REG, &pclk_div_r);
+	if (ret)
+		return ret;
+
+	pclk_div_r = (pclk_div_r & 0x08) >> 3;
+
+	if (pclk_div_r == 0)
+		pclk_div = 1;
+
+	if (pclk_div_r == 1)
+		pclk_div = 2;
+
+	return ref_clk * pll1_mult / (1 + m_div) / mipi_div / pclk_div;
+}
+
+static int ov8865_set_sclk(struct ov8865_dev *sensor)
+{
+	const struct ov8865_mode_info *mode = sensor->current_mode;
+	int ret;
+	u8 val;
+
+	if ((mode->id  == OV8865_MODE_UXGA_1600_1200) ||
+	    (mode->id == OV8865_MODE_720P_1280_720) ||
+	    (mode->id == OV8865_MODE_SVGA_800_600))
+		val = 0x09;
+	else
+		val = 0x04;
+
+	ret = ov8865_write_reg(sensor, OV8865_PLL_CTRLF_REG, val);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_PLL_CTRL12_REG, 0x01);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_PLL_CTRL1E_REG, 0x0c);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_PLL_CTRLE_REG, 0x00);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ov8865_set_virtual_channel(struct ov8865_dev *sensor, u8 channel)
+{
+	u8 channel_id;
+	int ret;
+
+	ret = ov8865_read_reg(sensor, OV8865_MIPI_CTRL13_REG, &channel_id);
+	if (ret)
+		return ret;
+
+	ret = ov8865_write_reg(sensor, OV8865_MIPI_CTRL13_REG, channel_id |
+				channel);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ov8865_set_mode(struct ov8865_dev *sensor)
+{
+	const struct ov8865_mode_info *mode = sensor->current_mode;
+	int ret;
+
+	ret = ov8865_set_pclk(sensor);
+	if (ret < 0)
+		return ret;
+
+	ret = ov8865_set_sclk(sensor);
+	if (ret < 0)
+		return ret;
+
+	ret = ov8865_set_black_level(sensor);
+	if (ret)
+		return ret;
+
+	ret = ov8865_set_timings(sensor, mode);
+	if (ret)
+		return ret;
+
+	ret = ov8865_set_mode_direct(sensor, mode);
+	if (ret < 0)
+		return ret;
+
+	ret = ov8865_set_virtual_channel(sensor, 0);
+	if (ret < 0)
+		return ret;
+
+	sensor->last_mode = mode;
+	return 0;
+}
+
+static int ov8865_restore_mode(struct ov8865_dev *sensor)
+{
+	int ret;
+
+	ret = ov8865_load_regs(sensor, &ov8865_mode_init_data);
+	if (ret)
+		return ret;
+
+	sensor->last_mode = &ov8865_mode_init_data;
+
+	ret = ov8865_set_mode(sensor);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void ov8865_power(struct ov8865_dev *sensor, bool enable)
+{
+	gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
+}
+
+static void ov8865_reset(struct ov8865_dev *sensor, bool enable)
+{
+	gpiod_set_value_cansleep(sensor->reset_gpio, enable ? 0 : 1);
+}
+
+static int ov8865_set_power_on(struct ov8865_dev *sensor)
+{
+	struct i2c_client *client = sensor->i2c_client;
+	int ret = 0;
+
+	ov8865_power(sensor, false);
+	ov8865_reset(sensor, false);
+
+	ret = clk_prepare_enable(sensor->xclk);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to enable clock\n",
+			__func__);
+		return ret;
+	}
+
+	ov8865_power(sensor, true);
+
+	ret = regulator_bulk_enable(OV8865_NUM_SUPPLIES, sensor->supplies);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to enable regulators\n",
+			__func__);
+		goto err_power_off;
+	}
+
+	ov8865_reset(sensor, true);
+	usleep_range(10000, 12000);
+
+	return 0;
+
+err_power_off:
+	ov8865_power(sensor, false);
+	clk_disable_unprepare(sensor->xclk);
+	return ret;
+}
+
+static void ov8865_set_power_off(struct ov8865_dev *sensor)
+{
+	ov8865_power(sensor, false);
+	regulator_bulk_disable(OV8865_NUM_SUPPLIES, sensor->supplies);
+	clk_disable_unprepare(sensor->xclk);
+}
+
+static int ov8865_set_power(struct ov8865_dev *sensor, bool on)
+{
+	int ret = 0;
+
+	if (on) {
+		ret = ov8865_set_power_on(sensor);
+		if (ret)
+			return ret;
+
+		ret = ov8865_restore_mode(sensor);
+		if (ret)
+			goto err_power_off;
+	} else {
+		ov8865_set_power_off(sensor);
+	}
+
+	return 0;
+
+err_power_off:
+	ov8865_set_power_off(sensor);
+	return ret;
+}
+
+static int ov8865_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	int ret = 0;
+
+	mutex_lock(&sensor->lock);
+	if (sensor->power_count == !on) {
+		ret = ov8865_set_power(sensor, !!on);
+		if (ret)
+			goto out;
+	}
+
+	/* Update the power count. */
+	sensor->power_count += on ? 1 : -1;
+	WARN_ON(sensor->power_count < 0);
+out:
+	mutex_unlock(&sensor->lock);
+
+	if (on && !ret && sensor->power_count == 1) {
+		/* Initialize the hardware. */
+		ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
+	}
+
+	return ret;
+}
+
+static int ov8865_try_frame_interval(struct ov8865_dev *sensor,
+				     struct v4l2_fract *fi,
+				     u32 width, u32 height)
+{
+	const struct ov8865_mode_info *mode;
+	enum ov8865_frame_rate rate = OV8865_30_FPS;
+	int minfps, maxfps, best_fps, fps;
+	int i;
+
+	minfps = ov8865_framerates[OV8865_30_FPS];
+	maxfps = ov8865_framerates[OV8865_90_FPS];
+
+	if (fi->numerator == 0) {
+		fi->denominator = maxfps;
+		fi->numerator = 1;
+		rate = OV8865_90_FPS;
+		goto find_mode;
+	}
+
+	fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator),
+			minfps, maxfps);
+
+	best_fps = minfps;
+	for (i = 0; i < ARRAY_SIZE(ov8865_framerates); i++) {
+		int curr_fps = ov8865_framerates[i];
+
+		if (abs(curr_fps - fps) < abs(best_fps - fps)) {
+			best_fps = curr_fps;
+			rate = i;
+		}
+	}
+
+	fi->numerator = 1;
+	fi->denominator = best_fps;
+
+find_mode:
+	mode = ov8865_find_mode(sensor, rate, width, height, false);
+
+	return mode ? rate : -EINVAL;
+}
+
+static int ov8865_try_fmt_internal(struct v4l2_subdev *sd,
+				   struct v4l2_mbus_framefmt *fmt,
+				   enum ov8865_frame_rate fr,
+				   const struct ov8865_mode_info **new_mode)
+{
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	const struct ov8865_mode_info *mode;
+	int i;
+
+	mode = ov8865_find_mode(sensor, fr, fmt->width, fmt->height, true);
+	if (!mode)
+		return -EINVAL;
+
+	fmt->width = mode->hact;
+	fmt->height = mode->vact;
+
+	if (new_mode)
+		*new_mode = mode;
+
+	for (i = 0; i < ARRAY_SIZE(ov8865_formats); i++)
+		if (ov8865_formats[i].code == fmt->code)
+			break;
+
+	if (i == ARRAY_SIZE(ov8865_formats))
+		i = 0;
+
+	fmt->code = ov8865_formats[i].code;
+	fmt->colorspace = ov8865_formats[i].colorspace;
+	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+
+	return 0;
+}
+
+static int ov8865_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	struct v4l2_mbus_framefmt *fmt;
+
+	if (format->pad != 0)
+		return -EINVAL;
+
+	mutex_lock(&sensor->lock);
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		fmt = v4l2_subdev_get_try_format(&sensor->sd, cfg,
+						 format->pad);
+	else
+		fmt = &sensor->fmt;
+
+	if (fmt)
+		format->format = *fmt;
+
+	mutex_unlock(&sensor->lock);
+
+	return fmt ? 0 : -EINVAL;
+}
+
+static int ov8865_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	const struct ov8865_mode_info *new_mode;
+	struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
+	struct v4l2_mbus_framefmt *fmt;
+	int ret;
+
+	if (format->pad != 0)
+		return -EINVAL;
+
+	mutex_lock(&sensor->lock);
+
+	if (sensor->streaming) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = ov8865_try_fmt_internal(sd, mbus_fmt, sensor->current_fr,
+				      &new_mode);
+	if (ret)
+		goto out;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
+	else
+		fmt = &sensor->fmt;
+
+	if (fmt)
+		*fmt = *mbus_fmt;
+	else
+		ret = -EINVAL;
+
+	if (new_mode != sensor->current_mode)
+		sensor->current_mode = new_mode;
+
+	__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
+				 ov8865_calc_pixel_rate(sensor));
+
+out:
+	mutex_unlock(&sensor->lock);
+	return ret;
+}
+
+static int ov8865_set_ctrl_hflip(struct ov8865_dev *sensor, int value)
+{
+	return ov8865_mod_reg(sensor, OV8865_FORMAT2_REG,
+			      OV8865_FORMAT2_MIRROR_DIG |
+			      OV8865_FORMAT2_MIRROR_ARR,
+			      (!(value ^ sensor->upside_down)) ?
+			      (OV8865_FORMAT2_MIRROR_DIG |
+			       OV8865_FORMAT2_MIRROR_ARR) : 0);
+}
+
+static int ov8865_set_ctrl_vflip(struct ov8865_dev *sensor, int value)
+{
+	return ov8865_mod_reg(sensor, OV8865_FORMAT1_REG,
+			      OV8865_FORMAT1_MIRROR_DIG |
+			      OV8865_FORMAT1_MIRROR_ARR,
+			      (value ^ sensor->upside_down) ?
+			      (OV8865_FORMAT2_MIRROR_DIG |
+			       OV8865_FORMAT2_MIRROR_ARR) : 0);
+}
+
+static int ov8865_get_exposure(struct ov8865_dev *sensor)
+{
+	int exp, ret, pclk, hts, line_time;
+	u8 temp;
+
+	ret = ov8865_read_reg(sensor, OV8865_EXPOSURE_CTRL_HH_REG, &temp);
+	if (ret)
+		return ret;
+	exp = ((int)temp & 0x0f) << 16;
+
+	ret = ov8865_read_reg(sensor, OV8865_EXPOSURE_CTRL_H_REG, &temp);
+	if (ret)
+		return ret;
+	exp |= ((int)temp << 8);
+
+	ret = ov8865_read_reg(sensor, OV8865_EXPOSURE_CTRL_L_REG, &temp);
+	if (ret)
+		return ret;
+	exp |= (int)temp;
+
+	ret = ov8865_get_pclk(sensor);
+	if (ret <= 0)
+		return ret;
+
+	pclk = ret;
+
+	ret = ov8865_get_hts(sensor);
+	if (ret <= 0)
+		return ret;
+
+	hts = ret;
+
+	line_time = hts / pclk;
+
+	/* The low 4 bits of exposure are the fractional part. And the unit is
+	 * 1/16 of a line lecture time. The pclk and HTS are used to calculate
+	 * this time. For V4L2, the value 1 of exposure stands for 100us of
+	 * capture.
+	 */
+	return (exp >> 4) * line_time / 16 / 100;
+}
+
+static int ov8865_get_gain(struct ov8865_dev *sensor)
+{
+	u16 gain;
+	int ret;
+
+	/* Linear gain. */
+	ret = ov8865_read_reg16(sensor, OV8865_GAIN_CTRL_H_REG, &gain);
+	if (ret)
+		return ret;
+
+	return gain & 0x1fff;
+}
+
+static int ov8865_set_ctrl_exp(struct ov8865_dev *sensor)
+{
+	struct ov8865_ctrls *ctrls = &sensor->ctrls;
+	int ret = 0, hts, pclk, line_time;
+	int exposure = ctrls->exposure->val;
+	/* The low 4 bits of exposure are the fractional part. And the unit is
+	 * 1/16 of a line lecture time. The pclk and HTS are used to calculate
+	 * this time. For V4L2, the value 1 of exposure stands for 100us of
+	 * capture.
+	 */
+
+	ret = ov8865_get_pclk(sensor);
+	if (ret <= 0)
+		return ret;
+	pclk = ret;
+
+	ret = ov8865_get_hts(sensor);
+	if (ret <= 0)
+		return ret;
+	hts = ret;
+
+	line_time = hts / pclk;
+
+	exposure = ctrls->exposure->val * 16 / line_time * 100;
+	exposure = (exposure << 4);
+
+	if (ctrls->exposure->is_new) {
+		ret = ov8865_write_reg(sensor, OV8865_EXPOSURE_CTRL_L_REG,
+				       exposure & 0xff);
+		if (ret)
+			return ret;
+
+		ret = ov8865_write_reg(sensor, OV8865_EXPOSURE_CTRL_H_REG,
+				       (exposure >> 8) & 0xff);
+		if (ret)
+			return ret;
+
+		ret = ov8865_write_reg(sensor, OV8865_EXPOSURE_CTRL_HH_REG,
+				       (exposure >> 16) & 0x0f);
+	}
+
+	return ret;
+}
+
+static int ov8865_set_ctrl_gain(struct ov8865_dev *sensor)
+{
+	struct ov8865_ctrls *ctrls = &sensor->ctrls;
+	int ret = 0;
+	int val = ctrls->gain->val;
+
+	/* Linear gain. */
+	if (ctrls->gain->is_new)
+		ret = ov8865_write_reg16(sensor, OV8865_GAIN_CTRL_H_REG,
+					 (u16)val & 0x1fff);
+	return ret;
+}
+
+static int ov8865_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	int val;
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		val = ov8865_get_gain(sensor);
+		if (val < 0)
+			return val;
+		sensor->ctrls.gain->val = val;
+		break;
+	case V4L2_CID_EXPOSURE:
+		val = ov8865_get_exposure(sensor);
+		if (val < 0)
+			return val;
+		sensor->ctrls.exposure->val = val;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	int ret;
+
+	if (sensor->power_count == 0)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_GAIN:
+		ret = ov8865_set_ctrl_gain(sensor);
+		break;
+	case V4L2_CID_EXPOSURE:
+		ret = ov8865_set_ctrl_exp(sensor);
+		break;
+	case V4L2_CID_HFLIP:
+		ret = ov8865_set_ctrl_hflip(sensor, ctrl->val);
+		break;
+	case V4L2_CID_VFLIP:
+		ret = ov8865_set_ctrl_vflip(sensor, ctrl->val);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov8865_ctrl_ops = {
+	.g_volatile_ctrl = ov8865_g_volatile_ctrl,
+	.s_ctrl = ov8865_s_ctrl,
+};
+
+static int ov8865_init_controls(struct ov8865_dev *sensor)
+{
+	const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops;
+	struct ov8865_ctrls *ctrls = &sensor->ctrls;
+	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+	int ret;
+
+	v4l2_ctrl_handler_init(hdl, 32);
+	hdl->lock = &sensor->lock;
+	ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE,
+					      0, INT_MAX, 1,
+					      ov8865_calc_pixel_rate(sensor));
+	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, 1,
+					    2000, 1, 1);
+	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, 1*16, 64*16 - 1,
+					1, 1*16);
+	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (hdl->error) {
+		ret = hdl->error;
+		goto err_free_ctrls;
+	}
+
+	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	sensor->sd.ctrl_handler = hdl;
+
+	return 0;
+
+err_free_ctrls:
+	v4l2_ctrl_handler_free(hdl);
+	return ret;
+}
+
+
+static int ov8865_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+
+	if (fse->pad != 0 || fse->index >= OV8865_NUM_MODES)
+		return -EINVAL;
+
+	fse->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	fse->min_width = ov8865_mode_data[fse->index].hact;
+	fse->max_width = fse->min_width;
+	fse->min_height = ov8865_mode_data[fse->index].vact;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int ov8865_enum_frame_interval(struct v4l2_subdev *sd,
+				      struct v4l2_subdev_pad_config *cfg,
+				      struct v4l2_subdev_frame_interval_enum
+				      *fie)
+{
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	struct v4l2_fract tpf;
+	int ret;
+
+	if (fie->pad != 0 || fie->index >= OV8865_NUM_FRAMERATES)
+		return -EINVAL;
+
+	tpf.numerator = 1;
+	tpf.denominator = ov8865_framerates[fie->index];
+
+	ret = ov8865_try_frame_interval(sensor, &tpf,
+					fie->width, fie->height);
+	if (ret < 0)
+		return -EINVAL;
+
+	fie->interval = tpf;
+
+	return 0;
+}
+
+static int ov8865_g_frame_interval(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_frame_interval *fi)
+{
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+
+	mutex_lock(&sensor->lock);
+	fi->interval = sensor->frame_interval;
+	mutex_unlock(&sensor->lock);
+
+	return 0;
+}
+
+static int ov8865_s_frame_interval(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_frame_interval *fi)
+{
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	const struct ov8865_mode_info *mode;
+	int frame_rate, ret = 0;
+
+	if (fi->pad != 0)
+		return -EINVAL;
+
+	mutex_lock(&sensor->lock);
+
+	if (sensor->streaming) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	mode = sensor->current_mode;
+
+	frame_rate = ov8865_try_frame_interval(sensor, &fi->interval,
+					       mode->hact, mode->vact);
+	if (frame_rate < 0) {
+		fi->interval = sensor->frame_interval;
+		goto out;
+	}
+
+	mode = ov8865_find_mode(sensor, frame_rate, mode->hact,
+				 mode->vact, true);
+	if (!mode) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (mode != sensor->current_mode ||
+	    frame_rate != sensor->current_fr) {
+		sensor->current_fr = frame_rate;
+		sensor->frame_interval = fi->interval;
+		sensor->current_mode = mode;
+
+		__v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate,
+					 ov8865_calc_pixel_rate(sensor));
+	}
+
+out:
+	mutex_unlock(&sensor->lock);
+	return ret;
+}
+
+static int ov8865_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->pad != 0 || code->index >= ARRAY_SIZE(ov8865_formats))
+		return -EINVAL;
+
+	code->code = ov8865_formats[code->index].code;
+
+	return 0;
+}
+
+static int ov8865_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+	int ret = 0;
+
+	mutex_lock(&sensor->lock);
+
+	if (sensor->streaming == !enable) {
+		if (enable && ret)
+			goto out;
+
+		ret = ov8865_write_reg(sensor, OV8865_SW_STANDBY_REG, enable ?
+				     OV8865_SW_STANDBY_STANDBY_N : 0x00);
+		if (ret)
+			return ret;
+
+		ret = ov8865_write_reg(sensor, OV8865_MIPI_CTRL_REG,
+				       enable ? 0x72 : 0x62);
+		if (ret)
+			goto out;
+
+		if (!ret)
+			sensor->streaming = enable;
+	}
+
+out:
+	mutex_unlock(&sensor->lock);
+	return ret;
+}
+
+static const struct v4l2_subdev_core_ops ov8865_core_ops = {
+	.s_power = ov8865_s_power,
+	.log_status = v4l2_ctrl_subdev_log_status,
+	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops ov8865_video_ops = {
+	.g_frame_interval = ov8865_g_frame_interval,
+	.s_frame_interval = ov8865_s_frame_interval,
+	.s_stream = ov8865_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops ov8865_pad_ops = {
+	.enum_mbus_code = ov8865_enum_mbus_code,
+	.get_fmt = ov8865_get_fmt,
+	.set_fmt = ov8865_set_fmt,
+	.enum_frame_size = ov8865_enum_frame_size,
+	.enum_frame_interval = ov8865_enum_frame_interval,
+};
+
+static const struct v4l2_subdev_ops ov8865_subdev_ops = {
+	.core = &ov8865_core_ops,
+	.video = &ov8865_video_ops,
+	.pad = &ov8865_pad_ops,
+};
+
+static int ov8865_get_regulators(struct ov8865_dev *sensor)
+{
+	int i;
+
+	for (i = 0; i < OV8865_NUM_SUPPLIES; i++)
+		sensor->supplies[i].supply = ov8865_supply_names[i];
+
+	return devm_regulator_bulk_get(&sensor->i2c_client->dev,
+				       OV8865_NUM_SUPPLIES,
+				       sensor->supplies);
+}
+
+static int ov8865_check_chip_id(struct ov8865_dev *sensor)
+{
+	struct i2c_client *client = sensor->i2c_client;
+	int ret = 0;
+	u8 chip_id_0, chip_id_1, chip_id_2;
+	u32 chip_id = 0x000000;
+
+	ret = ov8865_set_power_on(sensor);
+	if (ret)
+		return ret;
+
+	ret = ov8865_read_reg(sensor, OV8865_CHIP_ID_REG, &chip_id_0);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to reach chip identifier\n",
+			__func__);
+		goto power_off;
+	}
+
+	ret = ov8865_read_reg(sensor, OV8865_CHIP_ID_REG + 1, &chip_id_1);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to reach chip identifier\n",
+			__func__);
+		goto power_off;
+	}
+
+	ret = ov8865_read_reg(sensor, OV8865_CHIP_ID_REG + 2, &chip_id_2);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to reach chip identifier\n",
+			__func__);
+		goto power_off;
+	}
+
+	chip_id = ((u32)chip_id_0 << 16) | ((u32)chip_id_1 << 8) |
+		((u32)chip_id_2);
+
+	if (chip_id != OV8865_CHIP_ID) {
+		dev_err(&client->dev, "%s: wrong chip identifier, expected 0x008865, got 0x%x\n", __func__, chip_id);
+		ret = -ENXIO;
+	}
+
+power_off:
+	ov8865_set_power_off(sensor);
+	return ret;
+}
+
+static int ov8865_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct fwnode_handle *endpoint;
+	struct ov8865_dev *sensor;
+	const struct ov8865_mode_info *default_mode;
+	struct v4l2_mbus_framefmt *fmt;
+	u32 rotation;
+	int ret = 0;
+
+	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->i2c_client = client;
+
+	/*
+	 * Default init sequence initialize sensor to
+	 * RAW SBGGR10 3264x1836@30fps.
+	 */
+
+	default_mode = &ov8865_mode_data[OV8865_MODE_QUXGA_3264_2448];
+
+	fmt = &sensor->fmt;
+	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	fmt->colorspace = V4L2_COLORSPACE_RAW;
+	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+	fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+	fmt->width = default_mode->hact;
+	fmt->height = default_mode->vact;
+	fmt->field = V4L2_FIELD_NONE;
+	sensor->frame_interval.numerator = 1;
+	sensor->frame_interval.denominator = ov8865_framerates[OV8865_30_FPS];
+	sensor->current_fr = OV8865_30_FPS;
+	sensor->current_mode = default_mode;
+	sensor->last_mode = default_mode;
+
+	/* Optional indication of physical rotation of sensor. */
+	ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation",
+				       &rotation);
+	if (!ret) {
+		switch (rotation) {
+		case 180:
+			sensor->upside_down = true;
+			/* fall through */
+		case 0:
+			break;
+		default:
+			dev_warn(dev, "%u degrees rotation is not supported, ignoring..\n",
+				 rotation);
+		}
+	}
+
+	endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev),
+						  NULL);
+	if (!endpoint) {
+		dev_err(dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_fwnode_endpoint_parse(endpoint, &sensor->ep);
+	fwnode_handle_put(endpoint);
+	if (ret) {
+		dev_err(dev, "Could not parse endpoint\n");
+		return ret;
+	}
+
+	/* Get system clock (xclk). */
+	sensor->xclk = devm_clk_get(dev, "xclk");
+	if (IS_ERR(sensor->xclk)) {
+		dev_err(dev, "failed to get xclk\n");
+		return PTR_ERR(sensor->xclk);
+	}
+
+	sensor->xclk_freq = clk_get_rate(sensor->xclk);
+	if (sensor->xclk_freq != 24000000) {
+		dev_err(dev, "xclk frequency out of range: %d Hz, it should be 24000000 Hz\n",
+			sensor->xclk_freq);
+		return -EINVAL;
+	}
+	/* Request optional power down pin. */
+	sensor->pwdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
+						    GPIOD_OUT_HIGH);
+	if (IS_ERR(sensor->pwdn_gpio))
+		return PTR_ERR(sensor->pwdn_gpio);
+
+	/* Request optional reset pin. */
+	sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						     GPIOD_OUT_HIGH);
+	if (IS_ERR(sensor->reset_gpio))
+		return PTR_ERR(sensor->reset_gpio);
+
+	v4l2_i2c_subdev_init(&sensor->sd, client, &ov8865_subdev_ops);
+	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+			    V4L2_SUBDEV_FL_HAS_EVENTS;
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+	if (ret)
+		return ret;
+
+	ret = ov8865_get_regulators(sensor);
+	if (ret)
+		return ret;
+
+	mutex_init(&sensor->lock);
+
+	ret = ov8865_check_chip_id(sensor);
+	if (ret)
+		goto err_entity_cleanup;
+
+	ret = ov8865_init_controls(sensor);
+	if (ret)
+		goto err_entity_cleanup;
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret)
+		goto err_free_ctrls;
+
+	return 0;
+
+err_free_ctrls:
+	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+err_entity_cleanup:
+	mutex_destroy(&sensor->lock);
+	media_entity_cleanup(&sensor->sd.entity);
+	return ret;
+}
+
+
+static int ov8865_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov8865_dev *sensor = to_ov8865_dev(sd);
+
+	v4l2_async_unregister_subdev(&sensor->sd);
+	mutex_destroy(&sensor->lock);
+	media_entity_cleanup(&sensor->sd.entity);
+	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov8865_id[] = {
+	{ "ov8865", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, ov8865_id);
+
+static const struct of_device_id ov8865_dt_ids[] = {
+	{ .compatible = "ovti,ov8865" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ov8865_dt_ids);
+
+static struct i2c_driver ov8865_i2c_driver = {
+	.driver	= {
+		 .name = "ov8865",
+		 .of_match_table = ov8865_dt_ids,
+	 },
+	 .id_table     = ov8865_id,
+	 .probe_new    = ov8865_probe,
+	 .remove       = ov8865_remove,
+};
+
+module_i2c_driver(ov8865_i2c_driver);
+
+MODULE_DESCRIPTION("OV8865 MIPI Camera Subdev Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kévin L'hôpital <kevin.lhopital@bootlin.com>");
-- 
2.17.1


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

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

* [PATCH 4/7] media: sunxi: sun6i-csi: Move the sun6i_csi_dev structure to the common header
  2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
                   ` (2 preceding siblings ...)
  2020-08-21 14:59 ` [PATCH 3/7] media: i2c: Add support for the OV8865 image sensor Kévin L'hôpital
@ 2020-08-21 14:59 ` Kévin L'hôpital
  2020-08-21 14:59 ` [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T Kévin L'hôpital
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-21 14:59 UTC (permalink / raw)
  To: linux-media
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	Kévin L'hôpital, linux-kernel, mripard,
	paul.kocialkowski, wens, robh+dt, yong.deng, mchehab,
	linux-arm-kernel

Access to the sun6i_csi_dev structure is needed to add the
MIPI CSI2 support.

Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
---
 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 12 ------------
 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 12 ++++++++++++
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 055eb0b8e396..680fa31f380a 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -29,18 +29,6 @@
 
 #define MODULE_NAME	"sun6i-csi"
 
-struct sun6i_csi_dev {
-	struct sun6i_csi		csi;
-	struct device			*dev;
-
-	struct regmap			*regmap;
-	struct clk			*clk_mod;
-	struct clk			*clk_ram;
-	struct reset_control		*rstc_bus;
-
-	int				planar_offset[3];
-};
-
 static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
 {
 	return container_of(csi, struct sun6i_csi_dev, csi);
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
index 8b83d15de0d0..c4a87bdab8c3 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -48,6 +48,18 @@ struct sun6i_csi {
 	struct sun6i_video		video;
 };
 
+struct sun6i_csi_dev {
+	struct sun6i_csi	csi;
+	struct device		*dev;
+	struct regmap		*regmap;
+	struct clk		*clk_mod;
+	struct clk		*clk_ram;
+	struct clk		*clk_mipi;
+	struct clk		*clk_misc;
+	struct reset_control	*rstc_bus;
+	int			planar_offset[3];
+};
+
 /**
  * sun6i_csi_is_format_supported() - check if the format supported by csi
  * @csi:	pointer to the csi
-- 
2.17.1


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

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

* [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T
  2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
                   ` (3 preceding siblings ...)
  2020-08-21 14:59 ` [PATCH 4/7] media: sunxi: sun6i-csi: Move the sun6i_csi_dev structure to the common header Kévin L'hôpital
@ 2020-08-21 14:59 ` Kévin L'hôpital
  2020-08-25 14:37   ` Maxime Ripard
  2020-08-21 14:59 ` [PATCH 6/7] ARM: dts: sun8i: a83t: Add support for the MIPI CSI-2 in CSI node Kévin L'hôpital
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-21 14:59 UTC (permalink / raw)
  To: linux-media
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	Kévin L'hôpital, linux-kernel, mripard,
	paul.kocialkowski, wens, robh+dt, yong.deng, mchehab,
	linux-arm-kernel

This patch add the support only for the Allwinner A83T MIPI CSI2

Currently, the driver is not supported the other Allwinner V3's MIPI CSI2

It has been tested with the ov8865 image sensor.

Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
---
 .../media/platform/sunxi/sun6i-csi/Makefile   |   2 +-
 .../platform/sunxi/sun6i-csi/sun6i_csi.c      |  82 ++++--
 .../sunxi/sun6i-csi/sun8i_a83t_dphy.c         |  20 ++
 .../sunxi/sun6i-csi/sun8i_a83t_dphy.h         |  16 ++
 .../sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h     |  15 ++
 .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c    | 249 ++++++++++++++++++
 .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h    |  16 ++
 .../sun6i-csi/sun8i_a83t_mipi_csi2_reg.h      |  42 +++
 8 files changed, 425 insertions(+), 17 deletions(-)
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h

diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile
index e7e315347804..0f3849790463 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/Makefile
+++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
-sun6i-csi-y += sun6i_video.o sun6i_csi.o
+sun6i-csi-y += sun6i_video.o sun6i_csi.o sun8i_a83t_mipi_csi2.o sun8i_a83t_dphy.o
 
 obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index 680fa31f380a..37aec0b57a46 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -26,6 +26,7 @@
 
 #include "sun6i_csi.h"
 #include "sun6i_csi_reg.h"
+#include "sun8i_a83t_mipi_csi2.h"
 
 #define MODULE_NAME	"sun6i-csi"
 
@@ -40,6 +41,18 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
 {
 	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
 
+	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
+		if (!sdev->clk_mipi) {
+			dev_err(sdev->dev, "Use MIPI-CSI2 device with no MIPI clock\n");
+			return false;
+		}
+		if (!sdev->clk_misc) {
+			dev_err(sdev->dev, "Use MIPI-CSI2 device with no misc clock\n");
+			return false;
+		}
+		return true;
+	}
+
 	/*
 	 * Some video receivers have the ability to be compatible with
 	 * 8bit and 16bit bus width.
@@ -160,10 +173,14 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
 		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
 
 		clk_disable_unprepare(sdev->clk_ram);
+
 		if (of_device_is_compatible(dev->of_node,
 					    "allwinner,sun50i-a64-csi"))
 			clk_rate_exclusive_put(sdev->clk_mod);
 		clk_disable_unprepare(sdev->clk_mod);
+		if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
+			sun6i_mipi_csi_clk_disable(csi);
+
 		reset_control_assert(sdev->rstc_bus);
 		return 0;
 	}
@@ -189,10 +206,18 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
 		goto clk_ram_disable;
 	}
 
+	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
+		ret = sun6i_mipi_csi_clk_enable(csi);
+		if (ret)
+			goto reset_control_assert;
+	}
+
 	regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
 
 	return 0;
 
+reset_control_assert:
+	reset_control_assert(sdev->rstc_bus);
 clk_ram_disable:
 	clk_disable_unprepare(sdev->clk_ram);
 clk_mod_disable:
@@ -421,27 +446,33 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
 		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
 			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
 		break;
+	case V4L2_MBUS_CSI2_DPHY:
+		cfg |= CSI_IF_CFG_MIPI_IF_MIPI;
+		sun6i_mipi_csi_setup_bus(csi);
+		break;
 	default:
 		dev_warn(sdev->dev, "Unsupported bus type: %d\n",
 			 endpoint->bus_type);
 		break;
 	}
 
-	switch (bus_width) {
-	case 8:
-		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
-		break;
-	case 10:
-		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
-		break;
-	case 12:
-		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
-		break;
-	case 16: /* No need to configure DATA_WIDTH for 16bit */
-		break;
-	default:
-		dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width);
-		break;
+	if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) {
+		switch (bus_width) {
+		case 8:
+			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
+			break;
+		case 10:
+			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
+			break;
+		case 12:
+			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
+			break;
+		case 16: /* No need to configure DATA_WIDTH for 16bit */
+			break;
+		default:
+			dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width);
+			break;
+		}
 	}
 
 	regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
@@ -593,6 +624,9 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
 	struct regmap *regmap = sdev->regmap;
 
 	if (!enable) {
+		if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
+			sun6i_mipi_csi_set_stream(csi, 0);
+
 		regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
 		regmap_write(regmap, CSI_CH_INT_EN_REG, 0);
 		return;
@@ -609,6 +643,9 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
 
 	regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON,
 			   CSI_CAP_CH0_VCAP_ON);
+
+	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
+		sun6i_mipi_csi_set_stream(csi, 1);
 }
 
 /* -----------------------------------------------------------------------------
@@ -692,6 +729,7 @@ static int sun6i_csi_fwnode_parse(struct device *dev,
 	}
 
 	switch (vep->bus_type) {
+	case V4L2_MBUS_CSI2_DPHY:
 	case V4L2_MBUS_PARALLEL:
 	case V4L2_MBUS_BT656:
 		csi->v4l2_ep = *vep;
@@ -812,7 +850,7 @@ static const struct regmap_config sun6i_csi_regmap_config = {
 	.reg_bits       = 32,
 	.reg_stride     = 4,
 	.val_bits       = 32,
-	.max_register	= 0x9c,
+	.max_register	= 0x2000,
 };
 
 static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
@@ -847,6 +885,18 @@ static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
 		return PTR_ERR(sdev->clk_ram);
 	}
 
+	sdev->clk_mipi = devm_clk_get(&pdev->dev, "mipi");
+	if (IS_ERR(sdev->clk_mipi)) {
+		sdev->clk_mipi = NULL;
+		dev_warn(&pdev->dev, "Unable to acquire mipi clock. No mipi support\n");
+	}
+
+	sdev->clk_misc = devm_clk_get(&pdev->dev, "misc");
+	if (IS_ERR(sdev->clk_misc)) {
+		sdev->clk_misc = NULL;
+		dev_warn(&pdev->dev, "Unable to acquire misc clock. No mipi support\n");
+	}
+
 	sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL);
 	if (IS_ERR(sdev->rstc_bus)) {
 		dev_err(&pdev->dev, "Cannot get reset controller\n");
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
new file mode 100644
index 000000000000..3f5e4395aaa5
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sun6i_dphy.c
+ * Copyright Kévin L'hôpital (C) 2020
+ */
+
+#include "sun8i_a83t_dphy.h"
+#include "sun8i_a83t_dphy_reg.h"
+
+void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev)
+{
+	regmap_write(sdev->regmap, DPHY_CTRL_REG, 0xb8df698e);
+}
+
+void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev)
+{
+	regmap_write(sdev->regmap, DPHY_CTRL_REG, 0x80008000);
+	regmap_write(sdev->regmap, DPHY_ANA0_REG, 0xa0200000);
+}
+
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
new file mode 100644
index 000000000000..f776ed098cb3
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * sun6i_dphy.h
+ * Copyright Kévin L'hôpital (C) 2020
+ */
+
+#ifndef __SUN8I_A83T_DPHY_H__
+#define __SUN8I_A83T_DPHY_H__
+
+#include <linux/regmap.h>
+#include "sun6i_csi.h"
+
+void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev);
+void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev);
+
+#endif /* __SUN8I_A83T_DPHY_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
new file mode 100644
index 000000000000..c88824e4ec2e
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Allwinner A83t DPHY register description
+ * Copyright Kévin L'hôpital (C) 2020
+ */
+
+#ifndef __SUN8I_A83T_DPHY_REG_H__
+#define __SUN8I_A83T_DPHY_REG_H__
+
+
+#define DPHY_OFFSET			0x1000
+#define DPHY_CTRL_REG			(DPHY_OFFSET + 0x010)
+#define DPHY_ANA0_REG			(DPHY_OFFSET + 0x030)
+
+#endif /* __SUN8I_A83T_DPHY_REG_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
new file mode 100644
index 000000000000..3f117e8d447f
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Allwinner A83t MIPI Camera Sensor Interface driver
+ * Copyright Kévin L'hôpital (C) 2020
+ */
+
+#include <linux/clk.h>
+#include "sun8i_a83t_mipi_csi2.h"
+#include "sun8i_a83t_mipi_csi2_reg.h"
+#include "sun8i_a83t_dphy.h"
+#include <linux/delay.h>
+
+#define IS_FLAG(x, y) (((x) & (y)) == y)
+
+enum mipi_csi2_pkt_fmt {
+	MIPI_FS           = 0X00,
+	MIPI_FE           = 0X01,
+	MIPI_LS           = 0X02,
+	MIPI_LE           = 0X03,
+	MIPI_SDAT0          = 0X08,
+	MIPI_SDAT1          = 0X09,
+	MIPI_SDAT2          = 0X0A,
+	MIPI_SDAT3          = 0X0B,
+	MIPI_SDAT4          = 0X0C,
+	MIPI_SDAT5          = 0X0D,
+	MIPI_SDAT6          = 0X0E,
+	MIPI_SDAT7          = 0X0F,
+	MIPI_BLK            = 0X11,
+	MIPI_EMBD         = 0X12,
+	MIPI_YUV420       = 0X18,
+	MIPI_YUV420_10    = 0X19,
+	MIPI_YUV420_CSP   = 0X1C,
+	MIPI_YUV420_CSP_10 =  0X1D,
+	MIPI_YUV422       = 0X1E,
+	MIPI_YUV422_10    = 0X1F,
+	MIPI_RGB565       = 0X22,
+	MIPI_RGB888       = 0X24,
+	MIPI_RAW8         = 0X2A,
+	MIPI_RAW10          = 0X2B,
+	MIPI_RAW12          = 0X2C,
+	MIPI_USR_DAT0     = 0X30,
+	MIPI_USR_DAT1     = 0X31,
+	MIPI_USR_DAT2     = 0X32,
+	MIPI_USR_DAT3     = 0X33,
+	MIPI_USR_DAT4     = 0X34,
+	MIPI_USR_DAT5     = 0X35,
+	MIPI_USR_DAT6     = 0X36,
+	MIPI_USR_DAT7     = 0X37,
+};
+
+static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
+{
+	return container_of(csi, struct sun6i_csi_dev, csi);
+}
+
+static enum mipi_csi2_pkt_fmt get_pkt_fmt(u16 bus_pix_code)
+{
+	switch (bus_pix_code) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+		return MIPI_RGB565;
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+		return MIPI_YUV422;
+	case MEDIA_BUS_FMT_UYVY10_2X10:
+		return MIPI_YUV422_10;
+	case MEDIA_BUS_FMT_RGB888_1X24:
+		return MIPI_RGB888;
+	case MEDIA_BUS_FMT_SBGGR8_1X8:
+	case MEDIA_BUS_FMT_SGBRG8_1X8:
+	case MEDIA_BUS_FMT_SGRBG8_1X8:
+	case MEDIA_BUS_FMT_SRGGB8_1X8:
+		return MIPI_RAW8;
+	case MEDIA_BUS_FMT_SBGGR10_1X10:
+	case MEDIA_BUS_FMT_SGBRG10_1X10:
+	case MEDIA_BUS_FMT_SGRBG10_1X10:
+	case MEDIA_BUS_FMT_SRGGB10_1X10:
+		return MIPI_RAW10;
+	case MEDIA_BUS_FMT_SBGGR12_1X12:
+	case MEDIA_BUS_FMT_SGBRG12_1X12:
+	case MEDIA_BUS_FMT_SGRBG12_1X12:
+	case MEDIA_BUS_FMT_SRGGB12_1X12:
+		return MIPI_RAW12;
+	default:
+		return MIPI_RAW8;
+	}
+}
+
+
+void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable)
+{
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+
+	if (enable)
+		regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
+				  MIPI_CSI2_CFG_REG_SYNC_EN,
+				  MIPI_CSI2_CFG_REG_SYNC_EN);
+	else
+		regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
+				  MIPI_CSI2_CFG_REG_SYNC_EN, 0);
+
+}
+
+void sun6i_mipi_csi_init(struct sun6i_csi_dev *sdev)
+{
+	regmap_write(sdev->regmap, MIPI_CSI2_CTRL_REG, 0xb8c39bec);
+	regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0xb8d257f8);
+	sun6i_dphy_first_init(sdev);
+	regmap_write(sdev->regmap, MIPI_CSI2_RSVD1_REG,
+		     HW_LOCK_REGISTER_VALUE_1);
+	regmap_write(sdev->regmap, MIPI_CSI2_RSVD2_REG,
+		     HW_LOCK_REGISTER_VALUE_2);
+	regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0);
+	regmap_write(sdev->regmap, MIPI_CSI2_VCDT0_REG, 0);
+	regmap_write(sdev->regmap, MIPI_CSI2_CFG_REG, 0xb8c64f24);
+	sun6i_dphy_second_init(sdev);
+	regmap_write(sdev->regmap, MIPI_CSI2_CTRL_REG, 0x80000000);
+	regmap_write(sdev->regmap, MIPI_CSI2_CFG_REG, 0x12200000);
+}
+
+void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi)
+{
+	struct v4l2_fwnode_endpoint *endpoint = &csi->v4l2_ep;
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+	int lane_num = endpoint->bus.mipi_csi2.num_data_lanes;
+	int flags = endpoint->bus.mipi_csi2.flags;
+	int total_rx_ch = 0;
+	int vc[4];
+	int ch;
+
+	sun6i_mipi_csi_init(sdev);
+
+	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_0)) {
+		vc[total_rx_ch] = 0;
+		total_rx_ch++;
+	}
+
+	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_1)) {
+		vc[total_rx_ch] = 1;
+		total_rx_ch++;
+	}
+
+	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_2)) {
+		vc[total_rx_ch] = 2;
+		total_rx_ch++;
+	}
+
+	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_3)) {
+		vc[total_rx_ch] = 3;
+		total_rx_ch++;
+	}
+
+	if (!total_rx_ch) {
+		dev_dbg(sdev->dev,
+			 "No receive channel assigned, using channel 0.\n");
+		vc[total_rx_ch] = 0;
+		total_rx_ch++;
+	}
+	/* Set lane. */
+	regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
+			  MIPI_CSI2_CFG_REG_N_LANE_MASK, (lane_num - 1) <<
+			  MIPI_CSI2_CFG_REG_N_LANE_SHIFT);
+	/* Set total channels. */
+	regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
+			  MIPI_CSI2_CFG_REG_N_CHANNEL_MASK, (total_rx_ch - 1) <<
+			  MIPI_CSI2_CFG_REG_N_CHANNEL_SHIFT);
+
+	for (ch = 0; ch < total_rx_ch; ch++) {
+		switch (ch) {
+		case 0:
+			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+					  MIPI_CSI2_VCDT0_REG_CH0_DT_MASK,
+					  get_pkt_fmt(csi->config.code));
+			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+					  MIPI_CSI2_VCDT0_REG_CH0_VC_MASK,
+					  vc[ch] << MIPI_CSI2_VCDT0_REG_CH0_VC_SHIFT);
+			break;
+		case 1:
+			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+					  MIPI_CSI2_VCDT0_REG_CH1_DT_MASK,
+					  get_pkt_fmt(csi->config.code)
+					  <<
+					  MIPI_CSI2_VCDT0_REG_CH1_DT_SHIFT);
+			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+					  MIPI_CSI2_VCDT0_REG_CH1_VC_MASK,
+					  vc[ch] << MIPI_CSI2_VCDT0_REG_CH1_VC_SHIFT);
+			break;
+		case 2:
+			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+					  MIPI_CSI2_VCDT0_REG_CH2_DT_MASK,
+					  get_pkt_fmt(csi->config.code)
+					  <<
+					  MIPI_CSI2_VCDT0_REG_CH2_DT_SHIFT);
+			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+					  MIPI_CSI2_VCDT0_REG_CH2_VC_MASK,
+					  vc[ch] << MIPI_CSI2_VCDT0_REG_CH2_VC_SHIFT);
+			break;
+		case 3:
+			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+					  MIPI_CSI2_VCDT0_REG_CH3_DT_MASK,
+					  get_pkt_fmt(csi->config.code)
+					  <<
+					  MIPI_CSI2_VCDT0_REG_CH3_DT_SHIFT);
+			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+					  MIPI_CSI2_VCDT0_REG_CH3_VC_MASK,
+					  vc[ch] << MIPI_CSI2_VCDT0_REG_CH3_VC_SHIFT);
+			break;
+		default:
+			regmap_write(sdev->regmap, MIPI_CSI2_VCDT0_REG,
+				     MIPI_CSI2_VCDT0_REG_DEFAULT);
+			break;
+		}
+	}
+	mdelay(10);
+
+}
+
+int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi)
+{
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+	int ret;
+
+	ret = clk_prepare_enable(sdev->clk_mipi);
+	if (ret) {
+		dev_err(sdev->dev, "Enable clk_mipi clk err %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(sdev->clk_misc);
+	if (ret) {
+		dev_err(sdev->dev, "Enable clk_misc clk err %d\n", ret);
+		goto clk_mipi_disable;
+	}
+
+	return 0;
+
+clk_mipi_disable:
+	clk_disable_unprepare(sdev->clk_mipi);
+	return ret;
+}
+
+void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi)
+{
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+
+	clk_disable_unprepare(sdev->clk_misc);
+	clk_disable_unprepare(sdev->clk_mipi);
+}
+
+
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
new file mode 100644
index 000000000000..a94c69ccee39
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright Kévin L'hôpital (C) 2020
+ */
+
+#ifndef __SUN8I_A83T_MIPI_CSI2_H__
+#define __SUN8I_A83T_MIPI_CSI2_H__
+#include <linux/regmap.h>
+#include "sun6i_csi.h"
+
+void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable);
+void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi);
+int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi);
+void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi);
+
+#endif /* __SUN8I_A83T_MIPI_CSI2_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
new file mode 100644
index 000000000000..4d6fde3e50ef
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Allwinner A83t MIPI CSI register description
+ * Copyright Kévin L'hôpital (C) 2020
+ */
+
+#ifndef __SUN8I_A83T_MIPI_CSI2_REG_H__
+#define __SUN8I_A83T_MIPI_CSI2_REG_H__
+
+
+#define MIPI_CSI2_OFFSET			0x1000
+#define MIPI_CSI2_CTRL_REG			(MIPI_CSI2_OFFSET + 0x004)
+#define MIPI_CSI2_RX_PKT_NUM_REG		(MIPI_CSI2_OFFSET + 0x008)
+#define MIPI_CSI2_RSVD1_REG			(MIPI_CSI2_OFFSET + 0x018)
+#define HW_LOCK_REGISTER_VALUE_1		0xb8c8a30c
+#define MIPI_CSI2_RSVD2_REG			(MIPI_CSI2_OFFSET + 0x01c)
+#define HW_LOCK_REGISTER_VALUE_2		0xb8df8ad7
+#define MIPI_CSI2_CFG_REG			(MIPI_CSI2_OFFSET + 0x100)
+#define MIPI_CSI2_CFG_REG_SYNC_EN		BIT(31)
+#define MIPI_CSI2_CFG_REG_N_LANE_SHIFT		4
+#define MIPI_CSI2_CFG_REG_N_LANE_MASK		0x30
+#define MIPI_CSI2_CFG_REG_N_CHANNEL_SHIFT	16
+#define MIPI_CSI2_CFG_REG_N_CHANNEL_MASK	0x30000
+#define MIPI_CSI2_VCDT0_REG			(MIPI_CSI2_OFFSET + 0x104)
+#define MIPI_CSI2_VCDT0_REG_CH0_DT_MASK		0x3f
+#define MIPI_CSI2_VCDT0_REG_CH0_VC_SHIFT	6
+#define MIPI_CSI2_VCDT0_REG_CH0_VC_MASK		0xc0
+#define MIPI_CSI2_VCDT0_REG_CH1_DT_SHIFT	8
+#define MIPI_CSI2_VCDT0_REG_CH1_DT_MASK		0x3f00
+#define MIPI_CSI2_VCDT0_REG_CH1_VC_SHIFT	14
+#define MIPI_CSI2_VCDT0_REG_CH1_VC_MASK		0xc000
+#define MIPI_CSI2_VCDT0_REG_CH2_DT_SHIFT	16
+#define MIPI_CSI2_VCDT0_REG_CH2_DT_MASK		0x3f0000
+#define MIPI_CSI2_VCDT0_REG_CH2_VC_SHIFT	22
+#define MIPI_CSI2_VCDT0_REG_CH2_VC_MASK		0xc00000
+#define MIPI_CSI2_VCDT0_REG_CH3_DT_SHIFT	24
+#define MIPI_CSI2_VCDT0_REG_CH3_DT_MASK		0x3f000000
+#define MIPI_CSI2_VCDT0_REG_CH3_VC_SHIFT	30
+#define MIPI_CSI2_VCDT0_REG_CH3_VC_MASK		0xc0000000
+#define MIPI_CSI2_VCDT0_REG_DEFAULT		0xc0804000
+
+#endif /* __SUN8I_A83T_MIPI_CSI2_REG_H__ */
-- 
2.17.1


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

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

* [PATCH 6/7] ARM: dts: sun8i: a83t: Add support for the MIPI CSI-2 in CSI node
  2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
                   ` (4 preceding siblings ...)
  2020-08-21 14:59 ` [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T Kévin L'hôpital
@ 2020-08-21 14:59 ` Kévin L'hôpital
  2020-08-21 14:59 ` [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera Kévin L'hôpital
  2020-08-24 16:52 ` [PATCH 0/7] Support of MIPI CSI-2 for A83T and " Maxime Ripard
  7 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-21 14:59 UTC (permalink / raw)
  To: linux-media
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	Kévin L'hôpital, linux-kernel, mripard,
	paul.kocialkowski, wens, robh+dt, yong.deng, mchehab,
	linux-arm-kernel

Add in the CSI device node the MIPI CSI2 clocks, interrupt and
increase the memory size in order to add the support of the
MIPI CSI-2 for A83T

Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
---
 arch/arm/boot/dts/sun8i-a83t.dtsi | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/arch/arm/boot/dts/sun8i-a83t.dtsi b/arch/arm/boot/dts/sun8i-a83t.dtsi
index 53c38deb8a08..5e6421eb8e28 100644
--- a/arch/arm/boot/dts/sun8i-a83t.dtsi
+++ b/arch/arm/boot/dts/sun8i-a83t.dtsi
@@ -1025,12 +1025,15 @@
 
 		csi: camera@1cb0000 {
 			compatible = "allwinner,sun8i-a83t-csi";
-			reg = <0x01cb0000 0x1000>;
-			interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+			reg = <0x01cb0000 0x2000>;
+			interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&ccu CLK_BUS_CSI>,
 				 <&ccu CLK_CSI_SCLK>,
-				 <&ccu CLK_DRAM_CSI>;
-			clock-names = "bus", "mod", "ram";
+				 <&ccu CLK_DRAM_CSI>,
+				 <&ccu CLK_MIPI_CSI>,
+				 <&ccu CLK_CSI_MISC>;
+			clock-names = "bus", "mod", "ram", "mipi", "misc";
 			resets = <&ccu RST_BUS_CSI>;
 			status = "disabled";
 
-- 
2.17.1


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

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

* [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera
  2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
                   ` (5 preceding siblings ...)
  2020-08-21 14:59 ` [PATCH 6/7] ARM: dts: sun8i: a83t: Add support for the MIPI CSI-2 in CSI node Kévin L'hôpital
@ 2020-08-21 14:59 ` Kévin L'hôpital
  2020-08-25 14:40   ` Maxime Ripard
  2020-08-24 16:52 ` [PATCH 0/7] Support of MIPI CSI-2 for A83T and " Maxime Ripard
  7 siblings, 1 reply; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-21 14:59 UTC (permalink / raw)
  To: linux-media
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	Kévin L'hôpital, linux-kernel, mripard,
	paul.kocialkowski, wens, robh+dt, yong.deng, mchehab,
	linux-arm-kernel

The Bananapi M3 supports a camera module which includes an
OV8865 sensor connected via the parallel CSI interface and
an OV8865 sensor connected via MIPI CSI-2.

The I2C2 bus is shared by the two sensors as well as active-low
reset signal but each sensor has it own shutdown line.

The I2c address for the OV8865 is 0x36.

The bus type is hardcoded to 4 due to the lack of available
define usable in the device-tree.

Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
---
 arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts | 99 ++++++++++++++++++++
 1 file changed, 99 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts
index 9d34eabba121..f7839094695e 100644
--- a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts
+++ b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts
@@ -85,6 +85,38 @@
 		};
 	};
 
+	reg_ov8865_avdd: ov8865-avdd {
+		compatible = "regulator-fixed";
+		regulator-name = "ov8865-avdd";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		vin-supply = <&reg_dldo4>;
+	};
+
+	reg_ov8865_dovdd: ov8865-dovdd {
+		compatible = "regulator-fixed";
+		regulator-name = "ov8865-dovdd";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		vin-supply = <&reg_dldo4>;
+	};
+
+	reg_ov8865_afvdd: ov8865-afvdd {
+		compatible = "regulator-fixed";
+		regulator-name = "ov8865-afvdd";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		vin-supply = <&reg_dldo4>;
+	};
+
+	reg_ov8865_vdd2: ov8865-vdd2 {
+		compatible = "regulator-fixed";
+		regulator-name = "ov8865-vdd2";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+		vin-supply = <&reg_eldo1>;
+	};
+
 	reg_usb1_vbus: reg-usb1-vbus {
 		compatible = "regulator-fixed";
 		regulator-name = "usb1-vbus";
@@ -115,10 +147,59 @@
 	cpu-supply = <&reg_dcdc3>;
 };
 
+&ccu {
+	assigned-clocks = <&ccu CLK_CSI_MCLK>;
+	assigned-clock-parents = <&osc24M>;
+	assigned-clock-rates = <24000000>;
+};
+
+&csi {
+	pinctrl-names = "default";
+	status = "okay";
+};
+
+&csi_in {
+	mipi_csi2_from_ov8865: endpoint {
+		remote-endpoint = <&ov8865_to_mipi_csi2>;
+		clock-lanes = <0>;
+		data-lanes = <1 2 3 4>;
+		bus-type = <4>;
+	};
+};
+
 &de {
 	status = "okay";
 };
 
+&i2c2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c2_pe_pins>;
+	status = "okay";
+
+	ov8865: camera@36 {
+		compatible = "ovti,ov8865";
+		reg = <0x36>;
+		clocks = <&ccu CLK_CSI_MCLK>;
+		clock-names ="xclk";
+		AVDD-supply = <&reg_ov8865_avdd>;
+		DOVDD-supply = <&reg_ov8865_dovdd>;
+		VDD2-supply = <&reg_ov8865_vdd2>;
+		AFVDD-supply = <&reg_ov8865_afvdd>;
+		powerdown-gpios = <&pio 4 17 GPIO_ACTIVE_LOW>; /* PE17 */
+		reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */
+		rotation = <180>;
+
+		port {
+			ov8865_to_mipi_csi2: endpoint {
+				remote-endpoint = <&mipi_csi2_from_ov8865>;
+				data-lanes = <1 2 3 4>;
+				clock-lanes = <0>;
+				bus-type = <4>; /* V4L2_FWNODE_BUS_TYPE_CSI2_DPHY */
+			};
+		};
+	};
+};
+
 &ehci0 {
 	/* Terminus Tech FE 1.1s 4-port USB 2.0 hub here */
 	status = "okay";
@@ -191,6 +272,11 @@
 	status = "okay";
 };
 
+&pio {
+	pinctrl-names = "default";
+	pinctrl-0 = <&csi_mclk_pin>;
+};
+
 &r_cir {
 	clock-frequency = <3000000>;
 	status = "okay";
@@ -327,11 +413,24 @@
 	regulator-name = "vcc-pd";
 };
 
+&reg_dldo4 {
+	regulator-always-on;
+	regulator-min-microvolt = <2800000>;
+	regulator-max-microvolt = <2800000>;
+	regulator-name = "avdd-csi";
+};
+
 &reg_drivevbus {
 	regulator-name = "usb0-vbus";
 	status = "okay";
 };
 
+&reg_eldo1 {
+	regulator-min-microvolt = <1200000>;
+	regulator-max-microvolt = <1200000>;
+	regulator-name = "dvdd-csi-r";
+};
+
 &reg_fldo1 {
 	regulator-min-microvolt = <1080000>;
 	regulator-max-microvolt = <1320000>;
-- 
2.17.1


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

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

* Re: [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera
  2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
                   ` (6 preceding siblings ...)
  2020-08-21 14:59 ` [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera Kévin L'hôpital
@ 2020-08-24 16:52 ` Maxime Ripard
  2020-08-25 15:20   ` Kévin L'hôpital
  7 siblings, 1 reply; 24+ messages in thread
From: Maxime Ripard @ 2020-08-24 16:52 UTC (permalink / raw)
  To: Kévin L'hôpital
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media


[-- Attachment #1.1: Type: text/plain, Size: 1389 bytes --]

Hi,

On Fri, Aug 21, 2020 at 04:59:28PM +0200, Kévin L'hôpital wrote:
> 
> Kévin L'hôpital (7):
>   media: sun6i-csi: Fix the bpp for 10-bit bayer formats
>   dt-bindings: media: i2c: Add documentation for ov8865
>   media: i2c: Add support for the OV8865 image sensor
>   media: sunxi: sun6i-csi: Move the sun6i_csi_dev structure to the
>     common header
>   media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T
>   ARM: dts: sun8i: a83t: Add support for the MIPI CSI-2 in CSI node
>   [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865
>     camera

You should have a cover letter here to provide some context.

There's a bunch of things that would need to be explained and / or
argued for here, in particular:
  - Why did you need to plumb it into sun6i-csi?
  - You're naming the CSI part as the A83t CSI, while MIPI-CSI has been
    supported since the A31(?), is there a reason for that?
  - This is not documented anywhere, what did you base this work on?

Also, I think that documenting the general challenges you faced (which
were likely because of the first bullet point above) and how you solved
them here would be great to start a discussion if needed.

Finally, iirc, Hans requires a v4l2-compliance run for any new driver,
which isn't strictly the case for this driver, but isn't really *not*
the case either.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

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

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

* Re: [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats
  2020-08-21 14:59 ` [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats Kévin L'hôpital
@ 2020-08-24 16:55   ` Maxime Ripard
  2020-08-25 15:23     ` Kévin L'hôpital
  2020-08-25 15:50   ` Chen-Yu Tsai
  1 sibling, 1 reply; 24+ messages in thread
From: Maxime Ripard @ 2020-08-24 16:55 UTC (permalink / raw)
  To: Kévin L'hôpital
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media


[-- Attachment #1.1: Type: text/plain, Size: 705 bytes --]

On Fri, Aug 21, 2020 at 04:59:29PM +0200, Kévin L'hôpital wrote:
> 10-bit bayer formats are aligned to 16 bits in memory, so this is what
> needs to be used as bpp for calculating the size of the buffers to
> allocate.
> 
> Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>

Generally speaking, you should also explain why it's not an issue for
the callers. Depending on what that function is supposed to be doing
(returning the padded bits or the padded bits per pixel), your patch
could be either right or wrong.

Since all the callers are using it to generate the number of bytes per
line, your patch is indeed correct. But it should be mentionned in the
commit log.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

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

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

* Re: [PATCH 2/7] dt-bindings: media: i2c: Add documentation for ov8865
  2020-08-21 14:59 ` [PATCH 2/7] dt-bindings: media: i2c: Add documentation for ov8865 Kévin L'hôpital
@ 2020-08-24 16:59   ` Maxime Ripard
  2020-08-25 15:17     ` Kévin L'hôpital
  0 siblings, 1 reply; 24+ messages in thread
From: Maxime Ripard @ 2020-08-24 16:59 UTC (permalink / raw)
  To: Kévin L'hôpital
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media


[-- Attachment #1.1: Type: text/plain, Size: 2297 bytes --]

Hi,

On Fri, Aug 21, 2020 at 04:59:30PM +0200, Kévin L'hôpital wrote:
> Add a documentation for the sensor ov8865 from Omnivision.
> 
> Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>

In order to ease the submission of both drivers, you should probably
split this series into two, one with the MIPI-CSI driver, and one with
the ov8865 driver.

> ---
>  .../devicetree/bindings/media/i2c/ov8865.txt  | 51 +++++++++++++++++++
>  1 file changed, 51 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ov8865.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/ov8865.txt b/Documentation/devicetree/bindings/media/i2c/ov8865.txt
> new file mode 100644
> index 000000000000..ac5a662288de
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/ov8865.txt
> @@ -0,0 +1,51 @@
> +* Omnivision OV8865 MIPI CSI-2
> +
> +Required Properties:
> +- compatible: should be "ovti,ov8865"
> +- clocks: reference to the xclk input clock.
> +- clock-names: should be "xclk".
> +- DOVDD-supply: Digital I/O voltage supply, 2.8 volts
> +- AVDD-supply: Analog voltage supply, 2.8 volts
> +- AFVDD-supply: Analog voltage supply, 2.8 volts
> +- DVDD-supply: Digital core voltage supply, 1.2 volts
> +- reset-gpios: reference to the GPIO connected to the reset pin.
> +	       This is an active low signal to the OV8865.
> +- powerdown-gpios: reference to the GPIO connected to the powerdown pin.
> +		   This is an active low signal to the OV8865.
> +- rotation: as defined in
> +	    Documentation/devicetree/bindings/media/video-interfaces.txt,
> +	    valid values are 0 (sensor mounted upright) and 180 (sensor
> +	    mounted upside down).
> +- remote-endpoint: a phandle to the bus receiver's endpoint node.
> +- clock-lanes: should be set to <0> (clock lane on hardware lane 0).
> +- data-lanes: should be set to <4> (four CSI-2 lanes supported).
> +
> +The device node must contain one 'port' child node for its digital output video
> +port, in accordance with the video interface bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.

Free form DT documentation is deprecated nowadays, you should be doing a
YAML schema instead (like the ov8856 driver).

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

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

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

* Re: [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T
  2020-08-21 14:59 ` [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T Kévin L'hôpital
@ 2020-08-25 14:37   ` Maxime Ripard
  2020-08-26  9:17     ` Kévin L'hôpital
  0 siblings, 1 reply; 24+ messages in thread
From: Maxime Ripard @ 2020-08-25 14:37 UTC (permalink / raw)
  To: Kévin L'hôpital
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Hi,

On Fri, Aug 21, 2020 at 04:59:33PM +0200, Kévin L'hôpital wrote:
> This patch add the support only for the Allwinner A83T MIPI CSI2
> 
> Currently, the driver is not supported the other Allwinner V3's MIPI CSI2
> 
> It has been tested with the ov8865 image sensor.
> 
> Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>

Explaining how it's different from the v3s would help

> ---
>  .../media/platform/sunxi/sun6i-csi/Makefile   |   2 +-
>  .../platform/sunxi/sun6i-csi/sun6i_csi.c      |  82 ++++--
>  .../sunxi/sun6i-csi/sun8i_a83t_dphy.c         |  20 ++
>  .../sunxi/sun6i-csi/sun8i_a83t_dphy.h         |  16 ++
>  .../sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h     |  15 ++
>  .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c    | 249 ++++++++++++++++++
>  .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h    |  16 ++
>  .../sun6i-csi/sun8i_a83t_mipi_csi2_reg.h      |  42 +++
>  8 files changed, 425 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
>  create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
>  create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
>  create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
>  create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
>  create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
> 
> diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile
> index e7e315347804..0f3849790463 100644
> --- a/drivers/media/platform/sunxi/sun6i-csi/Makefile
> +++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile
> @@ -1,4 +1,4 @@
>  # SPDX-License-Identifier: GPL-2.0-only
> -sun6i-csi-y += sun6i_video.o sun6i_csi.o
> +sun6i-csi-y += sun6i_video.o sun6i_csi.o sun8i_a83t_mipi_csi2.o sun8i_a83t_dphy.o
>  
>  obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o
> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
> index 680fa31f380a..37aec0b57a46 100644
> --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
> @@ -26,6 +26,7 @@
>  
>  #include "sun6i_csi.h"
>  #include "sun6i_csi_reg.h"
> +#include "sun8i_a83t_mipi_csi2.h"
>  
>  #define MODULE_NAME	"sun6i-csi"
>  
> @@ -40,6 +41,18 @@ bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
>  {
>  	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
>  
> +	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
> +		if (!sdev->clk_mipi) {
> +			dev_err(sdev->dev, "Use MIPI-CSI2 device with no MIPI clock\n");
> +			return false;
> +		}
> +		if (!sdev->clk_misc) {
> +			dev_err(sdev->dev, "Use MIPI-CSI2 device with no misc clock\n");
> +			return false;
> +		}
> +		return true;
> +	}
> +

I'm not sure we need to check for that everywhere, just doing so in the
fwnode parsing function sohuld be enough.

>  	/*
>  	 * Some video receivers have the ability to be compatible with
>  	 * 8bit and 16bit bus width.
> @@ -160,10 +173,14 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
>  		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
>  
>  		clk_disable_unprepare(sdev->clk_ram);
> +
>  		if (of_device_is_compatible(dev->of_node,
>  					    "allwinner,sun50i-a64-csi"))
>  			clk_rate_exclusive_put(sdev->clk_mod);
>  		clk_disable_unprepare(sdev->clk_mod);
> +		if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
> +			sun6i_mipi_csi_clk_disable(csi);
> +
>  		reset_control_assert(sdev->rstc_bus);
>  		return 0;
>  	}
> @@ -189,10 +206,18 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
>  		goto clk_ram_disable;
>  	}
>  
> +	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
> +		ret = sun6i_mipi_csi_clk_enable(csi);
> +		if (ret)
> +			goto reset_control_assert;
> +	}
> +
>  	regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
>  
>  	return 0;
>  
> +reset_control_assert:
> +	reset_control_assert(sdev->rstc_bus);
>  clk_ram_disable:
>  	clk_disable_unprepare(sdev->clk_ram);
>  clk_mod_disable:
> @@ -421,27 +446,33 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
>  		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
>  			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
>  		break;
> +	case V4L2_MBUS_CSI2_DPHY:
> +		cfg |= CSI_IF_CFG_MIPI_IF_MIPI;
> +		sun6i_mipi_csi_setup_bus(csi);
> +		break;
>  	default:
>  		dev_warn(sdev->dev, "Unsupported bus type: %d\n",
>  			 endpoint->bus_type);
>  		break;
>  	}
>  
> -	switch (bus_width) {
> -	case 8:
> -		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
> -		break;
> -	case 10:
> -		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
> -		break;
> -	case 12:
> -		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
> -		break;
> -	case 16: /* No need to configure DATA_WIDTH for 16bit */
> -		break;
> -	default:
> -		dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width);
> -		break;
> +	if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) {
> +		switch (bus_width) {
> +		case 8:
> +			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
> +			break;
> +		case 10:
> +			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
> +			break;
> +		case 12:
> +			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
> +			break;
> +		case 16: /* No need to configure DATA_WIDTH for 16bit */
> +			break;
> +		default:
> +			dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width);
> +			break;
> +		}
>  	}

I'm not sure why we need to do some setup in both cases, and some only
in the MIPI-CSI case here, can you clarify that a bit?

>  
>  	regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
> @@ -593,6 +624,9 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
>  	struct regmap *regmap = sdev->regmap;
>  
>  	if (!enable) {
> +		if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
> +			sun6i_mipi_csi_set_stream(csi, 0);
> +
>  		regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
>  		regmap_write(regmap, CSI_CH_INT_EN_REG, 0);
>  		return;
> @@ -609,6 +643,9 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
>  
>  	regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON,
>  			   CSI_CAP_CH0_VCAP_ON);
> +
> +	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
> +		sun6i_mipi_csi_set_stream(csi, 1);
>  }
>  
>  /* -----------------------------------------------------------------------------
> @@ -692,6 +729,7 @@ static int sun6i_csi_fwnode_parse(struct device *dev,
>  	}
>  
>  	switch (vep->bus_type) {
> +	case V4L2_MBUS_CSI2_DPHY:
>  	case V4L2_MBUS_PARALLEL:
>  	case V4L2_MBUS_BT656:
>  		csi->v4l2_ep = *vep;
> @@ -812,7 +850,7 @@ static const struct regmap_config sun6i_csi_regmap_config = {
>  	.reg_bits       = 32,
>  	.reg_stride     = 4,
>  	.val_bits       = 32,
> -	.max_register	= 0x9c,
> +	.max_register	= 0x2000,
>  };
>  
>  static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
> @@ -847,6 +885,18 @@ static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
>  		return PTR_ERR(sdev->clk_ram);
>  	}
>  
> +	sdev->clk_mipi = devm_clk_get(&pdev->dev, "mipi");
> +	if (IS_ERR(sdev->clk_mipi)) {
> +		sdev->clk_mipi = NULL;
> +		dev_warn(&pdev->dev, "Unable to acquire mipi clock. No mipi support\n");
> +	}
> +
> +	sdev->clk_misc = devm_clk_get(&pdev->dev, "misc");
> +	if (IS_ERR(sdev->clk_misc)) {
> +		sdev->clk_misc = NULL;
> +		dev_warn(&pdev->dev, "Unable to acquire misc clock. No mipi support\n");
> +	}
> +

This will raise an error on platforms that use that driver for CSI but
don't have MIPI-CSI (like the H3 IIRC?), this isn't really ok.

I guess we could have a per-soc flag where you'd say if MIPI-CSI is
supported and only try to get the clock on the relevant SoCs.

Also, you're changing the DT binding, the documentation should be
updated.

>  	sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL);
>  	if (IS_ERR(sdev->rstc_bus)) {
>  		dev_err(&pdev->dev, "Cannot get reset controller\n");
> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
> new file mode 100644
> index 000000000000..3f5e4395aaa5
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
> @@ -0,0 +1,20 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * sun6i_dphy.c
> + * Copyright Kévin L'hôpital (C) 2020
> + */
> +
> +#include "sun8i_a83t_dphy.h"
> +#include "sun8i_a83t_dphy_reg.h"
> +
> +void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev)
> +{
> +	regmap_write(sdev->regmap, DPHY_CTRL_REG, 0xb8df698e);
> +}
> +
> +void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev)
> +{
> +	regmap_write(sdev->regmap, DPHY_CTRL_REG, 0x80008000);
> +	regmap_write(sdev->regmap, DPHY_ANA0_REG, 0xa0200000);
> +}

Some documentation/comment on what that first init and second init is,
and what those magic values are doing would be great here.

> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
> new file mode 100644
> index 000000000000..f776ed098cb3
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * sun6i_dphy.h
> + * Copyright Kévin L'hôpital (C) 2020
> + */
> +
> +#ifndef __SUN8I_A83T_DPHY_H__
> +#define __SUN8I_A83T_DPHY_H__
> +
> +#include <linux/regmap.h>
> +#include "sun6i_csi.h"
> +
> +void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev);
> +void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev);
> +
> +#endif /* __SUN8I_A83T_DPHY_H__ */
> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
> new file mode 100644
> index 000000000000..c88824e4ec2e
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Allwinner A83t DPHY register description
> + * Copyright Kévin L'hôpital (C) 2020
> + */
> +
> +#ifndef __SUN8I_A83T_DPHY_REG_H__
> +#define __SUN8I_A83T_DPHY_REG_H__
> +
> +
> +#define DPHY_OFFSET			0x1000
> +#define DPHY_CTRL_REG			(DPHY_OFFSET + 0x010)
> +#define DPHY_ANA0_REG			(DPHY_OFFSET + 0x030)
> +
> +#endif /* __SUN8I_A83T_DPHY_REG_H__ */

Ideally this should be a D-PHY driver under drivers/phy

> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
> new file mode 100644
> index 000000000000..3f117e8d447f
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
> @@ -0,0 +1,249 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Allwinner A83t MIPI Camera Sensor Interface driver
> + * Copyright Kévin L'hôpital (C) 2020
> + */
> +
> +#include <linux/clk.h>
> +#include "sun8i_a83t_mipi_csi2.h"
> +#include "sun8i_a83t_mipi_csi2_reg.h"
> +#include "sun8i_a83t_dphy.h"
> +#include <linux/delay.h>
> +
> +#define IS_FLAG(x, y) (((x) & (y)) == y)
> +
> +enum mipi_csi2_pkt_fmt {
> +	MIPI_FS           = 0X00,
> +	MIPI_FE           = 0X01,
> +	MIPI_LS           = 0X02,
> +	MIPI_LE           = 0X03,
> +	MIPI_SDAT0          = 0X08,
> +	MIPI_SDAT1          = 0X09,
> +	MIPI_SDAT2          = 0X0A,
> +	MIPI_SDAT3          = 0X0B,
> +	MIPI_SDAT4          = 0X0C,
> +	MIPI_SDAT5          = 0X0D,
> +	MIPI_SDAT6          = 0X0E,
> +	MIPI_SDAT7          = 0X0F,
> +	MIPI_BLK            = 0X11,
> +	MIPI_EMBD         = 0X12,
> +	MIPI_YUV420       = 0X18,
> +	MIPI_YUV420_10    = 0X19,
> +	MIPI_YUV420_CSP   = 0X1C,
> +	MIPI_YUV420_CSP_10 =  0X1D,
> +	MIPI_YUV422       = 0X1E,
> +	MIPI_YUV422_10    = 0X1F,
> +	MIPI_RGB565       = 0X22,
> +	MIPI_RGB888       = 0X24,
> +	MIPI_RAW8         = 0X2A,
> +	MIPI_RAW10          = 0X2B,
> +	MIPI_RAW12          = 0X2C,
> +	MIPI_USR_DAT0     = 0X30,
> +	MIPI_USR_DAT1     = 0X31,
> +	MIPI_USR_DAT2     = 0X32,
> +	MIPI_USR_DAT3     = 0X33,
> +	MIPI_USR_DAT4     = 0X34,
> +	MIPI_USR_DAT5     = 0X35,
> +	MIPI_USR_DAT6     = 0X36,
> +	MIPI_USR_DAT7     = 0X37,
> +};
> +
> +static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
> +{
> +	return container_of(csi, struct sun6i_csi_dev, csi);
> +}
> +
> +static enum mipi_csi2_pkt_fmt get_pkt_fmt(u16 bus_pix_code)
> +{
> +	switch (bus_pix_code) {
> +	case MEDIA_BUS_FMT_RGB565_1X16:
> +		return MIPI_RGB565;
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		return MIPI_YUV422;
> +	case MEDIA_BUS_FMT_UYVY10_2X10:
> +		return MIPI_YUV422_10;
> +	case MEDIA_BUS_FMT_RGB888_1X24:
> +		return MIPI_RGB888;
> +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> +		return MIPI_RAW8;
> +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> +		return MIPI_RAW10;
> +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> +		return MIPI_RAW12;
> +	default:
> +		return MIPI_RAW8;
> +	}
> +}
> +
> +

There's an extra new line here

> +void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable)
> +{
> +	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> +
> +	if (enable)
> +		regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
> +				  MIPI_CSI2_CFG_REG_SYNC_EN,
> +				  MIPI_CSI2_CFG_REG_SYNC_EN);
> +	else
> +		regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
> +				  MIPI_CSI2_CFG_REG_SYNC_EN, 0);

Do you really need regmap_write_bits here, or is regmap_update_bits
enough?

> +
> +}
> +
> +void sun6i_mipi_csi_init(struct sun6i_csi_dev *sdev)
> +{
> +	regmap_write(sdev->regmap, MIPI_CSI2_CTRL_REG, 0xb8c39bec);
> +	regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0xb8d257f8);
> +	sun6i_dphy_first_init(sdev);
> +	regmap_write(sdev->regmap, MIPI_CSI2_RSVD1_REG,
> +		     HW_LOCK_REGISTER_VALUE_1);
> +	regmap_write(sdev->regmap, MIPI_CSI2_RSVD2_REG,
> +		     HW_LOCK_REGISTER_VALUE_2);
> +	regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0);
> +	regmap_write(sdev->regmap, MIPI_CSI2_VCDT0_REG, 0);
> +	regmap_write(sdev->regmap, MIPI_CSI2_CFG_REG, 0xb8c64f24);
> +	sun6i_dphy_second_init(sdev);
> +	regmap_write(sdev->regmap, MIPI_CSI2_CTRL_REG, 0x80000000);
> +	regmap_write(sdev->regmap, MIPI_CSI2_CFG_REG, 0x12200000);

Again, some defines / comments would be great here.

> +}
> +
> +void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi)
> +{
> +	struct v4l2_fwnode_endpoint *endpoint = &csi->v4l2_ep;
> +	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> +	int lane_num = endpoint->bus.mipi_csi2.num_data_lanes;
> +	int flags = endpoint->bus.mipi_csi2.flags;
> +	int total_rx_ch = 0;
> +	int vc[4];
> +	int ch;
> +
> +	sun6i_mipi_csi_init(sdev);
> +
> +	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_0)) {
> +		vc[total_rx_ch] = 0;
> +		total_rx_ch++;
> +	}
> +
> +	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_1)) {
> +		vc[total_rx_ch] = 1;
> +		total_rx_ch++;
> +	}
> +
> +	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_2)) {
> +		vc[total_rx_ch] = 2;
> +		total_rx_ch++;
> +	}
> +
> +	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_3)) {
> +		vc[total_rx_ch] = 3;
> +		total_rx_ch++;
> +	}
> +

Is it supposed to handle multiple virtual channels at once? If so, how
do you get the results of each virtual channel?

> +	if (!total_rx_ch) {
> +		dev_dbg(sdev->dev,
> +			 "No receive channel assigned, using channel 0.\n");
> +		vc[total_rx_ch] = 0;
> +		total_rx_ch++;
> +	}
> +	/* Set lane. */
> +	regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
> +			  MIPI_CSI2_CFG_REG_N_LANE_MASK, (lane_num - 1) <<
> +			  MIPI_CSI2_CFG_REG_N_LANE_SHIFT);
> +	/* Set total channels. */
> +	regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
> +			  MIPI_CSI2_CFG_REG_N_CHANNEL_MASK, (total_rx_ch - 1) <<
> +			  MIPI_CSI2_CFG_REG_N_CHANNEL_SHIFT);
> +
> +	for (ch = 0; ch < total_rx_ch; ch++) {
> +		switch (ch) {
> +		case 0:
> +			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +					  MIPI_CSI2_VCDT0_REG_CH0_DT_MASK,
> +					  get_pkt_fmt(csi->config.code));
> +			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +					  MIPI_CSI2_VCDT0_REG_CH0_VC_MASK,
> +					  vc[ch] << MIPI_CSI2_VCDT0_REG_CH0_VC_SHIFT);
> +			break;
> +		case 1:
> +			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +					  MIPI_CSI2_VCDT0_REG_CH1_DT_MASK,
> +					  get_pkt_fmt(csi->config.code)
> +					  <<
> +					  MIPI_CSI2_VCDT0_REG_CH1_DT_SHIFT);
> +			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +					  MIPI_CSI2_VCDT0_REG_CH1_VC_MASK,
> +					  vc[ch] << MIPI_CSI2_VCDT0_REG_CH1_VC_SHIFT);
> +			break;
> +		case 2:
> +			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +					  MIPI_CSI2_VCDT0_REG_CH2_DT_MASK,
> +					  get_pkt_fmt(csi->config.code)
> +					  <<
> +					  MIPI_CSI2_VCDT0_REG_CH2_DT_SHIFT);
> +			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +					  MIPI_CSI2_VCDT0_REG_CH2_VC_MASK,
> +					  vc[ch] << MIPI_CSI2_VCDT0_REG_CH2_VC_SHIFT);
> +			break;
> +		case 3:
> +			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +					  MIPI_CSI2_VCDT0_REG_CH3_DT_MASK,
> +					  get_pkt_fmt(csi->config.code)
> +					  <<
> +					  MIPI_CSI2_VCDT0_REG_CH3_DT_SHIFT);
> +			regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +					  MIPI_CSI2_VCDT0_REG_CH3_VC_MASK,
> +					  vc[ch] << MIPI_CSI2_VCDT0_REG_CH3_VC_SHIFT);
> +			break;
> +		default:
> +			regmap_write(sdev->regmap, MIPI_CSI2_VCDT0_REG,
> +				     MIPI_CSI2_VCDT0_REG_DEFAULT);
> +			break;
> +		}
> +	}
> +	mdelay(10);

Why do you need an mdelay here?

> +
> +}
> +
> +int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi)
> +{
> +	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> +	int ret;
> +
> +	ret = clk_prepare_enable(sdev->clk_mipi);
> +	if (ret) {
> +		dev_err(sdev->dev, "Enable clk_mipi clk err %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(sdev->clk_misc);
> +	if (ret) {
> +		dev_err(sdev->dev, "Enable clk_misc clk err %d\n", ret);
> +		goto clk_mipi_disable;
> +	}
> +
> +	return 0;
> +
> +clk_mipi_disable:
> +	clk_disable_unprepare(sdev->clk_mipi);
> +	return ret;
> +}
> +
> +void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi)
> +{
> +	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> +
> +	clk_disable_unprepare(sdev->clk_misc);
> +	clk_disable_unprepare(sdev->clk_mipi);
> +}
> +
> +
> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
> new file mode 100644
> index 000000000000..a94c69ccee39
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright Kévin L'hôpital (C) 2020
> + */
> +
> +#ifndef __SUN8I_A83T_MIPI_CSI2_H__
> +#define __SUN8I_A83T_MIPI_CSI2_H__
> +#include <linux/regmap.h>
> +#include "sun6i_csi.h"
> +
> +void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable);
> +void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi);
> +int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi);
> +void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi);
> +
> +#endif /* __SUN8I_A83T_MIPI_CSI2_H__ */
> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
> new file mode 100644
> index 000000000000..4d6fde3e50ef
> --- /dev/null
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
> @@ -0,0 +1,42 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Allwinner A83t MIPI CSI register description
> + * Copyright Kévin L'hôpital (C) 2020
> + */
> +
> +#ifndef __SUN8I_A83T_MIPI_CSI2_REG_H__
> +#define __SUN8I_A83T_MIPI_CSI2_REG_H__
> +
> +
> +#define MIPI_CSI2_OFFSET			0x1000
> +#define MIPI_CSI2_CTRL_REG			(MIPI_CSI2_OFFSET + 0x004)
> +#define MIPI_CSI2_RX_PKT_NUM_REG		(MIPI_CSI2_OFFSET + 0x008)
> +#define MIPI_CSI2_RSVD1_REG			(MIPI_CSI2_OFFSET + 0x018)
> +#define HW_LOCK_REGISTER_VALUE_1		0xb8c8a30c
> +#define MIPI_CSI2_RSVD2_REG			(MIPI_CSI2_OFFSET + 0x01c)
> +#define HW_LOCK_REGISTER_VALUE_2		0xb8df8ad7

We should have defines for those, or at least where they are coming from

> +#define MIPI_CSI2_CFG_REG			(MIPI_CSI2_OFFSET + 0x100)
> +#define MIPI_CSI2_CFG_REG_SYNC_EN		BIT(31)
> +#define MIPI_CSI2_CFG_REG_N_LANE_SHIFT		4
> +#define MIPI_CSI2_CFG_REG_N_LANE_MASK		0x30

GENMASK would be great here (and everywhere below too)

Maxime

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

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

* Re: [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera
  2020-08-21 14:59 ` [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera Kévin L'hôpital
@ 2020-08-25 14:40   ` Maxime Ripard
  2020-08-26  8:58     ` Kévin L'hôpital
  0 siblings, 1 reply; 24+ messages in thread
From: Maxime Ripard @ 2020-08-25 14:40 UTC (permalink / raw)
  To: Kévin L'hôpital
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Hi,

On Fri, Aug 21, 2020 at 04:59:35PM +0200, Kévin L'hôpital wrote:
> The Bananapi M3 supports a camera module which includes an
> OV8865 sensor connected via the parallel CSI interface and
> an OV8865 sensor connected via MIPI CSI-2.
> 
> The I2C2 bus is shared by the two sensors as well as active-low
> reset signal but each sensor has it own shutdown line.
> 
> The I2c address for the OV8865 is 0x36.
> 
> The bus type is hardcoded to 4 due to the lack of available
> define usable in the device-tree.
> 
> Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
>
> ---
>  arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts | 99 ++++++++++++++++++++
>  1 file changed, 99 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts
> index 9d34eabba121..f7839094695e 100644
> --- a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts
> +++ b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts
> @@ -85,6 +85,38 @@
>  		};
>  	};
>  
> +	reg_ov8865_avdd: ov8865-avdd {
> +		compatible = "regulator-fixed";
> +		regulator-name = "ov8865-avdd";
> +		regulator-min-microvolt = <2800000>;
> +		regulator-max-microvolt = <2800000>;
> +		vin-supply = <&reg_dldo4>;
> +	};
> +
> +	reg_ov8865_dovdd: ov8865-dovdd {
> +		compatible = "regulator-fixed";
> +		regulator-name = "ov8865-dovdd";
> +		regulator-min-microvolt = <2800000>;
> +		regulator-max-microvolt = <2800000>;
> +		vin-supply = <&reg_dldo4>;
> +	};
> +
> +	reg_ov8865_afvdd: ov8865-afvdd {
> +		compatible = "regulator-fixed";
> +		regulator-name = "ov8865-afvdd";
> +		regulator-min-microvolt = <2800000>;
> +		regulator-max-microvolt = <2800000>;
> +		vin-supply = <&reg_dldo4>;
> +	};
> +
> +	reg_ov8865_vdd2: ov8865-vdd2 {
> +		compatible = "regulator-fixed";
> +		regulator-name = "ov8865-vdd2";
> +		regulator-min-microvolt = <1200000>;
> +		regulator-max-microvolt = <1200000>;
> +		vin-supply = <&reg_eldo1>;
> +	};
> +
>  	reg_usb1_vbus: reg-usb1-vbus {
>  		compatible = "regulator-fixed";
>  		regulator-name = "usb1-vbus";
> @@ -115,10 +147,59 @@
>  	cpu-supply = <&reg_dcdc3>;
>  };
>  
> +&ccu {
> +	assigned-clocks = <&ccu CLK_CSI_MCLK>;
> +	assigned-clock-parents = <&osc24M>;
> +	assigned-clock-rates = <24000000>;
> +};

Why do you need to use assigned-clocks here?

> +&csi {
> +	pinctrl-names = "default";
> +	status = "okay";
> +};

pinctrl-names alone is useless

> +
> +&csi_in {
> +	mipi_csi2_from_ov8865: endpoint {
> +		remote-endpoint = <&ov8865_to_mipi_csi2>;
> +		clock-lanes = <0>;
> +		data-lanes = <1 2 3 4>;
> +		bus-type = <4>;
> +	};
> +};
> +
>  &de {
>  	status = "okay";
>  };
>  
> +&i2c2 {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&i2c2_pe_pins>;
> +	status = "okay";
> +
> +	ov8865: camera@36 {
> +		compatible = "ovti,ov8865";
> +		reg = <0x36>;
> +		clocks = <&ccu CLK_CSI_MCLK>;
> +		clock-names ="xclk";
> +		AVDD-supply = <&reg_ov8865_avdd>;
> +		DOVDD-supply = <&reg_ov8865_dovdd>;
> +		VDD2-supply = <&reg_ov8865_vdd2>;
> +		AFVDD-supply = <&reg_ov8865_afvdd>;
> +		powerdown-gpios = <&pio 4 17 GPIO_ACTIVE_LOW>; /* PE17 */
> +		reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */
> +		rotation = <180>;
> +
> +		port {
> +			ov8865_to_mipi_csi2: endpoint {
> +				remote-endpoint = <&mipi_csi2_from_ov8865>;
> +				data-lanes = <1 2 3 4>;
> +				clock-lanes = <0>;
> +				bus-type = <4>; /* V4L2_FWNODE_BUS_TYPE_CSI2_DPHY */
> +			};
> +		};
> +	};
> +};
> +
>  &ehci0 {
>  	/* Terminus Tech FE 1.1s 4-port USB 2.0 hub here */
>  	status = "okay";
> @@ -191,6 +272,11 @@
>  	status = "okay";
>  };
>  
> +&pio {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&csi_mclk_pin>;
> +};

I'm not sure why you'd need to use the MCLK pin as a hog, assigning it
to the camera device should be enough?

Maxime

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

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

* Re: [PATCH 2/7] dt-bindings: media: i2c: Add documentation for ov8865
  2020-08-24 16:59   ` Maxime Ripard
@ 2020-08-25 15:17     ` Kévin L'hôpital
  0 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-25 15:17 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Hello,

Le Mon, 24 Aug 2020 18:59:10 +0200,
Maxime Ripard <maxime@cerno.tech> a écrit :

> Hi,
> 
> On Fri, Aug 21, 2020 at 04:59:30PM +0200, Kévin L'hôpital wrote:
> > Add a documentation for the sensor ov8865 from Omnivision.
> > 
> > Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>  
> 
> In order to ease the submission of both drivers, you should probably
> split this series into two, one with the MIPI-CSI driver, and one with
> the ov8865 driver.
> 

Yes, you are right. I will do this.

> > ---
> >  .../devicetree/bindings/media/i2c/ov8865.txt  | 51
> > +++++++++++++++++++ 1 file changed, 51 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/media/i2c/ov8865.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/media/i2c/ov8865.txt
> > b/Documentation/devicetree/bindings/media/i2c/ov8865.txt new file
> > mode 100644 index 000000000000..ac5a662288de
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/media/i2c/ov8865.txt
> > @@ -0,0 +1,51 @@
> > +* Omnivision OV8865 MIPI CSI-2
> > +
> > +Required Properties:
> > +- compatible: should be "ovti,ov8865"
> > +- clocks: reference to the xclk input clock.
> > +- clock-names: should be "xclk".
> > +- DOVDD-supply: Digital I/O voltage supply, 2.8 volts
> > +- AVDD-supply: Analog voltage supply, 2.8 volts
> > +- AFVDD-supply: Analog voltage supply, 2.8 volts
> > +- DVDD-supply: Digital core voltage supply, 1.2 volts
> > +- reset-gpios: reference to the GPIO connected to the reset pin.
> > +	       This is an active low signal to the OV8865.
> > +- powerdown-gpios: reference to the GPIO connected to the
> > powerdown pin.
> > +		   This is an active low signal to the OV8865.
> > +- rotation: as defined in
> > +
> > Documentation/devicetree/bindings/media/video-interfaces.txt,
> > +	    valid values are 0 (sensor mounted upright) and 180
> > (sensor
> > +	    mounted upside down).
> > +- remote-endpoint: a phandle to the bus receiver's endpoint node.
> > +- clock-lanes: should be set to <0> (clock lane on hardware lane
> > 0). +- data-lanes: should be set to <4> (four CSI-2 lanes
> > supported). +
> > +The device node must contain one 'port' child node for its digital
> > output video +port, in accordance with the video interface bindings
> > defined in
> > +Documentation/devicetree/bindings/media/video-interfaces.txt.  
> 
> Free form DT documentation is deprecated nowadays, you should be
> doing a YAML schema instead (like the ov8856 driver).
> 

All right, I will do a YAML schema.

> Maxime

Thank you very much for the review.
Kévin


-- 
Kevin L'Hopital, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

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

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

* Re: [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera
  2020-08-24 16:52 ` [PATCH 0/7] Support of MIPI CSI-2 for A83T and " Maxime Ripard
@ 2020-08-25 15:20   ` Kévin L'hôpital
  0 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-25 15:20 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Le Mon, 24 Aug 2020 18:52:44 +0200,
Maxime Ripard <maxime@cerno.tech> a écrit :

> Hi,
> 
> On Fri, Aug 21, 2020 at 04:59:28PM +0200, Kévin L'hôpital wrote:
> > 
> > Kévin L'hôpital (7):
> >   media: sun6i-csi: Fix the bpp for 10-bit bayer formats
> >   dt-bindings: media: i2c: Add documentation for ov8865
> >   media: i2c: Add support for the OV8865 image sensor
> >   media: sunxi: sun6i-csi: Move the sun6i_csi_dev structure to the
> >     common header
> >   media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T
> >   ARM: dts: sun8i: a83t: Add support for the MIPI CSI-2 in CSI node
> >   [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865
> >     camera  
> 
> You should have a cover letter here to provide some context.
> 
> There's a bunch of things that would need to be explained and / or
> argued for here, in particular:
>   - Why did you need to plumb it into sun6i-csi?
>   - You're naming the CSI part as the A83t CSI, while MIPI-CSI has
> been supported since the A31(?), is there a reason for that?
>   - This is not documented anywhere, what did you base this work on?
> 
> Also, I think that documenting the general challenges you faced (which
> were likely because of the first bullet point above) and how you
> solved them here would be great to start a discussion if needed.
> 
> Finally, iirc, Hans requires a v4l2-compliance run for any new driver,
> which isn't strictly the case for this driver, but isn't really *not*
> the case either.
> 
> Maxime

Thank you very much for the review, I will add all this context.

Kévin


-- 
Kevin L'Hopital, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

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

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

* Re: [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats
  2020-08-24 16:55   ` Maxime Ripard
@ 2020-08-25 15:23     ` Kévin L'hôpital
  0 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-25 15:23 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Hello,

Le Mon, 24 Aug 2020 18:55:36 +0200,
Maxime Ripard <maxime@cerno.tech> a écrit :

> On Fri, Aug 21, 2020 at 04:59:29PM +0200, Kévin L'hôpital wrote:
> > 10-bit bayer formats are aligned to 16 bits in memory, so this is
> > what needs to be used as bpp for calculating the size of the
> > buffers to allocate.
> > 
> > Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>  
> 
> Generally speaking, you should also explain why it's not an issue for
> the callers. Depending on what that function is supposed to be doing
> (returning the padded bits or the padded bits per pixel), your patch
> could be either right or wrong.
> 
> Since all the callers are using it to generate the number of bytes per
> line, your patch is indeed correct. But it should be mentionned in the
> commit log.
> 
> Maxime

All right, I will add this explanation.

Thank you very much for the review

Kévin 

-- 
Kevin L'Hopital, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

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

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

* Re: [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats
  2020-08-21 14:59 ` [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats Kévin L'hôpital
  2020-08-24 16:55   ` Maxime Ripard
@ 2020-08-25 15:50   ` Chen-Yu Tsai
  2020-08-26  9:18     ` Kévin L'hôpital
  1 sibling, 1 reply; 24+ messages in thread
From: Chen-Yu Tsai @ 2020-08-25 15:50 UTC (permalink / raw)
  To: Kévin L'hôpital
  Cc: Mark Rutland, devicetree, Philipp Zabel, Thomas Petazzoni,
	linux-kernel, Maxime Ripard, Paul Kocialkowski, Rob Herring,
	Yong Deng, Mauro Carvalho Chehab, linux-arm-kernel,
	Linux Media Mailing List

On Fri, Aug 21, 2020 at 11:00 PM Kévin L'hôpital
<kevin.lhopital@bootlin.com> wrote:
>
> 10-bit bayer formats are aligned to 16 bits in memory, so this is what
> needs to be used as bpp for calculating the size of the buffers to
> allocate.
>
> Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>

Please add:

Fixes: 5cc7522d8965 ("media: sun6i: Add support for Allwinner CSI V3s")



> ---
>  drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
> index c626821aaedb..8b83d15de0d0 100644
> --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
> +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
> @@ -100,7 +100,7 @@ static inline int sun6i_csi_get_bpp(unsigned int pixformat)
>         case V4L2_PIX_FMT_SGBRG10:
>         case V4L2_PIX_FMT_SGRBG10:
>         case V4L2_PIX_FMT_SRGGB10:
> -               return 10;
> +               return 16;
>         case V4L2_PIX_FMT_SBGGR12:
>         case V4L2_PIX_FMT_SGBRG12:
>         case V4L2_PIX_FMT_SGRBG12:
> --
> 2.17.1
>

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

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

* Re: [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera
  2020-08-25 14:40   ` Maxime Ripard
@ 2020-08-26  8:58     ` Kévin L'hôpital
  2020-08-27 15:38       ` Maxime Ripard
  0 siblings, 1 reply; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-26  8:58 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Hello,

Le Tue, 25 Aug 2020 16:40:22 +0200,
Maxime Ripard <maxime@cerno.tech> a écrit :

> Hi,
> 
> On Fri, Aug 21, 2020 at 04:59:35PM +0200, Kévin L'hôpital wrote:
> > The Bananapi M3 supports a camera module which includes an
> > OV8865 sensor connected via the parallel CSI interface and
> > an OV8865 sensor connected via MIPI CSI-2.
> > 
> > The I2C2 bus is shared by the two sensors as well as active-low
> > reset signal but each sensor has it own shutdown line.
> > 
> > The I2c address for the OV8865 is 0x36.
> > 
> > The bus type is hardcoded to 4 due to the lack of available
> > define usable in the device-tree.
> > 
> > Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>
> >
> > ---
> >  arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts | 99
> > ++++++++++++++++++++ 1 file changed, 99 insertions(+)
> > 
> > diff --git a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts
> > b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts index
> > 9d34eabba121..f7839094695e 100644 ---
> > a/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts +++
> > b/arch/arm/boot/dts/sun8i-a83t-bananapi-m3.dts @@ -85,6 +85,38 @@
> >  		};
> >  	};
> >  
> > +	reg_ov8865_avdd: ov8865-avdd {
> > +		compatible = "regulator-fixed";
> > +		regulator-name = "ov8865-avdd";
> > +		regulator-min-microvolt = <2800000>;
> > +		regulator-max-microvolt = <2800000>;
> > +		vin-supply = <&reg_dldo4>;
> > +	};
> > +
> > +	reg_ov8865_dovdd: ov8865-dovdd {
> > +		compatible = "regulator-fixed";
> > +		regulator-name = "ov8865-dovdd";
> > +		regulator-min-microvolt = <2800000>;
> > +		regulator-max-microvolt = <2800000>;
> > +		vin-supply = <&reg_dldo4>;
> > +	};
> > +
> > +	reg_ov8865_afvdd: ov8865-afvdd {
> > +		compatible = "regulator-fixed";
> > +		regulator-name = "ov8865-afvdd";
> > +		regulator-min-microvolt = <2800000>;
> > +		regulator-max-microvolt = <2800000>;
> > +		vin-supply = <&reg_dldo4>;
> > +	};
> > +
> > +	reg_ov8865_vdd2: ov8865-vdd2 {
> > +		compatible = "regulator-fixed";
> > +		regulator-name = "ov8865-vdd2";
> > +		regulator-min-microvolt = <1200000>;
> > +		regulator-max-microvolt = <1200000>;
> > +		vin-supply = <&reg_eldo1>;
> > +	};
> > +
> >  	reg_usb1_vbus: reg-usb1-vbus {
> >  		compatible = "regulator-fixed";
> >  		regulator-name = "usb1-vbus";
> > @@ -115,10 +147,59 @@
> >  	cpu-supply = <&reg_dcdc3>;
> >  };
> >  
> > +&ccu {
> > +	assigned-clocks = <&ccu CLK_CSI_MCLK>;
> > +	assigned-clock-parents = <&osc24M>;
> > +	assigned-clock-rates = <24000000>;
> > +};  
> 
> Why do you need to use assigned-clocks here?

I could do it in the ov8865 node, does it sound good to you ?

> 
> > +&csi {
> > +	pinctrl-names = "default";
> > +	status = "okay";
> > +};  
> 
> pinctrl-names alone is useless
> 
> > +
> > +&csi_in {
> > +	mipi_csi2_from_ov8865: endpoint {
> > +		remote-endpoint = <&ov8865_to_mipi_csi2>;
> > +		clock-lanes = <0>;
> > +		data-lanes = <1 2 3 4>;
> > +		bus-type = <4>;
> > +	};
> > +};
> > +
> >  &de {
> >  	status = "okay";
> >  };
> >  
> > +&i2c2 {
> > +	pinctrl-names = "default";
> > +	pinctrl-0 = <&i2c2_pe_pins>;
> > +	status = "okay";
> > +
> > +	ov8865: camera@36 {
> > +		compatible = "ovti,ov8865";
> > +		reg = <0x36>;
> > +		clocks = <&ccu CLK_CSI_MCLK>;
> > +		clock-names ="xclk";
> > +		AVDD-supply = <&reg_ov8865_avdd>;
> > +		DOVDD-supply = <&reg_ov8865_dovdd>;
> > +		VDD2-supply = <&reg_ov8865_vdd2>;
> > +		AFVDD-supply = <&reg_ov8865_afvdd>;
> > +		powerdown-gpios = <&pio 4 17 GPIO_ACTIVE_LOW>; /*
> > PE17 */
> > +		reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16
> > */
> > +		rotation = <180>;
> > +
> > +		port {
> > +			ov8865_to_mipi_csi2: endpoint {
> > +				remote-endpoint =
> > <&mipi_csi2_from_ov8865>;
> > +				data-lanes = <1 2 3 4>;
> > +				clock-lanes = <0>;
> > +				bus-type = <4>; /*
> > V4L2_FWNODE_BUS_TYPE_CSI2_DPHY */
> > +			};
> > +		};
> > +	};
> > +};
> > +
> >  &ehci0 {
> >  	/* Terminus Tech FE 1.1s 4-port USB 2.0 hub here */
> >  	status = "okay";
> > @@ -191,6 +272,11 @@
> >  	status = "okay";
> >  };
> >  
> > +&pio {
> > +	pinctrl-names = "default";
> > +	pinctrl-0 = <&csi_mclk_pin>;
> > +};  
> 
> I'm not sure why you'd need to use the MCLK pin as a hog, assigning it
> to the camera device should be enough?

Yes you are right, I will put it in the ov8865 node.
> 
> Maxime

Thank you very much for the review.
Kévin


-- 
Kevin L'Hopital, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

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

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

* Re: [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T
  2020-08-25 14:37   ` Maxime Ripard
@ 2020-08-26  9:17     ` Kévin L'hôpital
  2020-08-27 15:41       ` Maxime Ripard
  0 siblings, 1 reply; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-26  9:17 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Hello,

Le Tue, 25 Aug 2020 16:37:04 +0200,
Maxime Ripard <maxime@cerno.tech> a écrit :

> Hi,
> 
> On Fri, Aug 21, 2020 at 04:59:33PM +0200, Kévin L'hôpital wrote:
> > This patch add the support only for the Allwinner A83T MIPI CSI2
> > 
> > Currently, the driver is not supported the other Allwinner V3's
> > MIPI CSI2
> > 
> > It has been tested with the ov8865 image sensor.
> > 
> > Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>  
> 
> Explaining how it's different from the v3s would help
> 
> > ---
> >  .../media/platform/sunxi/sun6i-csi/Makefile   |   2 +-
> >  .../platform/sunxi/sun6i-csi/sun6i_csi.c      |  82 ++++--
> >  .../sunxi/sun6i-csi/sun8i_a83t_dphy.c         |  20 ++
> >  .../sunxi/sun6i-csi/sun8i_a83t_dphy.h         |  16 ++
> >  .../sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h     |  15 ++
> >  .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c    | 249
> > ++++++++++++++++++ .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h    |
> > 16 ++ .../sun6i-csi/sun8i_a83t_mipi_csi2_reg.h      |  42 +++
> >  8 files changed, 425 insertions(+), 17 deletions(-)
> >  create mode 100644
> > drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c create
> > mode 100644
> > drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h create
> > mode 100644
> > drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h create
> > mode 100644
> > drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
> > create mode 100644
> > drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
> > create mode 100644
> > drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
> > 
> > diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile
> > b/drivers/media/platform/sunxi/sun6i-csi/Makefile index
> > e7e315347804..0f3849790463 100644 ---
> > a/drivers/media/platform/sunxi/sun6i-csi/Makefile +++
> > b/drivers/media/platform/sunxi/sun6i-csi/Makefile @@ -1,4 +1,4 @@
> >  # SPDX-License-Identifier: GPL-2.0-only
> > -sun6i-csi-y += sun6i_video.o sun6i_csi.o
> > +sun6i-csi-y += sun6i_video.o sun6i_csi.o sun8i_a83t_mipi_csi2.o
> > sun8i_a83t_dphy.o 
> >  obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o
> > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
> > b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index
> > 680fa31f380a..37aec0b57a46 100644 ---
> > a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++
> > b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -26,6 +26,7
> > @@ 
> >  #include "sun6i_csi.h"
> >  #include "sun6i_csi_reg.h"
> > +#include "sun8i_a83t_mipi_csi2.h"
> >  
> >  #define MODULE_NAME	"sun6i-csi"
> >  
> > @@ -40,6 +41,18 @@ bool sun6i_csi_is_format_supported(struct
> > sun6i_csi *csi, {
> >  	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> >  
> > +	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
> > +		if (!sdev->clk_mipi) {
> > +			dev_err(sdev->dev, "Use MIPI-CSI2 device
> > with no MIPI clock\n");
> > +			return false;
> > +		}
> > +		if (!sdev->clk_misc) {
> > +			dev_err(sdev->dev, "Use MIPI-CSI2 device
> > with no misc clock\n");
> > +			return false;
> > +		}
> > +		return true;
> > +	}
> > +  
> 
> I'm not sure we need to check for that everywhere, just doing so in
> the fwnode parsing function sohuld be enough.
> 

All right, I will do it in the fwnode function.

> >  	/*
> >  	 * Some video receivers have the ability to be compatible
> > with
> >  	 * 8bit and 16bit bus width.
> > @@ -160,10 +173,14 @@ int sun6i_csi_set_power(struct sun6i_csi
> > *csi, bool enable) regmap_update_bits(regmap, CSI_EN_REG,
> > CSI_EN_CSI_EN, 0); 
> >  		clk_disable_unprepare(sdev->clk_ram);
> > +
> >  		if (of_device_is_compatible(dev->of_node,
> >  					    "allwinner,sun50i-a64-csi"))
> >  			clk_rate_exclusive_put(sdev->clk_mod);
> >  		clk_disable_unprepare(sdev->clk_mod);
> > +		if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
> > +			sun6i_mipi_csi_clk_disable(csi);
> > +
> >  		reset_control_assert(sdev->rstc_bus);
> >  		return 0;
> >  	}
> > @@ -189,10 +206,18 @@ int sun6i_csi_set_power(struct sun6i_csi
> > *csi, bool enable) goto clk_ram_disable;
> >  	}
> >  
> > +	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
> > +		ret = sun6i_mipi_csi_clk_enable(csi);
> > +		if (ret)
> > +			goto reset_control_assert;
> > +	}
> > +
> >  	regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN,
> > CSI_EN_CSI_EN); 
> >  	return 0;
> >  
> > +reset_control_assert:
> > +	reset_control_assert(sdev->rstc_bus);
> >  clk_ram_disable:
> >  	clk_disable_unprepare(sdev->clk_ram);
> >  clk_mod_disable:
> > @@ -421,27 +446,33 @@ static void sun6i_csi_setup_bus(struct
> > sun6i_csi_dev *sdev) if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
> >  			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
> >  		break;
> > +	case V4L2_MBUS_CSI2_DPHY:
> > +		cfg |= CSI_IF_CFG_MIPI_IF_MIPI;
> > +		sun6i_mipi_csi_setup_bus(csi);
> > +		break;
> >  	default:
> >  		dev_warn(sdev->dev, "Unsupported bus type: %d\n",
> >  			 endpoint->bus_type);
> >  		break;
> >  	}
> >  
> > -	switch (bus_width) {
> > -	case 8:
> > -		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
> > -		break;
> > -	case 10:
> > -		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
> > -		break;
> > -	case 12:
> > -		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
> > -		break;
> > -	case 16: /* No need to configure DATA_WIDTH for 16bit */
> > -		break;
> > -	default:
> > -		dev_warn(sdev->dev, "Unsupported bus width: %u\n",
> > bus_width);
> > -		break;
> > +	if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) {
> > +		switch (bus_width) {
> > +		case 8:
> > +			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
> > +			break;
> > +		case 10:
> > +			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
> > +			break;
> > +		case 12:
> > +			cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
> > +			break;
> > +		case 16: /* No need to configure DATA_WIDTH for
> > 16bit */
> > +			break;
> > +		default:
> > +			dev_warn(sdev->dev, "Unsupported bus
> > width: %u\n", bus_width);
> > +			break;
> > +		}
> >  	}  
> 
> I'm not sure why we need to do some setup in both cases, and some only
> in the MIPI-CSI case here, can you clarify that a bit?

Yes I will add a comment to explain it.
> 
> >  
> >  	regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
> > @@ -593,6 +624,9 @@ void sun6i_csi_set_stream(struct sun6i_csi
> > *csi, bool enable) struct regmap *regmap = sdev->regmap;
> >  
> >  	if (!enable) {
> > +		if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
> > +			sun6i_mipi_csi_set_stream(csi, 0);
> > +
> >  		regmap_update_bits(regmap, CSI_CAP_REG,
> > CSI_CAP_CH0_VCAP_ON, 0); regmap_write(regmap, CSI_CH_INT_EN_REG, 0);
> >  		return;
> > @@ -609,6 +643,9 @@ void sun6i_csi_set_stream(struct sun6i_csi
> > *csi, bool enable) 
> >  	regmap_update_bits(regmap, CSI_CAP_REG,
> > CSI_CAP_CH0_VCAP_ON, CSI_CAP_CH0_VCAP_ON);
> > +
> > +	if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY)
> > +		sun6i_mipi_csi_set_stream(csi, 1);
> >  }
> >  
> >  /*
> > -----------------------------------------------------------------------------
> > @@ -692,6 +729,7 @@ static int sun6i_csi_fwnode_parse(struct device
> > *dev, } 
> >  	switch (vep->bus_type) {
> > +	case V4L2_MBUS_CSI2_DPHY:
> >  	case V4L2_MBUS_PARALLEL:
> >  	case V4L2_MBUS_BT656:
> >  		csi->v4l2_ep = *vep;
> > @@ -812,7 +850,7 @@ static const struct regmap_config
> > sun6i_csi_regmap_config = { .reg_bits       = 32,
> >  	.reg_stride     = 4,
> >  	.val_bits       = 32,
> > -	.max_register	= 0x9c,
> > +	.max_register	= 0x2000,
> >  };
> >  
> >  static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
> > @@ -847,6 +885,18 @@ static int sun6i_csi_resource_request(struct
> > sun6i_csi_dev *sdev, return PTR_ERR(sdev->clk_ram);
> >  	}
> >  
> > +	sdev->clk_mipi = devm_clk_get(&pdev->dev, "mipi");
> > +	if (IS_ERR(sdev->clk_mipi)) {
> > +		sdev->clk_mipi = NULL;
> > +		dev_warn(&pdev->dev, "Unable to acquire mipi
> > clock. No mipi support\n");
> > +	}
> > +
> > +	sdev->clk_misc = devm_clk_get(&pdev->dev, "misc");
> > +	if (IS_ERR(sdev->clk_misc)) {
> > +		sdev->clk_misc = NULL;
> > +		dev_warn(&pdev->dev, "Unable to acquire misc
> > clock. No mipi support\n");
> > +	}
> > +  
> 
> This will raise an error on platforms that use that driver for CSI but
> don't have MIPI-CSI (like the H3 IIRC?), this isn't really ok.
> 
> I guess we could have a per-soc flag where you'd say if MIPI-CSI is
> supported and only try to get the clock on the relevant SoCs.
> 
> Also, you're changing the DT binding, the documentation should be
> updated.
> 

Yes you are right, I will add this.

> >  	sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev,
> > NULL); if (IS_ERR(sdev->rstc_bus)) {
> >  		dev_err(&pdev->dev, "Cannot get reset
> > controller\n"); diff --git
> > a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
> > b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c new file
> > mode 100644 index 000000000000..3f5e4395aaa5 --- /dev/null
> > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c
> > @@ -0,0 +1,20 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * sun6i_dphy.c
> > + * Copyright Kévin L'hôpital (C) 2020
> > + */
> > +
> > +#include "sun8i_a83t_dphy.h"
> > +#include "sun8i_a83t_dphy_reg.h"
> > +
> > +void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev)
> > +{
> > +	regmap_write(sdev->regmap, DPHY_CTRL_REG, 0xb8df698e);
> > +}
> > +
> > +void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev)
> > +{
> > +	regmap_write(sdev->regmap, DPHY_CTRL_REG, 0x80008000);
> > +	regmap_write(sdev->regmap, DPHY_ANA0_REG, 0xa0200000);
> > +}  
> 
> Some documentation/comment on what that first init and second init is,
> and what those magic values are doing would be great here.
> 

Yes I will add some comments.

> > diff --git
> > a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
> > b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h new file
> > mode 100644 index 000000000000..f776ed098cb3 --- /dev/null
> > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * sun6i_dphy.h
> > + * Copyright Kévin L'hôpital (C) 2020
> > + */
> > +
> > +#ifndef __SUN8I_A83T_DPHY_H__
> > +#define __SUN8I_A83T_DPHY_H__
> > +
> > +#include <linux/regmap.h>
> > +#include "sun6i_csi.h"
> > +
> > +void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev);
> > +void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev);
> > +
> > +#endif /* __SUN8I_A83T_DPHY_H__ */
> > diff --git
> > a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
> > b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h new
> > file mode 100644 index 000000000000..c88824e4ec2e --- /dev/null
> > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h
> > @@ -0,0 +1,15 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Allwinner A83t DPHY register description
> > + * Copyright Kévin L'hôpital (C) 2020
> > + */
> > +
> > +#ifndef __SUN8I_A83T_DPHY_REG_H__
> > +#define __SUN8I_A83T_DPHY_REG_H__
> > +
> > +
> > +#define DPHY_OFFSET			0x1000
> > +#define DPHY_CTRL_REG			(DPHY_OFFSET + 0x010)
> > +#define DPHY_ANA0_REG			(DPHY_OFFSET + 0x030)
> > +
> > +#endif /* __SUN8I_A83T_DPHY_REG_H__ */  
> 
> Ideally this should be a D-PHY driver under drivers/phy
> 
> > diff --git
> > a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
> > b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c new
> > file mode 100644 index 000000000000..3f117e8d447f --- /dev/null
> > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c
> > @@ -0,0 +1,249 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Allwinner A83t MIPI Camera Sensor Interface driver
> > + * Copyright Kévin L'hôpital (C) 2020
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include "sun8i_a83t_mipi_csi2.h"
> > +#include "sun8i_a83t_mipi_csi2_reg.h"
> > +#include "sun8i_a83t_dphy.h"
> > +#include <linux/delay.h>
> > +
> > +#define IS_FLAG(x, y) (((x) & (y)) == y)
> > +
> > +enum mipi_csi2_pkt_fmt {
> > +	MIPI_FS           = 0X00,
> > +	MIPI_FE           = 0X01,
> > +	MIPI_LS           = 0X02,
> > +	MIPI_LE           = 0X03,
> > +	MIPI_SDAT0          = 0X08,
> > +	MIPI_SDAT1          = 0X09,
> > +	MIPI_SDAT2          = 0X0A,
> > +	MIPI_SDAT3          = 0X0B,
> > +	MIPI_SDAT4          = 0X0C,
> > +	MIPI_SDAT5          = 0X0D,
> > +	MIPI_SDAT6          = 0X0E,
> > +	MIPI_SDAT7          = 0X0F,
> > +	MIPI_BLK            = 0X11,
> > +	MIPI_EMBD         = 0X12,
> > +	MIPI_YUV420       = 0X18,
> > +	MIPI_YUV420_10    = 0X19,
> > +	MIPI_YUV420_CSP   = 0X1C,
> > +	MIPI_YUV420_CSP_10 =  0X1D,
> > +	MIPI_YUV422       = 0X1E,
> > +	MIPI_YUV422_10    = 0X1F,
> > +	MIPI_RGB565       = 0X22,
> > +	MIPI_RGB888       = 0X24,
> > +	MIPI_RAW8         = 0X2A,
> > +	MIPI_RAW10          = 0X2B,
> > +	MIPI_RAW12          = 0X2C,
> > +	MIPI_USR_DAT0     = 0X30,
> > +	MIPI_USR_DAT1     = 0X31,
> > +	MIPI_USR_DAT2     = 0X32,
> > +	MIPI_USR_DAT3     = 0X33,
> > +	MIPI_USR_DAT4     = 0X34,
> > +	MIPI_USR_DAT5     = 0X35,
> > +	MIPI_USR_DAT6     = 0X36,
> > +	MIPI_USR_DAT7     = 0X37,
> > +};
> > +
> > +static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct
> > sun6i_csi *csi) +{
> > +	return container_of(csi, struct sun6i_csi_dev, csi);
> > +}
> > +
> > +static enum mipi_csi2_pkt_fmt get_pkt_fmt(u16 bus_pix_code)
> > +{
> > +	switch (bus_pix_code) {
> > +	case MEDIA_BUS_FMT_RGB565_1X16:
> > +		return MIPI_RGB565;
> > +	case MEDIA_BUS_FMT_UYVY8_2X8:
> > +	case MEDIA_BUS_FMT_UYVY8_1X16:
> > +		return MIPI_YUV422;
> > +	case MEDIA_BUS_FMT_UYVY10_2X10:
> > +		return MIPI_YUV422_10;
> > +	case MEDIA_BUS_FMT_RGB888_1X24:
> > +		return MIPI_RGB888;
> > +	case MEDIA_BUS_FMT_SBGGR8_1X8:
> > +	case MEDIA_BUS_FMT_SGBRG8_1X8:
> > +	case MEDIA_BUS_FMT_SGRBG8_1X8:
> > +	case MEDIA_BUS_FMT_SRGGB8_1X8:
> > +		return MIPI_RAW8;
> > +	case MEDIA_BUS_FMT_SBGGR10_1X10:
> > +	case MEDIA_BUS_FMT_SGBRG10_1X10:
> > +	case MEDIA_BUS_FMT_SGRBG10_1X10:
> > +	case MEDIA_BUS_FMT_SRGGB10_1X10:
> > +		return MIPI_RAW10;
> > +	case MEDIA_BUS_FMT_SBGGR12_1X12:
> > +	case MEDIA_BUS_FMT_SGBRG12_1X12:
> > +	case MEDIA_BUS_FMT_SGRBG12_1X12:
> > +	case MEDIA_BUS_FMT_SRGGB12_1X12:
> > +		return MIPI_RAW12;
> > +	default:
> > +		return MIPI_RAW8;
> > +	}
> > +}
> > +
> > +  
> 
> There's an extra new line here
> 
> > +void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable)
> > +{
> > +	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> > +
> > +	if (enable)
> > +		regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
> > +				  MIPI_CSI2_CFG_REG_SYNC_EN,
> > +				  MIPI_CSI2_CFG_REG_SYNC_EN);
> > +	else
> > +		regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
> > +				  MIPI_CSI2_CFG_REG_SYNC_EN, 0);  
> 
> Do you really need regmap_write_bits here, or is regmap_update_bits
> enough?

Yes I could use regmap_update_bits.
> 
> > +
> > +}
> > +
> > +void sun6i_mipi_csi_init(struct sun6i_csi_dev *sdev)
> > +{
> > +	regmap_write(sdev->regmap, MIPI_CSI2_CTRL_REG, 0xb8c39bec);
> > +	regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG,
> > 0xb8d257f8);
> > +	sun6i_dphy_first_init(sdev);
> > +	regmap_write(sdev->regmap, MIPI_CSI2_RSVD1_REG,
> > +		     HW_LOCK_REGISTER_VALUE_1);
> > +	regmap_write(sdev->regmap, MIPI_CSI2_RSVD2_REG,
> > +		     HW_LOCK_REGISTER_VALUE_2);
> > +	regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0);
> > +	regmap_write(sdev->regmap, MIPI_CSI2_VCDT0_REG, 0);
> > +	regmap_write(sdev->regmap, MIPI_CSI2_CFG_REG, 0xb8c64f24);
> > +	sun6i_dphy_second_init(sdev);
> > +	regmap_write(sdev->regmap, MIPI_CSI2_CTRL_REG, 0x80000000);
> > +	regmap_write(sdev->regmap, MIPI_CSI2_CFG_REG,
> > 0x12200000);  
> 
> Again, some defines / comments would be great here.
> 
> > +}
> > +
> > +void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi)
> > +{
> > +	struct v4l2_fwnode_endpoint *endpoint = &csi->v4l2_ep;
> > +	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> > +	int lane_num = endpoint->bus.mipi_csi2.num_data_lanes;
> > +	int flags = endpoint->bus.mipi_csi2.flags;
> > +	int total_rx_ch = 0;
> > +	int vc[4];
> > +	int ch;
> > +
> > +	sun6i_mipi_csi_init(sdev);
> > +
> > +	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_0)) {
> > +		vc[total_rx_ch] = 0;
> > +		total_rx_ch++;
> > +	}
> > +
> > +	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_1)) {
> > +		vc[total_rx_ch] = 1;
> > +		total_rx_ch++;
> > +	}
> > +
> > +	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_2)) {
> > +		vc[total_rx_ch] = 2;
> > +		total_rx_ch++;
> > +	}
> > +
> > +	if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_3)) {
> > +		vc[total_rx_ch] = 3;
> > +		total_rx_ch++;
> > +	}
> > +  
> 
> Is it supposed to handle multiple virtual channels at once? If so, how
> do you get the results of each virtual channel?
> 

Yes you are rigth, implement just one virtual channel makes more sens.

> > +	if (!total_rx_ch) {
> > +		dev_dbg(sdev->dev,
> > +			 "No receive channel assigned, using
> > channel 0.\n");
> > +		vc[total_rx_ch] = 0;
> > +		total_rx_ch++;
> > +	}
> > +	/* Set lane. */
> > +	regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
> > +			  MIPI_CSI2_CFG_REG_N_LANE_MASK, (lane_num
> > - 1) <<
> > +			  MIPI_CSI2_CFG_REG_N_LANE_SHIFT);
> > +	/* Set total channels. */
> > +	regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG,
> > +			  MIPI_CSI2_CFG_REG_N_CHANNEL_MASK,
> > (total_rx_ch - 1) <<
> > +			  MIPI_CSI2_CFG_REG_N_CHANNEL_SHIFT);
> > +
> > +	for (ch = 0; ch < total_rx_ch; ch++) {
> > +		switch (ch) {
> > +		case 0:
> > +			regmap_write_bits(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +
> > MIPI_CSI2_VCDT0_REG_CH0_DT_MASK,
> > +
> > get_pkt_fmt(csi->config.code));
> > +			regmap_write_bits(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +
> > MIPI_CSI2_VCDT0_REG_CH0_VC_MASK,
> > +					  vc[ch] <<
> > MIPI_CSI2_VCDT0_REG_CH0_VC_SHIFT);
> > +			break;
> > +		case 1:
> > +			regmap_write_bits(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +
> > MIPI_CSI2_VCDT0_REG_CH1_DT_MASK,
> > +
> > get_pkt_fmt(csi->config.code)
> > +					  <<
> > +
> > MIPI_CSI2_VCDT0_REG_CH1_DT_SHIFT);
> > +			regmap_write_bits(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +
> > MIPI_CSI2_VCDT0_REG_CH1_VC_MASK,
> > +					  vc[ch] <<
> > MIPI_CSI2_VCDT0_REG_CH1_VC_SHIFT);
> > +			break;
> > +		case 2:
> > +			regmap_write_bits(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +
> > MIPI_CSI2_VCDT0_REG_CH2_DT_MASK,
> > +
> > get_pkt_fmt(csi->config.code)
> > +					  <<
> > +
> > MIPI_CSI2_VCDT0_REG_CH2_DT_SHIFT);
> > +			regmap_write_bits(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +
> > MIPI_CSI2_VCDT0_REG_CH2_VC_MASK,
> > +					  vc[ch] <<
> > MIPI_CSI2_VCDT0_REG_CH2_VC_SHIFT);
> > +			break;
> > +		case 3:
> > +			regmap_write_bits(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +
> > MIPI_CSI2_VCDT0_REG_CH3_DT_MASK,
> > +
> > get_pkt_fmt(csi->config.code)
> > +					  <<
> > +
> > MIPI_CSI2_VCDT0_REG_CH3_DT_SHIFT);
> > +			regmap_write_bits(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +
> > MIPI_CSI2_VCDT0_REG_CH3_VC_MASK,
> > +					  vc[ch] <<
> > MIPI_CSI2_VCDT0_REG_CH3_VC_SHIFT);
> > +			break;
> > +		default:
> > +			regmap_write(sdev->regmap,
> > MIPI_CSI2_VCDT0_REG,
> > +				     MIPI_CSI2_VCDT0_REG_DEFAULT);
> > +			break;
> > +		}
> > +	}
> > +	mdelay(10);  
> 
> Why do you need an mdelay here?

yes a msleep could be more correct here.

> 
> > +
> > +}
> > +
> > +int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi)
> > +{
> > +	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> > +	int ret;
> > +
> > +	ret = clk_prepare_enable(sdev->clk_mipi);
> > +	if (ret) {
> > +		dev_err(sdev->dev, "Enable clk_mipi clk err %d\n",
> > ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = clk_prepare_enable(sdev->clk_misc);
> > +	if (ret) {
> > +		dev_err(sdev->dev, "Enable clk_misc clk err %d\n",
> > ret);
> > +		goto clk_mipi_disable;
> > +	}
> > +
> > +	return 0;
> > +
> > +clk_mipi_disable:
> > +	clk_disable_unprepare(sdev->clk_mipi);
> > +	return ret;
> > +}
> > +
> > +void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi)
> > +{
> > +	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
> > +
> > +	clk_disable_unprepare(sdev->clk_misc);
> > +	clk_disable_unprepare(sdev->clk_mipi);
> > +}
> > +
> > +
> > diff --git
> > a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
> > b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h new
> > file mode 100644 index 000000000000..a94c69ccee39 --- /dev/null
> > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright Kévin L'hôpital (C) 2020
> > + */
> > +
> > +#ifndef __SUN8I_A83T_MIPI_CSI2_H__
> > +#define __SUN8I_A83T_MIPI_CSI2_H__
> > +#include <linux/regmap.h>
> > +#include "sun6i_csi.h"
> > +
> > +void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable);
> > +void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi);
> > +int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi);
> > +void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi);
> > +
> > +#endif /* __SUN8I_A83T_MIPI_CSI2_H__ */
> > diff --git
> > a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
> > b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
> > new file mode 100644 index 000000000000..4d6fde3e50ef --- /dev/null
> > +++
> > b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h
> > @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Allwinner A83t MIPI CSI register description
> > + * Copyright Kévin L'hôpital (C) 2020
> > + */
> > +
> > +#ifndef __SUN8I_A83T_MIPI_CSI2_REG_H__
> > +#define __SUN8I_A83T_MIPI_CSI2_REG_H__
> > +
> > +
> > +#define MIPI_CSI2_OFFSET			0x1000
> > +#define MIPI_CSI2_CTRL_REG
> > (MIPI_CSI2_OFFSET + 0x004) +#define
> > MIPI_CSI2_RX_PKT_NUM_REG		(MIPI_CSI2_OFFSET + 0x008)
> > +#define MIPI_CSI2_RSVD1_REG
> > (MIPI_CSI2_OFFSET + 0x018) +#define
> > HW_LOCK_REGISTER_VALUE_1		0xb8c8a30c +#define
> > MIPI_CSI2_RSVD2_REG			(MIPI_CSI2_OFFSET +
> > 0x01c) +#define HW_LOCK_REGISTER_VALUE_2		0xb8df8ad7  
> 
> We should have defines for those, or at least where they are coming
> from
> 
> > +#define MIPI_CSI2_CFG_REG			(MIPI_CSI2_OFFSET
> > + 0x100) +#define MIPI_CSI2_CFG_REG_SYNC_EN		BIT(31)
> > +#define MIPI_CSI2_CFG_REG_N_LANE_SHIFT		4
> > +#define MIPI_CSI2_CFG_REG_N_LANE_MASK		0x30  
> 
> GENMASK would be great here (and everywhere below too)
> 
> Maxime

Thank you very much for the review.
Kévin

-- 
Kevin L'Hopital, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

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

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

* Re: [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats
  2020-08-25 15:50   ` Chen-Yu Tsai
@ 2020-08-26  9:18     ` Kévin L'hôpital
  0 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-26  9:18 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Mark Rutland, devicetree, Philipp Zabel, Thomas Petazzoni,
	linux-kernel, Maxime Ripard, Paul Kocialkowski, Rob Herring,
	Yong Deng, Mauro Carvalho Chehab, linux-arm-kernel,
	Linux Media Mailing List

Hello,

Le Tue, 25 Aug 2020 23:50:30 +0800,
Chen-Yu Tsai <wens@csie.org> a écrit :

> On Fri, Aug 21, 2020 at 11:00 PM Kévin L'hôpital
> <kevin.lhopital@bootlin.com> wrote:
> >
> > 10-bit bayer formats are aligned to 16 bits in memory, so this is
> > what needs to be used as bpp for calculating the size of the
> > buffers to allocate.
> >
> > Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com>  
> 
> Please add:
> 
> Fixes: 5cc7522d8965 ("media: sun6i: Add support for Allwinner CSI
> V3s")
> 
> 
I will add this, thank you very much for the review.
Kévin
> 
> > ---
> >  drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
> > b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h index
> > c626821aaedb..8b83d15de0d0 100644 ---
> > a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h +++
> > b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h @@ -100,7
> > +100,7 @@ static inline int sun6i_csi_get_bpp(unsigned int
> > pixformat) case V4L2_PIX_FMT_SGBRG10: case V4L2_PIX_FMT_SGRBG10:
> >         case V4L2_PIX_FMT_SRGGB10:
> > -               return 10;
> > +               return 16;
> >         case V4L2_PIX_FMT_SBGGR12:
> >         case V4L2_PIX_FMT_SGBRG12:
> >         case V4L2_PIX_FMT_SGRBG12:
> > --
> > 2.17.1
> >  



-- 
Kevin L'Hopital, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

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

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

* Re: [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera
  2020-08-26  8:58     ` Kévin L'hôpital
@ 2020-08-27 15:38       ` Maxime Ripard
  2020-08-28  7:31         ` Kévin L'hôpital
  0 siblings, 1 reply; 24+ messages in thread
From: Maxime Ripard @ 2020-08-27 15:38 UTC (permalink / raw)
  To: Kévin L'hôpital
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media


[-- Attachment #1.1: Type: text/plain, Size: 557 bytes --]

Hi,

On Wed, Aug 26, 2020 at 10:58:34AM +0200, Kévin L'hôpital wrote:
> > > +&ccu {
> > > +	assigned-clocks = <&ccu CLK_CSI_MCLK>;
> > > +	assigned-clock-parents = <&osc24M>;
> > > +	assigned-clock-rates = <24000000>;
> > > +};  
> > 
> > Why do you need to use assigned-clocks here?
> 
> I could do it in the ov8865 node, does it sound good to you ?

I mean, it depends on why you want to do it :)

If that's because the sensor expects a clock in a particular clock
range, then it should be enforced in the sensor driver itself.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

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

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

* Re: [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T
  2020-08-26  9:17     ` Kévin L'hôpital
@ 2020-08-27 15:41       ` Maxime Ripard
  2020-08-28  7:32         ` Kévin L'hôpital
  0 siblings, 1 reply; 24+ messages in thread
From: Maxime Ripard @ 2020-08-27 15:41 UTC (permalink / raw)
  To: Kévin L'hôpital
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media


[-- Attachment #1.1: Type: text/plain, Size: 318 bytes --]

On Wed, Aug 26, 2020 at 11:17:28AM +0200, Kévin L'hôpital wrote:
> > > +	mdelay(10);  
> > 
> > Why do you need an mdelay here?
> 
> yes a msleep could be more correct here.

My question was more about whether/why you need one in the first place,
not necessarily how you would implement that delay.

Maxime

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

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

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

* Re: [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera
  2020-08-27 15:38       ` Maxime Ripard
@ 2020-08-28  7:31         ` Kévin L'hôpital
  0 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-28  7:31 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Hello,
Le Thu, 27 Aug 2020 17:38:43 +0200,
Maxime Ripard <maxime@cerno.tech> a écrit :

> Hi,
> 
> On Wed, Aug 26, 2020 at 10:58:34AM +0200, Kévin L'hôpital wrote:
> > > > +&ccu {
> > > > +	assigned-clocks = <&ccu CLK_CSI_MCLK>;
> > > > +	assigned-clock-parents = <&osc24M>;
> > > > +	assigned-clock-rates = <24000000>;
> > > > +};    
> > > 
> > > Why do you need to use assigned-clocks here?  
> > 
> > I could do it in the ov8865 node, does it sound good to you ?  
> 
> I mean, it depends on why you want to do it :)
> 
> If that's because the sensor expects a clock in a particular clock
> range, then it should be enforced in the sensor driver itself.
> 
> Maxime

Yes I will put it in the sensor driver.
Thanks for the answer.
Kévin
-- 
Kevin L'Hopital, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

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

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

* Re: [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T
  2020-08-27 15:41       ` Maxime Ripard
@ 2020-08-28  7:32         ` Kévin L'hôpital
  0 siblings, 0 replies; 24+ messages in thread
From: Kévin L'hôpital @ 2020-08-28  7:32 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: mark.rutland, devicetree, p.zabel, thomas.petazzoni,
	linux-kernel, paul.kocialkowski, wens, robh+dt, yong.deng,
	mchehab, linux-arm-kernel, linux-media

Hello,
Le Thu, 27 Aug 2020 17:41:19 +0200,
Maxime Ripard <maxime@cerno.tech> a écrit :

> On Wed, Aug 26, 2020 at 11:17:28AM +0200, Kévin L'hôpital wrote:
> > > > +	mdelay(10);    
> > > 
> > > Why do you need an mdelay here?  
> > 
> > yes a msleep could be more correct here.  
> 
> My question was more about whether/why you need one in the first place,
> not necessarily how you would implement that delay.
> 
> Maxime

It was a mistake, I have removed it.
Thanks for the answer.
Kévin

-- 
Kevin L'Hopital, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com

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

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

end of thread, other threads:[~2020-08-28  7:34 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-21 14:59 [PATCH 0/7] Support of MIPI CSI-2 for A83T and OV8865 camera Kévin L'hôpital
2020-08-21 14:59 ` [PATCH 1/7] media: sun6i-csi: Fix the bpp for 10-bit bayer formats Kévin L'hôpital
2020-08-24 16:55   ` Maxime Ripard
2020-08-25 15:23     ` Kévin L'hôpital
2020-08-25 15:50   ` Chen-Yu Tsai
2020-08-26  9:18     ` Kévin L'hôpital
2020-08-21 14:59 ` [PATCH 2/7] dt-bindings: media: i2c: Add documentation for ov8865 Kévin L'hôpital
2020-08-24 16:59   ` Maxime Ripard
2020-08-25 15:17     ` Kévin L'hôpital
2020-08-21 14:59 ` [PATCH 3/7] media: i2c: Add support for the OV8865 image sensor Kévin L'hôpital
2020-08-21 14:59 ` [PATCH 4/7] media: sunxi: sun6i-csi: Move the sun6i_csi_dev structure to the common header Kévin L'hôpital
2020-08-21 14:59 ` [PATCH 5/7] media: sunxi: sun6i-csi: Add support of MIPI CSI-2 for A83T Kévin L'hôpital
2020-08-25 14:37   ` Maxime Ripard
2020-08-26  9:17     ` Kévin L'hôpital
2020-08-27 15:41       ` Maxime Ripard
2020-08-28  7:32         ` Kévin L'hôpital
2020-08-21 14:59 ` [PATCH 6/7] ARM: dts: sun8i: a83t: Add support for the MIPI CSI-2 in CSI node Kévin L'hôpital
2020-08-21 14:59 ` [PATCH 7/7] [NOT FOR MERGE] ARM: dts: sun8i: a83t: bananapi-m3: Enable OV8865 camera Kévin L'hôpital
2020-08-25 14:40   ` Maxime Ripard
2020-08-26  8:58     ` Kévin L'hôpital
2020-08-27 15:38       ` Maxime Ripard
2020-08-28  7:31         ` Kévin L'hôpital
2020-08-24 16:52 ` [PATCH 0/7] Support of MIPI CSI-2 for A83T and " Maxime Ripard
2020-08-25 15:20   ` Kévin L'hôpital

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).