xref: /openbmc/linux/drivers/media/i2c/ov8865.c (revision 6e1c9bc9)
111c0d8fdSPaul Kocialkowski // SPDX-License-Identifier: GPL-2.0-or-later
211c0d8fdSPaul Kocialkowski /*
311c0d8fdSPaul Kocialkowski  * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com>
411c0d8fdSPaul Kocialkowski  * Copyright 2020 Bootlin
511c0d8fdSPaul Kocialkowski  * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
611c0d8fdSPaul Kocialkowski  */
711c0d8fdSPaul Kocialkowski 
811c0d8fdSPaul Kocialkowski #include <linux/clk.h>
911c0d8fdSPaul Kocialkowski #include <linux/delay.h>
1011c0d8fdSPaul Kocialkowski #include <linux/device.h>
1111c0d8fdSPaul Kocialkowski #include <linux/i2c.h>
1211c0d8fdSPaul Kocialkowski #include <linux/module.h>
1311c0d8fdSPaul Kocialkowski #include <linux/of_graph.h>
1411c0d8fdSPaul Kocialkowski #include <linux/pm_runtime.h>
1511c0d8fdSPaul Kocialkowski #include <linux/regulator/consumer.h>
1611c0d8fdSPaul Kocialkowski #include <linux/videodev2.h>
1711c0d8fdSPaul Kocialkowski #include <media/v4l2-ctrls.h>
1811c0d8fdSPaul Kocialkowski #include <media/v4l2-device.h>
1911c0d8fdSPaul Kocialkowski #include <media/v4l2-fwnode.h>
2011c0d8fdSPaul Kocialkowski #include <media/v4l2-image-sizes.h>
2111c0d8fdSPaul Kocialkowski #include <media/v4l2-mediabus.h>
2211c0d8fdSPaul Kocialkowski 
2311c0d8fdSPaul Kocialkowski /* Clock rate */
2411c0d8fdSPaul Kocialkowski 
2511c0d8fdSPaul Kocialkowski #define OV8865_EXTCLK_RATE			24000000
2611c0d8fdSPaul Kocialkowski 
2711c0d8fdSPaul Kocialkowski /* Register definitions */
2811c0d8fdSPaul Kocialkowski 
2911c0d8fdSPaul Kocialkowski /* System */
3011c0d8fdSPaul Kocialkowski 
3111c0d8fdSPaul Kocialkowski #define OV8865_SW_STANDBY_REG			0x100
3211c0d8fdSPaul Kocialkowski #define OV8865_SW_STANDBY_STREAM_ON		BIT(0)
3311c0d8fdSPaul Kocialkowski 
3411c0d8fdSPaul Kocialkowski #define OV8865_SW_RESET_REG			0x103
3511c0d8fdSPaul Kocialkowski #define OV8865_SW_RESET_RESET			BIT(0)
3611c0d8fdSPaul Kocialkowski 
3711c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL0_REG			0x300
3811c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL0_PRE_DIV(v)		((v) & GENMASK(2, 0))
3911c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1_REG			0x301
4011c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1_MUL_H(v)		(((v) & GENMASK(9, 8)) >> 8)
4111c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL2_REG			0x302
4211c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL2_MUL_L(v)		((v) & GENMASK(7, 0))
4311c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL3_REG			0x303
4411c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL3_M_DIV(v)		(((v) - 1) & GENMASK(3, 0))
4511c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL4_REG			0x304
4611c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL4_MIPI_DIV(v)		((v) & GENMASK(1, 0))
4711c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL5_REG			0x305
4811c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL5_SYS_PRE_DIV(v)		((v) & GENMASK(1, 0))
4911c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL6_REG			0x306
5011c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL6_SYS_DIV(v)		(((v) - 1) & BIT(0))
5111c0d8fdSPaul Kocialkowski 
5211c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL8_REG			0x308
5311c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL9_REG			0x309
5411c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLA_REG			0x30a
5511c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLA_PRE_DIV_HALF(v)	(((v) - 1) & BIT(0))
5611c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLB_REG			0x30b
5711c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLB_PRE_DIV(v)		((v) & GENMASK(2, 0))
5811c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLC_REG			0x30c
5911c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLC_MUL_H(v)		(((v) & GENMASK(9, 8)) >> 8)
6011c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLD_REG			0x30d
6111c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLD_MUL_L(v)		((v) & GENMASK(7, 0))
6211c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLE_REG			0x30e
6311c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLE_SYS_DIV(v)		((v) & GENMASK(2, 0))
6411c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLF_REG			0x30f
6511c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLF_SYS_PRE_DIV(v)		(((v) - 1) & GENMASK(3, 0))
6611c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL10_REG			0x310
6711c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL11_REG			0x311
6811c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL12_REG			0x312
6911c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL12_PRE_DIV_HALF(v)	((((v) - 1) << 4) & BIT(4))
7011c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL12_DAC_DIV(v)		(((v) - 1) & GENMASK(3, 0))
7111c0d8fdSPaul Kocialkowski 
7211c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1B_REG			0x31b
7311c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1C_REG			0x31c
7411c0d8fdSPaul Kocialkowski 
7511c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1E_REG			0x31e
7611c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1E_PLL1_NO_LAT		BIT(3)
7711c0d8fdSPaul Kocialkowski 
7811c0d8fdSPaul Kocialkowski #define OV8865_PAD_OEN0_REG			0x3000
7911c0d8fdSPaul Kocialkowski 
8011c0d8fdSPaul Kocialkowski #define OV8865_PAD_OEN2_REG			0x3002
8111c0d8fdSPaul Kocialkowski 
8211c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST5_REG			0x3005
8311c0d8fdSPaul Kocialkowski 
8411c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_HH_REG			0x300a
8511c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_HH_VALUE			0x00
8611c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_H_REG			0x300b
8711c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_H_VALUE			0x88
8811c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_L_REG			0x300c
8911c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_L_VALUE			0x65
9011c0d8fdSPaul Kocialkowski #define OV8865_PAD_OUT2_REG			0x300d
9111c0d8fdSPaul Kocialkowski 
9211c0d8fdSPaul Kocialkowski #define OV8865_PAD_SEL2_REG			0x3010
9311c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_REG			0x3011
9411c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_DRIVE_STRENGTH_1X		(0 << 5)
9511c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_DRIVE_STRENGTH_2X		(1 << 5)
9611c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_DRIVE_STRENGTH_3X		(2 << 5)
9711c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_DRIVE_STRENGTH_4X		(3 << 5)
9811c0d8fdSPaul Kocialkowski 
9911c0d8fdSPaul Kocialkowski #define OV8865_PUMP_CLK_DIV_REG			0x3015
10011c0d8fdSPaul Kocialkowski #define OV8865_PUMP_CLK_DIV_PUMP_N(v)		(((v) << 4) & GENMASK(6, 4))
10111c0d8fdSPaul Kocialkowski #define OV8865_PUMP_CLK_DIV_PUMP_P(v)		((v) & GENMASK(2, 0))
10211c0d8fdSPaul Kocialkowski 
10311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_REG		0x3018
10411c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_LANES(v)		((((v) - 1) << 5) & \
10511c0d8fdSPaul Kocialkowski 						 GENMASK(7, 5))
10611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_MIPI_EN		BIT(4)
10711c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_UNKNOWN		BIT(1)
10811c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_LANES_PD_MIPI	BIT(0)
10911c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL1_REG		0x3019
11011c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST0_REG			0x301a
11111c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST1_REG			0x301b
11211c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST2_REG			0x301c
11311c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST3_REG			0x301d
11411c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST4_REG			0x301e
11511c0d8fdSPaul Kocialkowski 
11611c0d8fdSPaul Kocialkowski #define OV8865_PCLK_SEL_REG			0x3020
11711c0d8fdSPaul Kocialkowski #define OV8865_PCLK_SEL_PCLK_DIV_MASK		BIT(3)
11811c0d8fdSPaul Kocialkowski #define OV8865_PCLK_SEL_PCLK_DIV(v)		((((v) - 1) << 3) & BIT(3))
11911c0d8fdSPaul Kocialkowski 
12011c0d8fdSPaul Kocialkowski #define OV8865_MISC_CTRL_REG			0x3021
12111c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL2_REG		0x3022
12211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL2_CLK_LANES_PD_MIPI	BIT(1)
12311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC	BIT(0)
12411c0d8fdSPaul Kocialkowski 
12511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_BIT_SEL_REG			0x3031
12611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_BIT_SEL(v)			(((v) << 0) & GENMASK(4, 0))
12711c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL0_REG			0x3032
12811c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL0_PLL1_SYS_SEL(v)		(((v) << 7) & BIT(7))
12911c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_REG			0x3033
13011c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_MIPI_EOF		BIT(5)
13111c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_UNKNOWN			BIT(2)
13211c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK	BIT(1)
13311c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_PLL_SCLK_SEL(v)		(((v) << 1) & BIT(1))
13411c0d8fdSPaul Kocialkowski 
13511c0d8fdSPaul Kocialkowski #define OV8865_SCLK_CTRL_REG			0x3106
13611c0d8fdSPaul Kocialkowski #define OV8865_SCLK_CTRL_SCLK_DIV(v)		(((v) << 4) & GENMASK(7, 4))
13711c0d8fdSPaul Kocialkowski #define OV8865_SCLK_CTRL_SCLK_PRE_DIV(v)	(((v) << 2) & GENMASK(3, 2))
13811c0d8fdSPaul Kocialkowski #define OV8865_SCLK_CTRL_UNKNOWN		BIT(0)
13911c0d8fdSPaul Kocialkowski 
14011c0d8fdSPaul Kocialkowski /* Exposure/gain */
14111c0d8fdSPaul Kocialkowski 
14211c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_HH_REG		0x3500
14311c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_HH(v)		(((v) & GENMASK(19, 16)) >> 16)
14411c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_H_REG		0x3501
14511c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_H(v)		(((v) & GENMASK(15, 8)) >> 8)
14611c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_L_REG		0x3502
14711c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_L(v)		((v) & GENMASK(7, 0))
14811c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_GAIN_MANUAL_REG		0x3503
14911c0d8fdSPaul Kocialkowski 
15011c0d8fdSPaul Kocialkowski #define OV8865_GAIN_CTRL_H_REG			0x3508
15111c0d8fdSPaul Kocialkowski #define OV8865_GAIN_CTRL_H(v)			(((v) & GENMASK(12, 8)) >> 8)
15211c0d8fdSPaul Kocialkowski #define OV8865_GAIN_CTRL_L_REG			0x3509
15311c0d8fdSPaul Kocialkowski #define OV8865_GAIN_CTRL_L(v)			((v) & GENMASK(7, 0))
15411c0d8fdSPaul Kocialkowski 
15511c0d8fdSPaul Kocialkowski /* Timing */
15611c0d8fdSPaul Kocialkowski 
15711c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_X_H_REG		0x3800
15811c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_X_H(v)		(((v) & GENMASK(11, 8)) >> 8)
15911c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_X_L_REG		0x3801
16011c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_X_L(v)		((v) & GENMASK(7, 0))
16111c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_Y_H_REG		0x3802
16211c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_Y_H(v)		(((v) & GENMASK(11, 8)) >> 8)
16311c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_Y_L_REG		0x3803
16411c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_Y_L(v)		((v) & GENMASK(7, 0))
16511c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_X_H_REG			0x3804
16611c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_X_H(v)			(((v) & GENMASK(11, 8)) >> 8)
16711c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_X_L_REG			0x3805
16811c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_X_L(v)			((v) & GENMASK(7, 0))
16911c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_Y_H_REG			0x3806
17011c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_Y_H(v)			(((v) & GENMASK(11, 8)) >> 8)
17111c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_Y_L_REG			0x3807
17211c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_Y_L(v)			((v) & GENMASK(7, 0))
17311c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_X_H_REG		0x3808
17411c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_X_H(v)		(((v) & GENMASK(11, 8)) >> 8)
17511c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_X_L_REG		0x3809
17611c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_X_L(v)		((v) & GENMASK(7, 0))
17711c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_Y_H_REG		0x380a
17811c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_Y_H(v)		(((v) & GENMASK(11, 8)) >> 8)
17911c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_Y_L_REG		0x380b
18011c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_Y_L(v)		((v) & GENMASK(7, 0))
18111c0d8fdSPaul Kocialkowski #define OV8865_HTS_H_REG			0x380c
18211c0d8fdSPaul Kocialkowski #define OV8865_HTS_H(v)				(((v) & GENMASK(11, 8)) >> 8)
18311c0d8fdSPaul Kocialkowski #define OV8865_HTS_L_REG			0x380d
18411c0d8fdSPaul Kocialkowski #define OV8865_HTS_L(v)				((v) & GENMASK(7, 0))
18511c0d8fdSPaul Kocialkowski #define OV8865_VTS_H_REG			0x380e
18611c0d8fdSPaul Kocialkowski #define OV8865_VTS_H(v)				(((v) & GENMASK(11, 8)) >> 8)
18711c0d8fdSPaul Kocialkowski #define OV8865_VTS_L_REG			0x380f
18811c0d8fdSPaul Kocialkowski #define OV8865_VTS_L(v)				((v) & GENMASK(7, 0))
18911c0d8fdSPaul Kocialkowski #define OV8865_OFFSET_X_H_REG			0x3810
19011c0d8fdSPaul Kocialkowski #define OV8865_OFFSET_X_H(v)			(((v) & GENMASK(15, 8)) >> 8)
19111c0d8fdSPaul Kocialkowski #define OV8865_OFFSET_X_L_REG			0x3811
19211c0d8fdSPaul Kocialkowski #define OV8865_OFFSET_X_L(v)			((v) & GENMASK(7, 0))
19311c0d8fdSPaul Kocialkowski #define OV8865_OFFSET_Y_H_REG			0x3812
19411c0d8fdSPaul Kocialkowski #define OV8865_OFFSET_Y_H(v)			(((v) & GENMASK(14, 8)) >> 8)
19511c0d8fdSPaul Kocialkowski #define OV8865_OFFSET_Y_L_REG			0x3813
19611c0d8fdSPaul Kocialkowski #define OV8865_OFFSET_Y_L(v)			((v) & GENMASK(7, 0))
19711c0d8fdSPaul Kocialkowski #define OV8865_INC_X_ODD_REG			0x3814
19811c0d8fdSPaul Kocialkowski #define OV8865_INC_X_ODD(v)			((v) & GENMASK(4, 0))
19911c0d8fdSPaul Kocialkowski #define OV8865_INC_X_EVEN_REG			0x3815
20011c0d8fdSPaul Kocialkowski #define OV8865_INC_X_EVEN(v)			((v) & GENMASK(4, 0))
20111c0d8fdSPaul Kocialkowski #define OV8865_VSYNC_START_H_REG		0x3816
20211c0d8fdSPaul Kocialkowski #define OV8865_VSYNC_START_H(v)			(((v) & GENMASK(15, 8)) >> 8)
20311c0d8fdSPaul Kocialkowski #define OV8865_VSYNC_START_L_REG		0x3817
20411c0d8fdSPaul Kocialkowski #define OV8865_VSYNC_START_L(v)			((v) & GENMASK(7, 0))
20511c0d8fdSPaul Kocialkowski #define OV8865_VSYNC_END_H_REG			0x3818
20611c0d8fdSPaul Kocialkowski #define OV8865_VSYNC_END_H(v)			(((v) & GENMASK(15, 8)) >> 8)
20711c0d8fdSPaul Kocialkowski #define OV8865_VSYNC_END_L_REG			0x3819
20811c0d8fdSPaul Kocialkowski #define OV8865_VSYNC_END_L(v)			((v) & GENMASK(7, 0))
20911c0d8fdSPaul Kocialkowski #define OV8865_HSYNC_FIRST_H_REG		0x381a
21011c0d8fdSPaul Kocialkowski #define OV8865_HSYNC_FIRST_H(v)			(((v) & GENMASK(15, 8)) >> 8)
21111c0d8fdSPaul Kocialkowski #define OV8865_HSYNC_FIRST_L_REG		0x381b
21211c0d8fdSPaul Kocialkowski #define OV8865_HSYNC_FIRST_L(v)			((v) & GENMASK(7, 0))
21311c0d8fdSPaul Kocialkowski 
21411c0d8fdSPaul Kocialkowski #define OV8865_FORMAT1_REG			0x3820
21511c0d8fdSPaul Kocialkowski #define OV8865_FORMAT1_FLIP_VERT_ISP_EN		BIT(2)
21611c0d8fdSPaul Kocialkowski #define OV8865_FORMAT1_FLIP_VERT_SENSOR_EN	BIT(1)
21711c0d8fdSPaul Kocialkowski #define OV8865_FORMAT2_REG			0x3821
21811c0d8fdSPaul Kocialkowski #define OV8865_FORMAT2_HSYNC_EN			BIT(6)
21911c0d8fdSPaul Kocialkowski #define OV8865_FORMAT2_FST_VBIN_EN		BIT(5)
22011c0d8fdSPaul Kocialkowski #define OV8865_FORMAT2_FST_HBIN_EN		BIT(4)
22111c0d8fdSPaul Kocialkowski #define OV8865_FORMAT2_ISP_HORZ_VAR2_EN		BIT(3)
22211c0d8fdSPaul Kocialkowski #define OV8865_FORMAT2_FLIP_HORZ_ISP_EN		BIT(2)
22311c0d8fdSPaul Kocialkowski #define OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN	BIT(1)
22411c0d8fdSPaul Kocialkowski #define OV8865_FORMAT2_SYNC_HBIN_EN		BIT(0)
22511c0d8fdSPaul Kocialkowski 
22611c0d8fdSPaul Kocialkowski #define OV8865_INC_Y_ODD_REG			0x382a
22711c0d8fdSPaul Kocialkowski #define OV8865_INC_Y_ODD(v)			((v) & GENMASK(4, 0))
22811c0d8fdSPaul Kocialkowski #define OV8865_INC_Y_EVEN_REG			0x382b
22911c0d8fdSPaul Kocialkowski #define OV8865_INC_Y_EVEN(v)			((v) & GENMASK(4, 0))
23011c0d8fdSPaul Kocialkowski 
23111c0d8fdSPaul Kocialkowski #define OV8865_ABLC_NUM_REG			0x3830
23211c0d8fdSPaul Kocialkowski #define OV8865_ABLC_NUM(v)			((v) & GENMASK(4, 0))
23311c0d8fdSPaul Kocialkowski 
23411c0d8fdSPaul Kocialkowski #define OV8865_ZLINE_NUM_REG			0x3836
23511c0d8fdSPaul Kocialkowski #define OV8865_ZLINE_NUM(v)			((v) & GENMASK(4, 0))
23611c0d8fdSPaul Kocialkowski 
23711c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_CTRL_REG		0x3841
23811c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG	BIT(5)
23911c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG	BIT(4)
24011c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG	BIT(3)
24111c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG	BIT(2)
24211c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG	BIT(1)
24311c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG	BIT(0)
24411c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_X_OFFSET_H_REG		0x3842
24511c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_X_OFFSET_L_REG		0x3843
24611c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_Y_OFFSET_H_REG		0x3844
24711c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_Y_OFFSET_L_REG		0x3845
24811c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_BOUNDARIES_REG		0x3846
24911c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_BOUNDARIES_Y(v)	(((v) << 4) & GENMASK(7, 4))
25011c0d8fdSPaul Kocialkowski #define OV8865_AUTO_SIZE_BOUNDARIES_X(v)	((v) & GENMASK(3, 0))
25111c0d8fdSPaul Kocialkowski 
25211c0d8fdSPaul Kocialkowski /* PSRAM */
25311c0d8fdSPaul Kocialkowski 
25411c0d8fdSPaul Kocialkowski #define OV8865_PSRAM_CTRL8_REG			0x3f08
25511c0d8fdSPaul Kocialkowski 
25611c0d8fdSPaul Kocialkowski /* Black Level */
25711c0d8fdSPaul Kocialkowski 
25811c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_REG			0x4000
25911c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_TRIG_RANGE_EN		BIT(7)
26011c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_TRIG_FORMAT_EN		BIT(6)
26111c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_TRIG_GAIN_EN		BIT(5)
26211c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN	BIT(4)
26311c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_TRIG_MANUAL_EN		BIT(3)
26411c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_FREEZE_EN		BIT(2)
26511c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_ALWAYS_EN		BIT(1)
26611c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL0_FILTER_EN		BIT(0)
26711c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_REG			0x4001
26811c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_DITHER_EN		BIT(7)
26911c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_ZERO_LINE_DIFF_EN	BIT(6)
27011c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_COL_SHIFT_256		(0 << 4)
27111c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_COL_SHIFT_128		(1 << 4)
27211c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_COL_SHIFT_64		(2 << 4)
27311c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_COL_SHIFT_32		(3 << 4)
27411c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_OFFSET_LIMIT_EN	BIT(2)
27511c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1_COLUMN_CANCEL_EN	BIT(1)
27611c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL2_REG			0x4002
27711c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL3_REG			0x4003
27811c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL4_REG			0x4004
27911c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL5_REG			0x4005
28011c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL6_REG			0x4006
28111c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL7_REG			0x4007
28211c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL8_REG			0x4008
28311c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL9_REG			0x4009
28411c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRLA_REG			0x400a
28511c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRLB_REG			0x400b
28611c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRLC_REG			0x400c
28711c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRLD_REG			0x400d
28811c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRLD_OFFSET_TRIGGER(v)	((v) & GENMASK(7, 0))
28911c0d8fdSPaul Kocialkowski 
29011c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1F_REG			0x401f
29111c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1F_RB_REVERSE		BIT(3)
29211c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1F_INTERPOL_X_EN		BIT(2)
29311c0d8fdSPaul Kocialkowski #define OV8865_BLC_CTRL1F_INTERPOL_Y_EN		BIT(1)
29411c0d8fdSPaul Kocialkowski 
29511c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_LEFT_START_H_REG	0x4020
29611c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_LEFT_START_H(v)	(((v) & GENMASK(11, 8)) >> 8)
29711c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_LEFT_START_L_REG	0x4021
29811c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_LEFT_START_L(v)	((v) & GENMASK(7, 0))
29911c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_LEFT_END_H_REG	0x4022
30011c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_LEFT_END_H(v)		(((v) & GENMASK(11, 8)) >> 8)
30111c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_LEFT_END_L_REG	0x4023
30211c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_LEFT_END_L(v)		((v) & GENMASK(7, 0))
30311c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_RIGHT_START_H_REG	0x4024
30411c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_RIGHT_START_H(v)	(((v) & GENMASK(11, 8)) >> 8)
30511c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_RIGHT_START_L_REG	0x4025
30611c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_RIGHT_START_L(v)	((v) & GENMASK(7, 0))
30711c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_RIGHT_END_H_REG	0x4026
30811c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_RIGHT_END_H(v)	(((v) & GENMASK(11, 8)) >> 8)
30911c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_RIGHT_END_L_REG	0x4027
31011c0d8fdSPaul Kocialkowski #define OV8865_BLC_ANCHOR_RIGHT_END_L(v)	((v) & GENMASK(7, 0))
31111c0d8fdSPaul Kocialkowski 
31211c0d8fdSPaul Kocialkowski #define OV8865_BLC_TOP_ZLINE_START_REG		0x4028
31311c0d8fdSPaul Kocialkowski #define OV8865_BLC_TOP_ZLINE_START(v)		((v) & GENMASK(5, 0))
31411c0d8fdSPaul Kocialkowski #define OV8865_BLC_TOP_ZLINE_NUM_REG		0x4029
31511c0d8fdSPaul Kocialkowski #define OV8865_BLC_TOP_ZLINE_NUM(v)		((v) & GENMASK(4, 0))
31611c0d8fdSPaul Kocialkowski #define OV8865_BLC_TOP_BLKLINE_START_REG	0x402a
31711c0d8fdSPaul Kocialkowski #define OV8865_BLC_TOP_BLKLINE_START(v)		((v) & GENMASK(5, 0))
31811c0d8fdSPaul Kocialkowski #define OV8865_BLC_TOP_BLKLINE_NUM_REG		0x402b
31911c0d8fdSPaul Kocialkowski #define OV8865_BLC_TOP_BLKLINE_NUM(v)		((v) & GENMASK(4, 0))
32011c0d8fdSPaul Kocialkowski #define OV8865_BLC_BOT_ZLINE_START_REG		0x402c
32111c0d8fdSPaul Kocialkowski #define OV8865_BLC_BOT_ZLINE_START(v)		((v) & GENMASK(5, 0))
32211c0d8fdSPaul Kocialkowski #define OV8865_BLC_BOT_ZLINE_NUM_REG		0x402d
32311c0d8fdSPaul Kocialkowski #define OV8865_BLC_BOT_ZLINE_NUM(v)		((v) & GENMASK(4, 0))
32411c0d8fdSPaul Kocialkowski #define OV8865_BLC_BOT_BLKLINE_START_REG	0x402e
32511c0d8fdSPaul Kocialkowski #define OV8865_BLC_BOT_BLKLINE_START(v)		((v) & GENMASK(5, 0))
32611c0d8fdSPaul Kocialkowski #define OV8865_BLC_BOT_BLKLINE_NUM_REG		0x402f
32711c0d8fdSPaul Kocialkowski #define OV8865_BLC_BOT_BLKLINE_NUM(v)		((v) & GENMASK(4, 0))
32811c0d8fdSPaul Kocialkowski 
32911c0d8fdSPaul Kocialkowski #define OV8865_BLC_OFFSET_LIMIT_REG		0x4034
33011c0d8fdSPaul Kocialkowski #define OV8865_BLC_OFFSET_LIMIT(v)		((v) & GENMASK(7, 0))
33111c0d8fdSPaul Kocialkowski 
33211c0d8fdSPaul Kocialkowski /* VFIFO */
33311c0d8fdSPaul Kocialkowski 
33411c0d8fdSPaul Kocialkowski #define OV8865_VFIFO_READ_START_H_REG		0x4600
33511c0d8fdSPaul Kocialkowski #define OV8865_VFIFO_READ_START_H(v)		(((v) & GENMASK(15, 8)) >> 8)
33611c0d8fdSPaul Kocialkowski #define OV8865_VFIFO_READ_START_L_REG		0x4601
33711c0d8fdSPaul Kocialkowski #define OV8865_VFIFO_READ_START_L(v)		((v) & GENMASK(7, 0))
33811c0d8fdSPaul Kocialkowski 
33911c0d8fdSPaul Kocialkowski /* MIPI */
34011c0d8fdSPaul Kocialkowski 
34111c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL0_REG			0x4800
34211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL1_REG			0x4801
34311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL2_REG			0x4802
34411c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL3_REG			0x4803
34511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL4_REG			0x4804
34611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL5_REG			0x4805
34711c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL6_REG			0x4806
34811c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL7_REG			0x4807
34911c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL8_REG			0x4808
35011c0d8fdSPaul Kocialkowski 
35111c0d8fdSPaul Kocialkowski #define OV8865_MIPI_FCNT_MAX_H_REG		0x4810
35211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_FCNT_MAX_L_REG		0x4811
35311c0d8fdSPaul Kocialkowski 
35411c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL13_REG			0x4813
35511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL14_REG			0x4814
35611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL15_REG			0x4815
35711c0d8fdSPaul Kocialkowski #define OV8865_MIPI_EMBEDDED_DT_REG		0x4816
35811c0d8fdSPaul Kocialkowski 
35911c0d8fdSPaul Kocialkowski #define OV8865_MIPI_HS_ZERO_MIN_H_REG		0x4818
36011c0d8fdSPaul Kocialkowski #define OV8865_MIPI_HS_ZERO_MIN_L_REG		0x4819
36111c0d8fdSPaul Kocialkowski #define OV8865_MIPI_HS_TRAIL_MIN_H_REG		0x481a
36211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_HS_TRAIL_MIN_L_REG		0x481b
36311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLK_ZERO_MIN_H_REG		0x481c
36411c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLK_ZERO_MIN_L_REG		0x481d
36511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLK_PREPARE_MAX_REG		0x481e
36611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLK_PREPARE_MIN_REG		0x481f
36711c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLK_POST_MIN_H_REG		0x4820
36811c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLK_POST_MIN_L_REG		0x4821
36911c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLK_TRAIL_MIN_H_REG		0x4822
37011c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLK_TRAIL_MIN_L_REG		0x4823
37111c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LPX_P_MIN_H_REG		0x4824
37211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LPX_P_MIN_L_REG		0x4825
37311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_HS_PREPARE_MIN_REG		0x4826
37411c0d8fdSPaul Kocialkowski #define OV8865_MIPI_HS_PREPARE_MAX_REG		0x4827
37511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_HS_EXIT_MIN_H_REG		0x4828
37611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_HS_EXIT_MIN_L_REG		0x4829
37711c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_HS_ZERO_MIN_REG		0x482a
37811c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_HS_TRAIL_MIN_REG		0x482b
37911c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_CLK_ZERO_MIN_REG		0x482c
38011c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_CLK_PREPARE_REG		0x482d
38111c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_CLK_POST_MIN_REG		0x482e
38211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_CLK_TRAIL_MIN_REG	0x482f
38311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_LPX_P_MIN_REG		0x4830
38411c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_HS_PREPARE_REG		0x4831
38511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_UI_HS_EXIT_MIN_REG		0x4832
38611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_PKT_START_SIZE_REG		0x4833
38711c0d8fdSPaul Kocialkowski 
38811c0d8fdSPaul Kocialkowski #define OV8865_MIPI_PCLK_PERIOD_REG		0x4837
38911c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LP_GPIO0_REG		0x4838
39011c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LP_GPIO1_REG		0x4839
39111c0d8fdSPaul Kocialkowski 
39211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL3C_REG			0x483c
39311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LP_GPIO4_REG		0x483d
39411c0d8fdSPaul Kocialkowski 
39511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL4A_REG			0x484a
39611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL4B_REG			0x484b
39711c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CTRL4C_REG			0x484c
39811c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LANE_TEST_PATTERN_REG	0x484d
39911c0d8fdSPaul Kocialkowski #define OV8865_MIPI_FRAME_END_DELAY_REG		0x484e
40011c0d8fdSPaul Kocialkowski #define OV8865_MIPI_CLOCK_TEST_PATTERN_REG	0x484f
40111c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LANE_SEL01_REG		0x4850
40211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LANE_SEL01_LANE0(v)		(((v) << 0) & GENMASK(2, 0))
40311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LANE_SEL01_LANE1(v)		(((v) << 4) & GENMASK(6, 4))
40411c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LANE_SEL23_REG		0x4851
40511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LANE_SEL23_LANE2(v)		(((v) << 0) & GENMASK(2, 0))
40611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_LANE_SEL23_LANE3(v)		(((v) << 4) & GENMASK(6, 4))
40711c0d8fdSPaul Kocialkowski 
40811c0d8fdSPaul Kocialkowski /* ISP */
40911c0d8fdSPaul Kocialkowski 
41011c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL0_REG			0x5000
41111c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL0_LENC_EN		BIT(7)
41211c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL0_WHITE_BALANCE_EN	BIT(4)
41311c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL0_DPC_BLACK_EN		BIT(2)
41411c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL0_DPC_WHITE_EN		BIT(1)
41511c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL1_REG			0x5001
41611c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL1_BLC_EN			BIT(0)
41711c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL2_REG			0x5002
41811c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL2_DEBUG			BIT(3)
41911c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL2_VARIOPIXEL_EN		BIT(2)
42011c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL2_VSYNC_LATCH_EN		BIT(0)
42111c0d8fdSPaul Kocialkowski #define OV8865_ISP_CTRL3_REG			0x5003
42211c0d8fdSPaul Kocialkowski 
42311c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_RED_H_REG		0x5018
42411c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_RED_H(v)		(((v) & GENMASK(13, 6)) >> 6)
42511c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_RED_L_REG		0x5019
42611c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_RED_L(v)		((v) & GENMASK(5, 0))
42711c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_GREEN_H_REG		0x501a
42811c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_GREEN_H(v)		(((v) & GENMASK(13, 6)) >> 6)
42911c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_GREEN_L_REG		0x501b
43011c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_GREEN_L(v)		((v) & GENMASK(5, 0))
43111c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_BLUE_H_REG		0x501c
43211c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_BLUE_H(v)		(((v) & GENMASK(13, 6)) >> 6)
43311c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_BLUE_L_REG		0x501d
43411c0d8fdSPaul Kocialkowski #define OV8865_ISP_GAIN_BLUE_L(v)		((v) & GENMASK(5, 0))
43511c0d8fdSPaul Kocialkowski 
43611c0d8fdSPaul Kocialkowski /* VarioPixel */
43711c0d8fdSPaul Kocialkowski 
43811c0d8fdSPaul Kocialkowski #define OV8865_VAP_CTRL0_REG			0x5900
43911c0d8fdSPaul Kocialkowski #define OV8865_VAP_CTRL1_REG			0x5901
44011c0d8fdSPaul Kocialkowski #define OV8865_VAP_CTRL1_HSUB_COEF(v)		((((v) - 1) << 2) & \
44111c0d8fdSPaul Kocialkowski 						 GENMASK(3, 2))
44211c0d8fdSPaul Kocialkowski #define OV8865_VAP_CTRL1_VSUB_COEF(v)		(((v) - 1) & GENMASK(1, 0))
44311c0d8fdSPaul Kocialkowski 
44411c0d8fdSPaul Kocialkowski /* Pre-DSP */
44511c0d8fdSPaul Kocialkowski 
44611c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_REG			0x5e00
44711c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_PATTERN_EN		BIT(7)
44811c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_ROLLING_BAR_EN		BIT(6)
44911c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_TRANSPARENT_MODE	BIT(5)
45011c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_SQUARES_BW_MODE	BIT(4)
45111c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_PATTERN_COLOR_BARS	0
45211c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA	1
45311c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES	2
45411c0d8fdSPaul Kocialkowski #define OV8865_PRE_CTRL0_PATTERN_BLACK		3
45511c0d8fdSPaul Kocialkowski 
45611c0d8fdSPaul Kocialkowski /* Macros */
45711c0d8fdSPaul Kocialkowski 
45811c0d8fdSPaul Kocialkowski #define ov8865_subdev_sensor(s) \
45911c0d8fdSPaul Kocialkowski 	container_of(s, struct ov8865_sensor, subdev)
46011c0d8fdSPaul Kocialkowski 
46111c0d8fdSPaul Kocialkowski #define ov8865_ctrl_subdev(c) \
46236e4f2b2SPaul Kocialkowski 	(&container_of((c)->handler, struct ov8865_sensor, \
46336e4f2b2SPaul Kocialkowski 		       ctrls.handler)->subdev)
46411c0d8fdSPaul Kocialkowski 
46511c0d8fdSPaul Kocialkowski /* Data structures */
46611c0d8fdSPaul Kocialkowski 
46711c0d8fdSPaul Kocialkowski struct ov8865_register_value {
46811c0d8fdSPaul Kocialkowski 	u16 address;
46911c0d8fdSPaul Kocialkowski 	u8 value;
47011c0d8fdSPaul Kocialkowski 	unsigned int delay_ms;
47111c0d8fdSPaul Kocialkowski };
47211c0d8fdSPaul Kocialkowski 
47311c0d8fdSPaul Kocialkowski /*
47411c0d8fdSPaul Kocialkowski  * PLL1 Clock Tree:
47511c0d8fdSPaul Kocialkowski  *
47611c0d8fdSPaul Kocialkowski  * +-< EXTCLK
47711c0d8fdSPaul Kocialkowski  * |
47811c0d8fdSPaul Kocialkowski  * +-+ pll_pre_div_half (0x30a [0])
47911c0d8fdSPaul Kocialkowski  *   |
48011c0d8fdSPaul Kocialkowski  *   +-+ pll_pre_div (0x300 [2:0], special values:
48111c0d8fdSPaul Kocialkowski  *     |              0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8)
48211c0d8fdSPaul Kocialkowski  *     +-+ pll_mul (0x301 [1:0], 0x302 [7:0])
48311c0d8fdSPaul Kocialkowski  *       |
48411c0d8fdSPaul Kocialkowski  *       +-+ m_div (0x303 [3:0])
48511c0d8fdSPaul Kocialkowski  *       | |
48611c0d8fdSPaul Kocialkowski  *       | +-> PHY_SCLK
48711c0d8fdSPaul Kocialkowski  *       | |
48811c0d8fdSPaul Kocialkowski  *       | +-+ mipi_div (0x304 [1:0], special values: 0: 4, 1: 5, 2: 6, 3: 8)
48911c0d8fdSPaul Kocialkowski  *       |   |
49011c0d8fdSPaul Kocialkowski  *       |   +-+ pclk_div (0x3020 [3])
49111c0d8fdSPaul Kocialkowski  *       |     |
49211c0d8fdSPaul Kocialkowski  *       |     +-> PCLK
49311c0d8fdSPaul Kocialkowski  *       |
49411c0d8fdSPaul Kocialkowski  *       +-+ sys_pre_div (0x305 [1:0], special values: 0: 3, 1: 4, 2: 5, 3: 6)
49511c0d8fdSPaul Kocialkowski  *         |
49611c0d8fdSPaul Kocialkowski  *         +-+ sys_div (0x306 [0])
49711c0d8fdSPaul Kocialkowski  *           |
49811c0d8fdSPaul Kocialkowski  *           +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2)
49911c0d8fdSPaul Kocialkowski  *             |
50011c0d8fdSPaul Kocialkowski  *             +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK)
50111c0d8fdSPaul Kocialkowski  *               |
50211c0d8fdSPaul Kocialkowski  *               +-+ sclk_pre_div (0x3106 [3:2], special values:
50311c0d8fdSPaul Kocialkowski  *                 |               0: 1, 1: 2, 2: 4, 3: 1)
50411c0d8fdSPaul Kocialkowski  *                 |
50511c0d8fdSPaul Kocialkowski  *                 +-+ sclk_div (0x3106 [7:4], special values: 0: 1)
50611c0d8fdSPaul Kocialkowski  *                   |
50711c0d8fdSPaul Kocialkowski  *                   +-> SCLK
50811c0d8fdSPaul Kocialkowski  */
50911c0d8fdSPaul Kocialkowski 
51011c0d8fdSPaul Kocialkowski struct ov8865_pll1_config {
51111c0d8fdSPaul Kocialkowski 	unsigned int pll_pre_div_half;
51211c0d8fdSPaul Kocialkowski 	unsigned int pll_pre_div;
51311c0d8fdSPaul Kocialkowski 	unsigned int pll_mul;
51411c0d8fdSPaul Kocialkowski 	unsigned int m_div;
51511c0d8fdSPaul Kocialkowski 	unsigned int mipi_div;
51611c0d8fdSPaul Kocialkowski 	unsigned int pclk_div;
51711c0d8fdSPaul Kocialkowski 	unsigned int sys_pre_div;
51811c0d8fdSPaul Kocialkowski 	unsigned int sys_div;
51911c0d8fdSPaul Kocialkowski };
52011c0d8fdSPaul Kocialkowski 
52111c0d8fdSPaul Kocialkowski /*
52211c0d8fdSPaul Kocialkowski  * PLL2 Clock Tree:
52311c0d8fdSPaul Kocialkowski  *
52411c0d8fdSPaul Kocialkowski  * +-< EXTCLK
52511c0d8fdSPaul Kocialkowski  * |
52611c0d8fdSPaul Kocialkowski  * +-+ pll_pre_div_half (0x312 [4])
52711c0d8fdSPaul Kocialkowski  *   |
52811c0d8fdSPaul Kocialkowski  *   +-+ pll_pre_div (0x30b [2:0], special values:
52911c0d8fdSPaul Kocialkowski  *     |              0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8)
53011c0d8fdSPaul Kocialkowski  *     +-+ pll_mul (0x30c [1:0], 0x30d [7:0])
53111c0d8fdSPaul Kocialkowski  *       |
53211c0d8fdSPaul Kocialkowski  *       +-+ dac_div (0x312 [3:0])
53311c0d8fdSPaul Kocialkowski  *       | |
53411c0d8fdSPaul Kocialkowski  *       | +-> DAC_CLK
53511c0d8fdSPaul Kocialkowski  *       |
53611c0d8fdSPaul Kocialkowski  *       +-+ sys_pre_div (0x30f [3:0])
53711c0d8fdSPaul Kocialkowski  *         |
53811c0d8fdSPaul Kocialkowski  *         +-+ sys_div (0x30e [2:0], special values:
53911c0d8fdSPaul Kocialkowski  *           |          0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 3.5, 6: 4, 7:5)
54011c0d8fdSPaul Kocialkowski  *           |
54111c0d8fdSPaul Kocialkowski  *           +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2)
54211c0d8fdSPaul Kocialkowski  *             |
54311c0d8fdSPaul Kocialkowski  *             +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK)
54411c0d8fdSPaul Kocialkowski  *               |
54511c0d8fdSPaul Kocialkowski  *               +-+ sclk_pre_div (0x3106 [3:2], special values:
54611c0d8fdSPaul Kocialkowski  *                 |               0: 1, 1: 2, 2: 4, 3: 1)
54711c0d8fdSPaul Kocialkowski  *                 |
54811c0d8fdSPaul Kocialkowski  *                 +-+ sclk_div (0x3106 [7:4], special values: 0: 1)
54911c0d8fdSPaul Kocialkowski  *                   |
55011c0d8fdSPaul Kocialkowski  *                   +-> SCLK
55111c0d8fdSPaul Kocialkowski  */
55211c0d8fdSPaul Kocialkowski 
55311c0d8fdSPaul Kocialkowski struct ov8865_pll2_config {
55411c0d8fdSPaul Kocialkowski 	unsigned int pll_pre_div_half;
55511c0d8fdSPaul Kocialkowski 	unsigned int pll_pre_div;
55611c0d8fdSPaul Kocialkowski 	unsigned int pll_mul;
55711c0d8fdSPaul Kocialkowski 	unsigned int dac_div;
55811c0d8fdSPaul Kocialkowski 	unsigned int sys_pre_div;
55911c0d8fdSPaul Kocialkowski 	unsigned int sys_div;
56011c0d8fdSPaul Kocialkowski };
56111c0d8fdSPaul Kocialkowski 
56211c0d8fdSPaul Kocialkowski struct ov8865_sclk_config {
56311c0d8fdSPaul Kocialkowski 	unsigned int sys_sel;
56411c0d8fdSPaul Kocialkowski 	unsigned int sclk_sel;
56511c0d8fdSPaul Kocialkowski 	unsigned int sclk_pre_div;
56611c0d8fdSPaul Kocialkowski 	unsigned int sclk_div;
56711c0d8fdSPaul Kocialkowski };
56811c0d8fdSPaul Kocialkowski 
56911c0d8fdSPaul Kocialkowski /*
57011c0d8fdSPaul Kocialkowski  * General formulas for (array-centered) mode calculation:
57111c0d8fdSPaul Kocialkowski  * - photo_array_width = 3296
57211c0d8fdSPaul Kocialkowski  * - crop_start_x = (photo_array_width - output_size_x) / 2
57311c0d8fdSPaul Kocialkowski  * - crop_end_x = crop_start_x + offset_x + output_size_x - 1
57411c0d8fdSPaul Kocialkowski  *
57511c0d8fdSPaul Kocialkowski  * - photo_array_height = 2480
57611c0d8fdSPaul Kocialkowski  * - crop_start_y = (photo_array_height - output_size_y) / 2
57711c0d8fdSPaul Kocialkowski  * - crop_end_y = crop_start_y + offset_y + output_size_y - 1
57811c0d8fdSPaul Kocialkowski  */
57911c0d8fdSPaul Kocialkowski 
58011c0d8fdSPaul Kocialkowski struct ov8865_mode {
58111c0d8fdSPaul Kocialkowski 	unsigned int crop_start_x;
58211c0d8fdSPaul Kocialkowski 	unsigned int offset_x;
58311c0d8fdSPaul Kocialkowski 	unsigned int output_size_x;
58411c0d8fdSPaul Kocialkowski 	unsigned int crop_end_x;
58511c0d8fdSPaul Kocialkowski 	unsigned int hts;
58611c0d8fdSPaul Kocialkowski 
58711c0d8fdSPaul Kocialkowski 	unsigned int crop_start_y;
58811c0d8fdSPaul Kocialkowski 	unsigned int offset_y;
58911c0d8fdSPaul Kocialkowski 	unsigned int output_size_y;
59011c0d8fdSPaul Kocialkowski 	unsigned int crop_end_y;
59111c0d8fdSPaul Kocialkowski 	unsigned int vts;
59211c0d8fdSPaul Kocialkowski 
59311c0d8fdSPaul Kocialkowski 	/* With auto size, only output and total sizes need to be set. */
59411c0d8fdSPaul Kocialkowski 	bool size_auto;
59511c0d8fdSPaul Kocialkowski 	unsigned int size_auto_boundary_x;
59611c0d8fdSPaul Kocialkowski 	unsigned int size_auto_boundary_y;
59711c0d8fdSPaul Kocialkowski 
59811c0d8fdSPaul Kocialkowski 	bool binning_x;
59911c0d8fdSPaul Kocialkowski 	bool binning_y;
60011c0d8fdSPaul Kocialkowski 	bool variopixel;
60111c0d8fdSPaul Kocialkowski 	unsigned int variopixel_hsub_coef;
60211c0d8fdSPaul Kocialkowski 	unsigned int variopixel_vsub_coef;
60311c0d8fdSPaul Kocialkowski 
60411c0d8fdSPaul Kocialkowski 	/* Bits for the format register, used for binning. */
60511c0d8fdSPaul Kocialkowski 	bool sync_hbin;
60611c0d8fdSPaul Kocialkowski 	bool horz_var2;
60711c0d8fdSPaul Kocialkowski 
60811c0d8fdSPaul Kocialkowski 	unsigned int inc_x_odd;
60911c0d8fdSPaul Kocialkowski 	unsigned int inc_x_even;
61011c0d8fdSPaul Kocialkowski 	unsigned int inc_y_odd;
61111c0d8fdSPaul Kocialkowski 	unsigned int inc_y_even;
61211c0d8fdSPaul Kocialkowski 
61311c0d8fdSPaul Kocialkowski 	unsigned int vfifo_read_start;
61411c0d8fdSPaul Kocialkowski 
61511c0d8fdSPaul Kocialkowski 	unsigned int ablc_num;
61611c0d8fdSPaul Kocialkowski 	unsigned int zline_num;
61711c0d8fdSPaul Kocialkowski 
61811c0d8fdSPaul Kocialkowski 	unsigned int blc_top_zero_line_start;
61911c0d8fdSPaul Kocialkowski 	unsigned int blc_top_zero_line_num;
62011c0d8fdSPaul Kocialkowski 	unsigned int blc_top_black_line_start;
62111c0d8fdSPaul Kocialkowski 	unsigned int blc_top_black_line_num;
62211c0d8fdSPaul Kocialkowski 
62311c0d8fdSPaul Kocialkowski 	unsigned int blc_bottom_zero_line_start;
62411c0d8fdSPaul Kocialkowski 	unsigned int blc_bottom_zero_line_num;
62511c0d8fdSPaul Kocialkowski 	unsigned int blc_bottom_black_line_start;
62611c0d8fdSPaul Kocialkowski 	unsigned int blc_bottom_black_line_num;
62711c0d8fdSPaul Kocialkowski 
62811c0d8fdSPaul Kocialkowski 	u8 blc_col_shift_mask;
62911c0d8fdSPaul Kocialkowski 
63011c0d8fdSPaul Kocialkowski 	unsigned int blc_anchor_left_start;
63111c0d8fdSPaul Kocialkowski 	unsigned int blc_anchor_left_end;
63211c0d8fdSPaul Kocialkowski 	unsigned int blc_anchor_right_start;
63311c0d8fdSPaul Kocialkowski 	unsigned int blc_anchor_right_end;
63411c0d8fdSPaul Kocialkowski 
63511c0d8fdSPaul Kocialkowski 	struct v4l2_fract frame_interval;
63611c0d8fdSPaul Kocialkowski 
63711c0d8fdSPaul Kocialkowski 	const struct ov8865_pll1_config *pll1_config;
63811c0d8fdSPaul Kocialkowski 	const struct ov8865_pll2_config *pll2_config;
63911c0d8fdSPaul Kocialkowski 	const struct ov8865_sclk_config *sclk_config;
64011c0d8fdSPaul Kocialkowski 
64111c0d8fdSPaul Kocialkowski 	const struct ov8865_register_value *register_values;
64211c0d8fdSPaul Kocialkowski 	unsigned int register_values_count;
64311c0d8fdSPaul Kocialkowski };
64411c0d8fdSPaul Kocialkowski 
64511c0d8fdSPaul Kocialkowski struct ov8865_state {
64611c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode;
64711c0d8fdSPaul Kocialkowski 	u32 mbus_code;
64811c0d8fdSPaul Kocialkowski 
64911c0d8fdSPaul Kocialkowski 	bool streaming;
65011c0d8fdSPaul Kocialkowski };
65111c0d8fdSPaul Kocialkowski 
65211c0d8fdSPaul Kocialkowski struct ov8865_ctrls {
65311c0d8fdSPaul Kocialkowski 	struct v4l2_ctrl *link_freq;
65411c0d8fdSPaul Kocialkowski 	struct v4l2_ctrl *pixel_rate;
65511c0d8fdSPaul Kocialkowski 
65611c0d8fdSPaul Kocialkowski 	struct v4l2_ctrl_handler handler;
65711c0d8fdSPaul Kocialkowski };
65811c0d8fdSPaul Kocialkowski 
65911c0d8fdSPaul Kocialkowski struct ov8865_sensor {
66011c0d8fdSPaul Kocialkowski 	struct device *dev;
66111c0d8fdSPaul Kocialkowski 	struct i2c_client *i2c_client;
66211c0d8fdSPaul Kocialkowski 	struct gpio_desc *reset;
66311c0d8fdSPaul Kocialkowski 	struct gpio_desc *powerdown;
66411c0d8fdSPaul Kocialkowski 	struct regulator *avdd;
66511c0d8fdSPaul Kocialkowski 	struct regulator *dvdd;
66611c0d8fdSPaul Kocialkowski 	struct regulator *dovdd;
66711c0d8fdSPaul Kocialkowski 	struct clk *extclk;
66811c0d8fdSPaul Kocialkowski 
66911c0d8fdSPaul Kocialkowski 	struct v4l2_fwnode_endpoint endpoint;
67011c0d8fdSPaul Kocialkowski 	struct v4l2_subdev subdev;
67111c0d8fdSPaul Kocialkowski 	struct media_pad pad;
67211c0d8fdSPaul Kocialkowski 
67311c0d8fdSPaul Kocialkowski 	struct mutex mutex;
67411c0d8fdSPaul Kocialkowski 
67511c0d8fdSPaul Kocialkowski 	struct ov8865_state state;
67611c0d8fdSPaul Kocialkowski 	struct ov8865_ctrls ctrls;
67711c0d8fdSPaul Kocialkowski };
67811c0d8fdSPaul Kocialkowski 
67911c0d8fdSPaul Kocialkowski /* Static definitions */
68011c0d8fdSPaul Kocialkowski 
68111c0d8fdSPaul Kocialkowski /*
68211c0d8fdSPaul Kocialkowski  * EXTCLK = 24 MHz
68311c0d8fdSPaul Kocialkowski  * PHY_SCLK = 720 MHz
68411c0d8fdSPaul Kocialkowski  * MIPI_PCLK = 90 MHz
68511c0d8fdSPaul Kocialkowski  */
68611c0d8fdSPaul Kocialkowski static const struct ov8865_pll1_config ov8865_pll1_config_native = {
68711c0d8fdSPaul Kocialkowski 	.pll_pre_div_half	= 1,
68811c0d8fdSPaul Kocialkowski 	.pll_pre_div		= 0,
68911c0d8fdSPaul Kocialkowski 	.pll_mul		= 30,
69011c0d8fdSPaul Kocialkowski 	.m_div			= 1,
69111c0d8fdSPaul Kocialkowski 	.mipi_div		= 3,
69211c0d8fdSPaul Kocialkowski 	.pclk_div		= 1,
69311c0d8fdSPaul Kocialkowski 	.sys_pre_div		= 1,
69411c0d8fdSPaul Kocialkowski 	.sys_div		= 2,
69511c0d8fdSPaul Kocialkowski };
69611c0d8fdSPaul Kocialkowski 
69711c0d8fdSPaul Kocialkowski /*
69811c0d8fdSPaul Kocialkowski  * EXTCLK = 24 MHz
69911c0d8fdSPaul Kocialkowski  * DAC_CLK = 360 MHz
70011c0d8fdSPaul Kocialkowski  * SCLK = 144 MHz
70111c0d8fdSPaul Kocialkowski  */
70211c0d8fdSPaul Kocialkowski 
70311c0d8fdSPaul Kocialkowski static const struct ov8865_pll2_config ov8865_pll2_config_native = {
70411c0d8fdSPaul Kocialkowski 	.pll_pre_div_half	= 1,
70511c0d8fdSPaul Kocialkowski 	.pll_pre_div		= 0,
70611c0d8fdSPaul Kocialkowski 	.pll_mul		= 30,
70711c0d8fdSPaul Kocialkowski 	.dac_div		= 2,
70811c0d8fdSPaul Kocialkowski 	.sys_pre_div		= 5,
70911c0d8fdSPaul Kocialkowski 	.sys_div		= 0,
71011c0d8fdSPaul Kocialkowski };
71111c0d8fdSPaul Kocialkowski 
71211c0d8fdSPaul Kocialkowski /*
71311c0d8fdSPaul Kocialkowski  * EXTCLK = 24 MHz
71411c0d8fdSPaul Kocialkowski  * DAC_CLK = 360 MHz
71511c0d8fdSPaul Kocialkowski  * SCLK = 80 MHz
71611c0d8fdSPaul Kocialkowski  */
71711c0d8fdSPaul Kocialkowski 
71811c0d8fdSPaul Kocialkowski static const struct ov8865_pll2_config ov8865_pll2_config_binning = {
71911c0d8fdSPaul Kocialkowski 	.pll_pre_div_half	= 1,
72011c0d8fdSPaul Kocialkowski 	.pll_pre_div		= 0,
72111c0d8fdSPaul Kocialkowski 	.pll_mul		= 30,
72211c0d8fdSPaul Kocialkowski 	.dac_div		= 2,
72311c0d8fdSPaul Kocialkowski 	.sys_pre_div		= 10,
72411c0d8fdSPaul Kocialkowski 	.sys_div		= 0,
72511c0d8fdSPaul Kocialkowski };
72611c0d8fdSPaul Kocialkowski 
72711c0d8fdSPaul Kocialkowski static const struct ov8865_sclk_config ov8865_sclk_config_native = {
72811c0d8fdSPaul Kocialkowski 	.sys_sel		= 1,
72911c0d8fdSPaul Kocialkowski 	.sclk_sel		= 0,
73011c0d8fdSPaul Kocialkowski 	.sclk_pre_div		= 0,
73111c0d8fdSPaul Kocialkowski 	.sclk_div		= 0,
73211c0d8fdSPaul Kocialkowski };
73311c0d8fdSPaul Kocialkowski 
73411c0d8fdSPaul Kocialkowski static const struct ov8865_register_value ov8865_register_values_native[] = {
73511c0d8fdSPaul Kocialkowski 	/* Sensor */
73611c0d8fdSPaul Kocialkowski 
73711c0d8fdSPaul Kocialkowski 	{ 0x3700, 0x48 },
73811c0d8fdSPaul Kocialkowski 	{ 0x3701, 0x18 },
73911c0d8fdSPaul Kocialkowski 	{ 0x3702, 0x50 },
74011c0d8fdSPaul Kocialkowski 	{ 0x3703, 0x32 },
74111c0d8fdSPaul Kocialkowski 	{ 0x3704, 0x28 },
74211c0d8fdSPaul Kocialkowski 	{ 0x3706, 0x70 },
74311c0d8fdSPaul Kocialkowski 	{ 0x3707, 0x08 },
74411c0d8fdSPaul Kocialkowski 	{ 0x3708, 0x48 },
74511c0d8fdSPaul Kocialkowski 	{ 0x3709, 0x80 },
74611c0d8fdSPaul Kocialkowski 	{ 0x370a, 0x01 },
74711c0d8fdSPaul Kocialkowski 	{ 0x370b, 0x70 },
74811c0d8fdSPaul Kocialkowski 	{ 0x370c, 0x07 },
74911c0d8fdSPaul Kocialkowski 	{ 0x3718, 0x14 },
75011c0d8fdSPaul Kocialkowski 	{ 0x3712, 0x44 },
75111c0d8fdSPaul Kocialkowski 	{ 0x371e, 0x31 },
75211c0d8fdSPaul Kocialkowski 	{ 0x371f, 0x7f },
75311c0d8fdSPaul Kocialkowski 	{ 0x3720, 0x0a },
75411c0d8fdSPaul Kocialkowski 	{ 0x3721, 0x0a },
75511c0d8fdSPaul Kocialkowski 	{ 0x3724, 0x04 },
75611c0d8fdSPaul Kocialkowski 	{ 0x3725, 0x04 },
75711c0d8fdSPaul Kocialkowski 	{ 0x3726, 0x0c },
75811c0d8fdSPaul Kocialkowski 	{ 0x3728, 0x0a },
75911c0d8fdSPaul Kocialkowski 	{ 0x3729, 0x03 },
76011c0d8fdSPaul Kocialkowski 	{ 0x372a, 0x06 },
76111c0d8fdSPaul Kocialkowski 	{ 0x372b, 0xa6 },
76211c0d8fdSPaul Kocialkowski 	{ 0x372c, 0xa6 },
76311c0d8fdSPaul Kocialkowski 	{ 0x372d, 0xa6 },
76411c0d8fdSPaul Kocialkowski 	{ 0x372e, 0x0c },
76511c0d8fdSPaul Kocialkowski 	{ 0x372f, 0x20 },
76611c0d8fdSPaul Kocialkowski 	{ 0x3730, 0x02 },
76711c0d8fdSPaul Kocialkowski 	{ 0x3731, 0x0c },
76811c0d8fdSPaul Kocialkowski 	{ 0x3732, 0x28 },
76911c0d8fdSPaul Kocialkowski 	{ 0x3736, 0x30 },
77011c0d8fdSPaul Kocialkowski 	{ 0x373a, 0x04 },
77111c0d8fdSPaul Kocialkowski 	{ 0x373b, 0x18 },
77211c0d8fdSPaul Kocialkowski 	{ 0x373c, 0x14 },
77311c0d8fdSPaul Kocialkowski 	{ 0x373e, 0x06 },
77411c0d8fdSPaul Kocialkowski 	{ 0x375a, 0x0c },
77511c0d8fdSPaul Kocialkowski 	{ 0x375b, 0x26 },
77611c0d8fdSPaul Kocialkowski 	{ 0x375d, 0x04 },
77711c0d8fdSPaul Kocialkowski 	{ 0x375f, 0x28 },
77811c0d8fdSPaul Kocialkowski 	{ 0x3767, 0x1e },
77911c0d8fdSPaul Kocialkowski 	{ 0x3772, 0x46 },
78011c0d8fdSPaul Kocialkowski 	{ 0x3773, 0x04 },
78111c0d8fdSPaul Kocialkowski 	{ 0x3774, 0x2c },
78211c0d8fdSPaul Kocialkowski 	{ 0x3775, 0x13 },
78311c0d8fdSPaul Kocialkowski 	{ 0x3776, 0x10 },
78411c0d8fdSPaul Kocialkowski 	{ 0x37a0, 0x88 },
78511c0d8fdSPaul Kocialkowski 	{ 0x37a1, 0x7a },
78611c0d8fdSPaul Kocialkowski 	{ 0x37a2, 0x7a },
78711c0d8fdSPaul Kocialkowski 	{ 0x37a3, 0x02 },
78811c0d8fdSPaul Kocialkowski 	{ 0x37a5, 0x09 },
78911c0d8fdSPaul Kocialkowski 	{ 0x37a7, 0x88 },
79011c0d8fdSPaul Kocialkowski 	{ 0x37a8, 0xb0 },
79111c0d8fdSPaul Kocialkowski 	{ 0x37a9, 0xb0 },
79211c0d8fdSPaul Kocialkowski 	{ 0x37aa, 0x88 },
79311c0d8fdSPaul Kocialkowski 	{ 0x37ab, 0x5c },
79411c0d8fdSPaul Kocialkowski 	{ 0x37ac, 0x5c },
79511c0d8fdSPaul Kocialkowski 	{ 0x37ad, 0x55 },
79611c0d8fdSPaul Kocialkowski 	{ 0x37ae, 0x19 },
79711c0d8fdSPaul Kocialkowski 	{ 0x37af, 0x19 },
79811c0d8fdSPaul Kocialkowski 	{ 0x37b3, 0x84 },
79911c0d8fdSPaul Kocialkowski 	{ 0x37b4, 0x84 },
80011c0d8fdSPaul Kocialkowski 	{ 0x37b5, 0x66 },
80111c0d8fdSPaul Kocialkowski 
80211c0d8fdSPaul Kocialkowski 	/* PSRAM */
80311c0d8fdSPaul Kocialkowski 
80411c0d8fdSPaul Kocialkowski 	{ OV8865_PSRAM_CTRL8_REG, 0x16 },
80511c0d8fdSPaul Kocialkowski 
80611c0d8fdSPaul Kocialkowski 	/* ADC Sync */
80711c0d8fdSPaul Kocialkowski 
80811c0d8fdSPaul Kocialkowski 	{ 0x4500, 0x68 },
80911c0d8fdSPaul Kocialkowski };
81011c0d8fdSPaul Kocialkowski 
81111c0d8fdSPaul Kocialkowski static const struct ov8865_register_value ov8865_register_values_binning[] = {
81211c0d8fdSPaul Kocialkowski 	/* Sensor */
81311c0d8fdSPaul Kocialkowski 
81411c0d8fdSPaul Kocialkowski 	{ 0x3700, 0x24 },
81511c0d8fdSPaul Kocialkowski 	{ 0x3701, 0x0c },
81611c0d8fdSPaul Kocialkowski 	{ 0x3702, 0x28 },
81711c0d8fdSPaul Kocialkowski 	{ 0x3703, 0x19 },
81811c0d8fdSPaul Kocialkowski 	{ 0x3704, 0x14 },
81911c0d8fdSPaul Kocialkowski 	{ 0x3706, 0x38 },
82011c0d8fdSPaul Kocialkowski 	{ 0x3707, 0x04 },
82111c0d8fdSPaul Kocialkowski 	{ 0x3708, 0x24 },
82211c0d8fdSPaul Kocialkowski 	{ 0x3709, 0x40 },
82311c0d8fdSPaul Kocialkowski 	{ 0x370a, 0x00 },
82411c0d8fdSPaul Kocialkowski 	{ 0x370b, 0xb8 },
82511c0d8fdSPaul Kocialkowski 	{ 0x370c, 0x04 },
82611c0d8fdSPaul Kocialkowski 	{ 0x3718, 0x12 },
82711c0d8fdSPaul Kocialkowski 	{ 0x3712, 0x42 },
82811c0d8fdSPaul Kocialkowski 	{ 0x371e, 0x19 },
82911c0d8fdSPaul Kocialkowski 	{ 0x371f, 0x40 },
83011c0d8fdSPaul Kocialkowski 	{ 0x3720, 0x05 },
83111c0d8fdSPaul Kocialkowski 	{ 0x3721, 0x05 },
83211c0d8fdSPaul Kocialkowski 	{ 0x3724, 0x02 },
83311c0d8fdSPaul Kocialkowski 	{ 0x3725, 0x02 },
83411c0d8fdSPaul Kocialkowski 	{ 0x3726, 0x06 },
83511c0d8fdSPaul Kocialkowski 	{ 0x3728, 0x05 },
83611c0d8fdSPaul Kocialkowski 	{ 0x3729, 0x02 },
83711c0d8fdSPaul Kocialkowski 	{ 0x372a, 0x03 },
83811c0d8fdSPaul Kocialkowski 	{ 0x372b, 0x53 },
83911c0d8fdSPaul Kocialkowski 	{ 0x372c, 0xa3 },
84011c0d8fdSPaul Kocialkowski 	{ 0x372d, 0x53 },
84111c0d8fdSPaul Kocialkowski 	{ 0x372e, 0x06 },
84211c0d8fdSPaul Kocialkowski 	{ 0x372f, 0x10 },
84311c0d8fdSPaul Kocialkowski 	{ 0x3730, 0x01 },
84411c0d8fdSPaul Kocialkowski 	{ 0x3731, 0x06 },
84511c0d8fdSPaul Kocialkowski 	{ 0x3732, 0x14 },
84611c0d8fdSPaul Kocialkowski 	{ 0x3736, 0x20 },
84711c0d8fdSPaul Kocialkowski 	{ 0x373a, 0x02 },
84811c0d8fdSPaul Kocialkowski 	{ 0x373b, 0x0c },
84911c0d8fdSPaul Kocialkowski 	{ 0x373c, 0x0a },
85011c0d8fdSPaul Kocialkowski 	{ 0x373e, 0x03 },
85111c0d8fdSPaul Kocialkowski 	{ 0x375a, 0x06 },
85211c0d8fdSPaul Kocialkowski 	{ 0x375b, 0x13 },
85311c0d8fdSPaul Kocialkowski 	{ 0x375d, 0x02 },
85411c0d8fdSPaul Kocialkowski 	{ 0x375f, 0x14 },
85511c0d8fdSPaul Kocialkowski 	{ 0x3767, 0x1c },
85611c0d8fdSPaul Kocialkowski 	{ 0x3772, 0x23 },
85711c0d8fdSPaul Kocialkowski 	{ 0x3773, 0x02 },
85811c0d8fdSPaul Kocialkowski 	{ 0x3774, 0x16 },
85911c0d8fdSPaul Kocialkowski 	{ 0x3775, 0x12 },
86011c0d8fdSPaul Kocialkowski 	{ 0x3776, 0x08 },
86111c0d8fdSPaul Kocialkowski 	{ 0x37a0, 0x44 },
86211c0d8fdSPaul Kocialkowski 	{ 0x37a1, 0x3d },
86311c0d8fdSPaul Kocialkowski 	{ 0x37a2, 0x3d },
86411c0d8fdSPaul Kocialkowski 	{ 0x37a3, 0x01 },
86511c0d8fdSPaul Kocialkowski 	{ 0x37a5, 0x08 },
86611c0d8fdSPaul Kocialkowski 	{ 0x37a7, 0x44 },
86711c0d8fdSPaul Kocialkowski 	{ 0x37a8, 0x58 },
86811c0d8fdSPaul Kocialkowski 	{ 0x37a9, 0x58 },
86911c0d8fdSPaul Kocialkowski 	{ 0x37aa, 0x44 },
87011c0d8fdSPaul Kocialkowski 	{ 0x37ab, 0x2e },
87111c0d8fdSPaul Kocialkowski 	{ 0x37ac, 0x2e },
87211c0d8fdSPaul Kocialkowski 	{ 0x37ad, 0x33 },
87311c0d8fdSPaul Kocialkowski 	{ 0x37ae, 0x0d },
87411c0d8fdSPaul Kocialkowski 	{ 0x37af, 0x0d },
87511c0d8fdSPaul Kocialkowski 	{ 0x37b3, 0x42 },
87611c0d8fdSPaul Kocialkowski 	{ 0x37b4, 0x42 },
87711c0d8fdSPaul Kocialkowski 	{ 0x37b5, 0x33 },
87811c0d8fdSPaul Kocialkowski 
87911c0d8fdSPaul Kocialkowski 	/* PSRAM */
88011c0d8fdSPaul Kocialkowski 
88111c0d8fdSPaul Kocialkowski 	{ OV8865_PSRAM_CTRL8_REG, 0x0b },
88211c0d8fdSPaul Kocialkowski 
88311c0d8fdSPaul Kocialkowski 	/* ADC Sync */
88411c0d8fdSPaul Kocialkowski 
88511c0d8fdSPaul Kocialkowski 	{ 0x4500, 0x40 },
88611c0d8fdSPaul Kocialkowski };
88711c0d8fdSPaul Kocialkowski 
88811c0d8fdSPaul Kocialkowski static const struct ov8865_mode ov8865_modes[] = {
88911c0d8fdSPaul Kocialkowski 	/* 3264x2448 */
89011c0d8fdSPaul Kocialkowski 	{
89111c0d8fdSPaul Kocialkowski 		/* Horizontal */
89211c0d8fdSPaul Kocialkowski 		.output_size_x			= 3264,
89311c0d8fdSPaul Kocialkowski 		.hts				= 1944,
89411c0d8fdSPaul Kocialkowski 
89511c0d8fdSPaul Kocialkowski 		/* Vertical */
89611c0d8fdSPaul Kocialkowski 		.output_size_y			= 2448,
89711c0d8fdSPaul Kocialkowski 		.vts				= 2470,
89811c0d8fdSPaul Kocialkowski 
89911c0d8fdSPaul Kocialkowski 		.size_auto			= true,
90011c0d8fdSPaul Kocialkowski 		.size_auto_boundary_x		= 8,
90111c0d8fdSPaul Kocialkowski 		.size_auto_boundary_y		= 4,
90211c0d8fdSPaul Kocialkowski 
90311c0d8fdSPaul Kocialkowski 		/* Subsample increase */
90411c0d8fdSPaul Kocialkowski 		.inc_x_odd			= 1,
90511c0d8fdSPaul Kocialkowski 		.inc_x_even			= 1,
90611c0d8fdSPaul Kocialkowski 		.inc_y_odd			= 1,
90711c0d8fdSPaul Kocialkowski 		.inc_y_even			= 1,
90811c0d8fdSPaul Kocialkowski 
90911c0d8fdSPaul Kocialkowski 		/* VFIFO */
91011c0d8fdSPaul Kocialkowski 		.vfifo_read_start		= 16,
91111c0d8fdSPaul Kocialkowski 
91211c0d8fdSPaul Kocialkowski 		.ablc_num			= 4,
91311c0d8fdSPaul Kocialkowski 		.zline_num			= 1,
91411c0d8fdSPaul Kocialkowski 
91511c0d8fdSPaul Kocialkowski 		/* Black Level */
91611c0d8fdSPaul Kocialkowski 
91711c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_start	= 0,
91811c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_num		= 2,
91911c0d8fdSPaul Kocialkowski 		.blc_top_black_line_start	= 4,
92011c0d8fdSPaul Kocialkowski 		.blc_top_black_line_num		= 4,
92111c0d8fdSPaul Kocialkowski 
92211c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_start	= 2,
92311c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_num	= 2,
92411c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_start	= 8,
92511c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_num	= 2,
92611c0d8fdSPaul Kocialkowski 
92711c0d8fdSPaul Kocialkowski 		.blc_anchor_left_start		= 576,
92811c0d8fdSPaul Kocialkowski 		.blc_anchor_left_end		= 831,
92911c0d8fdSPaul Kocialkowski 		.blc_anchor_right_start		= 1984,
93011c0d8fdSPaul Kocialkowski 		.blc_anchor_right_end		= 2239,
93111c0d8fdSPaul Kocialkowski 
93211c0d8fdSPaul Kocialkowski 		/* Frame Interval */
93311c0d8fdSPaul Kocialkowski 		.frame_interval			= { 1, 30 },
93411c0d8fdSPaul Kocialkowski 
93511c0d8fdSPaul Kocialkowski 		/* PLL */
93611c0d8fdSPaul Kocialkowski 		.pll1_config			= &ov8865_pll1_config_native,
93711c0d8fdSPaul Kocialkowski 		.pll2_config			= &ov8865_pll2_config_native,
93811c0d8fdSPaul Kocialkowski 		.sclk_config			= &ov8865_sclk_config_native,
93911c0d8fdSPaul Kocialkowski 
94011c0d8fdSPaul Kocialkowski 		/* Registers */
94111c0d8fdSPaul Kocialkowski 		.register_values	= ov8865_register_values_native,
94211c0d8fdSPaul Kocialkowski 		.register_values_count	=
94311c0d8fdSPaul Kocialkowski 			ARRAY_SIZE(ov8865_register_values_native),
94411c0d8fdSPaul Kocialkowski 	},
94511c0d8fdSPaul Kocialkowski 	/* 3264x1836 */
94611c0d8fdSPaul Kocialkowski 	{
94711c0d8fdSPaul Kocialkowski 		/* Horizontal */
94811c0d8fdSPaul Kocialkowski 		.output_size_x			= 3264,
94911c0d8fdSPaul Kocialkowski 		.hts				= 2582,
95011c0d8fdSPaul Kocialkowski 
95111c0d8fdSPaul Kocialkowski 		/* Vertical */
95211c0d8fdSPaul Kocialkowski 		.output_size_y			= 1836,
95311c0d8fdSPaul Kocialkowski 		.vts				= 2002,
95411c0d8fdSPaul Kocialkowski 
95511c0d8fdSPaul Kocialkowski 		.size_auto			= true,
95611c0d8fdSPaul Kocialkowski 		.size_auto_boundary_x		= 8,
95711c0d8fdSPaul Kocialkowski 		.size_auto_boundary_y		= 4,
95811c0d8fdSPaul Kocialkowski 
95911c0d8fdSPaul Kocialkowski 		/* Subsample increase */
96011c0d8fdSPaul Kocialkowski 		.inc_x_odd			= 1,
96111c0d8fdSPaul Kocialkowski 		.inc_x_even			= 1,
96211c0d8fdSPaul Kocialkowski 		.inc_y_odd			= 1,
96311c0d8fdSPaul Kocialkowski 		.inc_y_even			= 1,
96411c0d8fdSPaul Kocialkowski 
96511c0d8fdSPaul Kocialkowski 		/* VFIFO */
96611c0d8fdSPaul Kocialkowski 		.vfifo_read_start		= 16,
96711c0d8fdSPaul Kocialkowski 
96811c0d8fdSPaul Kocialkowski 		.ablc_num			= 4,
96911c0d8fdSPaul Kocialkowski 		.zline_num			= 1,
97011c0d8fdSPaul Kocialkowski 
97111c0d8fdSPaul Kocialkowski 		/* Black Level */
97211c0d8fdSPaul Kocialkowski 
97311c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_start	= 0,
97411c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_num		= 2,
97511c0d8fdSPaul Kocialkowski 		.blc_top_black_line_start	= 4,
97611c0d8fdSPaul Kocialkowski 		.blc_top_black_line_num		= 4,
97711c0d8fdSPaul Kocialkowski 
97811c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_start	= 2,
97911c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_num	= 2,
98011c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_start	= 8,
98111c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_num	= 2,
98211c0d8fdSPaul Kocialkowski 
98311c0d8fdSPaul Kocialkowski 		.blc_anchor_left_start		= 576,
98411c0d8fdSPaul Kocialkowski 		.blc_anchor_left_end		= 831,
98511c0d8fdSPaul Kocialkowski 		.blc_anchor_right_start		= 1984,
98611c0d8fdSPaul Kocialkowski 		.blc_anchor_right_end		= 2239,
98711c0d8fdSPaul Kocialkowski 
98811c0d8fdSPaul Kocialkowski 		/* Frame Interval */
98911c0d8fdSPaul Kocialkowski 		.frame_interval			= { 1, 30 },
99011c0d8fdSPaul Kocialkowski 
99111c0d8fdSPaul Kocialkowski 		/* PLL */
99211c0d8fdSPaul Kocialkowski 		.pll1_config			= &ov8865_pll1_config_native,
99311c0d8fdSPaul Kocialkowski 		.pll2_config			= &ov8865_pll2_config_native,
99411c0d8fdSPaul Kocialkowski 		.sclk_config			= &ov8865_sclk_config_native,
99511c0d8fdSPaul Kocialkowski 
99611c0d8fdSPaul Kocialkowski 		/* Registers */
99711c0d8fdSPaul Kocialkowski 		.register_values	= ov8865_register_values_native,
99811c0d8fdSPaul Kocialkowski 		.register_values_count	=
99911c0d8fdSPaul Kocialkowski 			ARRAY_SIZE(ov8865_register_values_native),
100011c0d8fdSPaul Kocialkowski 	},
100111c0d8fdSPaul Kocialkowski 	/* 1632x1224 */
100211c0d8fdSPaul Kocialkowski 	{
100311c0d8fdSPaul Kocialkowski 		/* Horizontal */
100411c0d8fdSPaul Kocialkowski 		.output_size_x			= 1632,
100511c0d8fdSPaul Kocialkowski 		.hts				= 1923,
100611c0d8fdSPaul Kocialkowski 
100711c0d8fdSPaul Kocialkowski 		/* Vertical */
100811c0d8fdSPaul Kocialkowski 		.output_size_y			= 1224,
100911c0d8fdSPaul Kocialkowski 		.vts				= 1248,
101011c0d8fdSPaul Kocialkowski 
101111c0d8fdSPaul Kocialkowski 		.size_auto			= true,
101211c0d8fdSPaul Kocialkowski 		.size_auto_boundary_x		= 8,
101311c0d8fdSPaul Kocialkowski 		.size_auto_boundary_y		= 8,
101411c0d8fdSPaul Kocialkowski 
101511c0d8fdSPaul Kocialkowski 		/* Subsample increase */
101611c0d8fdSPaul Kocialkowski 		.inc_x_odd			= 3,
101711c0d8fdSPaul Kocialkowski 		.inc_x_even			= 1,
101811c0d8fdSPaul Kocialkowski 		.inc_y_odd			= 3,
101911c0d8fdSPaul Kocialkowski 		.inc_y_even			= 1,
102011c0d8fdSPaul Kocialkowski 
102111c0d8fdSPaul Kocialkowski 		/* Binning */
102211c0d8fdSPaul Kocialkowski 		.binning_y			= true,
102311c0d8fdSPaul Kocialkowski 		.sync_hbin			= true,
102411c0d8fdSPaul Kocialkowski 
102511c0d8fdSPaul Kocialkowski 		/* VFIFO */
102611c0d8fdSPaul Kocialkowski 		.vfifo_read_start		= 116,
102711c0d8fdSPaul Kocialkowski 
102811c0d8fdSPaul Kocialkowski 		.ablc_num			= 8,
102911c0d8fdSPaul Kocialkowski 		.zline_num			= 2,
103011c0d8fdSPaul Kocialkowski 
103111c0d8fdSPaul Kocialkowski 		/* Black Level */
103211c0d8fdSPaul Kocialkowski 
103311c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_start	= 0,
103411c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_num		= 2,
103511c0d8fdSPaul Kocialkowski 		.blc_top_black_line_start	= 4,
103611c0d8fdSPaul Kocialkowski 		.blc_top_black_line_num		= 4,
103711c0d8fdSPaul Kocialkowski 
103811c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_start	= 2,
103911c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_num	= 2,
104011c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_start	= 8,
104111c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_num	= 2,
104211c0d8fdSPaul Kocialkowski 
104311c0d8fdSPaul Kocialkowski 		.blc_anchor_left_start		= 288,
104411c0d8fdSPaul Kocialkowski 		.blc_anchor_left_end		= 415,
104511c0d8fdSPaul Kocialkowski 		.blc_anchor_right_start		= 992,
104611c0d8fdSPaul Kocialkowski 		.blc_anchor_right_end		= 1119,
104711c0d8fdSPaul Kocialkowski 
104811c0d8fdSPaul Kocialkowski 		/* Frame Interval */
104911c0d8fdSPaul Kocialkowski 		.frame_interval			= { 1, 30 },
105011c0d8fdSPaul Kocialkowski 
105111c0d8fdSPaul Kocialkowski 		/* PLL */
105211c0d8fdSPaul Kocialkowski 		.pll1_config			= &ov8865_pll1_config_native,
105311c0d8fdSPaul Kocialkowski 		.pll2_config			= &ov8865_pll2_config_binning,
105411c0d8fdSPaul Kocialkowski 		.sclk_config			= &ov8865_sclk_config_native,
105511c0d8fdSPaul Kocialkowski 
105611c0d8fdSPaul Kocialkowski 		/* Registers */
105711c0d8fdSPaul Kocialkowski 		.register_values	= ov8865_register_values_binning,
105811c0d8fdSPaul Kocialkowski 		.register_values_count	=
105911c0d8fdSPaul Kocialkowski 			ARRAY_SIZE(ov8865_register_values_binning),
106011c0d8fdSPaul Kocialkowski 	},
106111c0d8fdSPaul Kocialkowski 	/* 800x600 (SVGA) */
106211c0d8fdSPaul Kocialkowski 	{
106311c0d8fdSPaul Kocialkowski 		/* Horizontal */
106411c0d8fdSPaul Kocialkowski 		.output_size_x			= 800,
106511c0d8fdSPaul Kocialkowski 		.hts				= 1250,
106611c0d8fdSPaul Kocialkowski 
106711c0d8fdSPaul Kocialkowski 		/* Vertical */
106811c0d8fdSPaul Kocialkowski 		.output_size_y			= 600,
106911c0d8fdSPaul Kocialkowski 		.vts				= 640,
107011c0d8fdSPaul Kocialkowski 
107111c0d8fdSPaul Kocialkowski 		.size_auto			= true,
107211c0d8fdSPaul Kocialkowski 		.size_auto_boundary_x		= 8,
107311c0d8fdSPaul Kocialkowski 		.size_auto_boundary_y		= 8,
107411c0d8fdSPaul Kocialkowski 
107511c0d8fdSPaul Kocialkowski 		/* Subsample increase */
107611c0d8fdSPaul Kocialkowski 		.inc_x_odd			= 3,
107711c0d8fdSPaul Kocialkowski 		.inc_x_even			= 1,
107811c0d8fdSPaul Kocialkowski 		.inc_y_odd			= 5,
107911c0d8fdSPaul Kocialkowski 		.inc_y_even			= 3,
108011c0d8fdSPaul Kocialkowski 
108111c0d8fdSPaul Kocialkowski 		/* Binning */
108211c0d8fdSPaul Kocialkowski 		.binning_y			= true,
108311c0d8fdSPaul Kocialkowski 		.variopixel			= true,
108411c0d8fdSPaul Kocialkowski 		.variopixel_hsub_coef		= 2,
108511c0d8fdSPaul Kocialkowski 		.variopixel_vsub_coef		= 1,
108611c0d8fdSPaul Kocialkowski 		.sync_hbin			= true,
108711c0d8fdSPaul Kocialkowski 		.horz_var2			= true,
108811c0d8fdSPaul Kocialkowski 
108911c0d8fdSPaul Kocialkowski 		/* VFIFO */
109011c0d8fdSPaul Kocialkowski 		.vfifo_read_start		= 80,
109111c0d8fdSPaul Kocialkowski 
109211c0d8fdSPaul Kocialkowski 		.ablc_num			= 8,
109311c0d8fdSPaul Kocialkowski 		.zline_num			= 2,
109411c0d8fdSPaul Kocialkowski 
109511c0d8fdSPaul Kocialkowski 		/* Black Level */
109611c0d8fdSPaul Kocialkowski 
109711c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_start	= 0,
109811c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_num		= 2,
109911c0d8fdSPaul Kocialkowski 		.blc_top_black_line_start	= 2,
110011c0d8fdSPaul Kocialkowski 		.blc_top_black_line_num		= 2,
110111c0d8fdSPaul Kocialkowski 
110211c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_start	= 0,
110311c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_num	= 0,
110411c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_start	= 4,
110511c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_num	= 2,
110611c0d8fdSPaul Kocialkowski 
110711c0d8fdSPaul Kocialkowski 		.blc_col_shift_mask	= OV8865_BLC_CTRL1_COL_SHIFT_128,
110811c0d8fdSPaul Kocialkowski 
110911c0d8fdSPaul Kocialkowski 		.blc_anchor_left_start		= 288,
111011c0d8fdSPaul Kocialkowski 		.blc_anchor_left_end		= 415,
111111c0d8fdSPaul Kocialkowski 		.blc_anchor_right_start		= 992,
111211c0d8fdSPaul Kocialkowski 		.blc_anchor_right_end		= 1119,
111311c0d8fdSPaul Kocialkowski 
111411c0d8fdSPaul Kocialkowski 		/* Frame Interval */
111511c0d8fdSPaul Kocialkowski 		.frame_interval			= { 1, 90 },
111611c0d8fdSPaul Kocialkowski 
111711c0d8fdSPaul Kocialkowski 		/* PLL */
111811c0d8fdSPaul Kocialkowski 		.pll1_config			= &ov8865_pll1_config_native,
111911c0d8fdSPaul Kocialkowski 		.pll2_config			= &ov8865_pll2_config_binning,
112011c0d8fdSPaul Kocialkowski 		.sclk_config			= &ov8865_sclk_config_native,
112111c0d8fdSPaul Kocialkowski 
112211c0d8fdSPaul Kocialkowski 		/* Registers */
112311c0d8fdSPaul Kocialkowski 		.register_values	= ov8865_register_values_binning,
112411c0d8fdSPaul Kocialkowski 		.register_values_count	=
112511c0d8fdSPaul Kocialkowski 			ARRAY_SIZE(ov8865_register_values_binning),
112611c0d8fdSPaul Kocialkowski 	},
112711c0d8fdSPaul Kocialkowski };
112811c0d8fdSPaul Kocialkowski 
112911c0d8fdSPaul Kocialkowski static const u32 ov8865_mbus_codes[] = {
113011c0d8fdSPaul Kocialkowski 	MEDIA_BUS_FMT_SBGGR10_1X10,
113111c0d8fdSPaul Kocialkowski };
113211c0d8fdSPaul Kocialkowski 
113311c0d8fdSPaul Kocialkowski static const struct ov8865_register_value ov8865_init_sequence[] = {
113411c0d8fdSPaul Kocialkowski 	/* Analog */
113511c0d8fdSPaul Kocialkowski 
113611c0d8fdSPaul Kocialkowski 	{ 0x3604, 0x04 },
113711c0d8fdSPaul Kocialkowski 	{ 0x3602, 0x30 },
113811c0d8fdSPaul Kocialkowski 	{ 0x3605, 0x00 },
113911c0d8fdSPaul Kocialkowski 	{ 0x3607, 0x20 },
114011c0d8fdSPaul Kocialkowski 	{ 0x3608, 0x11 },
114111c0d8fdSPaul Kocialkowski 	{ 0x3609, 0x68 },
114211c0d8fdSPaul Kocialkowski 	{ 0x360a, 0x40 },
114311c0d8fdSPaul Kocialkowski 	{ 0x360c, 0xdd },
114411c0d8fdSPaul Kocialkowski 	{ 0x360e, 0x0c },
114511c0d8fdSPaul Kocialkowski 	{ 0x3610, 0x07 },
114611c0d8fdSPaul Kocialkowski 	{ 0x3612, 0x86 },
114711c0d8fdSPaul Kocialkowski 	{ 0x3613, 0x58 },
114811c0d8fdSPaul Kocialkowski 	{ 0x3614, 0x28 },
114911c0d8fdSPaul Kocialkowski 	{ 0x3617, 0x40 },
115011c0d8fdSPaul Kocialkowski 	{ 0x3618, 0x5a },
115111c0d8fdSPaul Kocialkowski 	{ 0x3619, 0x9b },
115211c0d8fdSPaul Kocialkowski 	{ 0x361c, 0x00 },
115311c0d8fdSPaul Kocialkowski 	{ 0x361d, 0x60 },
115411c0d8fdSPaul Kocialkowski 	{ 0x3631, 0x60 },
115511c0d8fdSPaul Kocialkowski 	{ 0x3633, 0x10 },
115611c0d8fdSPaul Kocialkowski 	{ 0x3634, 0x10 },
115711c0d8fdSPaul Kocialkowski 	{ 0x3635, 0x10 },
115811c0d8fdSPaul Kocialkowski 	{ 0x3636, 0x10 },
115911c0d8fdSPaul Kocialkowski 	{ 0x3638, 0xff },
116011c0d8fdSPaul Kocialkowski 	{ 0x3641, 0x55 },
116111c0d8fdSPaul Kocialkowski 	{ 0x3646, 0x86 },
116211c0d8fdSPaul Kocialkowski 	{ 0x3647, 0x27 },
116311c0d8fdSPaul Kocialkowski 	{ 0x364a, 0x1b },
116411c0d8fdSPaul Kocialkowski 
116511c0d8fdSPaul Kocialkowski 	/* Sensor */
116611c0d8fdSPaul Kocialkowski 
116711c0d8fdSPaul Kocialkowski 	{ 0x3700, 0x24 },
116811c0d8fdSPaul Kocialkowski 	{ 0x3701, 0x0c },
116911c0d8fdSPaul Kocialkowski 	{ 0x3702, 0x28 },
117011c0d8fdSPaul Kocialkowski 	{ 0x3703, 0x19 },
117111c0d8fdSPaul Kocialkowski 	{ 0x3704, 0x14 },
117211c0d8fdSPaul Kocialkowski 	{ 0x3705, 0x00 },
117311c0d8fdSPaul Kocialkowski 	{ 0x3706, 0x38 },
117411c0d8fdSPaul Kocialkowski 	{ 0x3707, 0x04 },
117511c0d8fdSPaul Kocialkowski 	{ 0x3708, 0x24 },
117611c0d8fdSPaul Kocialkowski 	{ 0x3709, 0x40 },
117711c0d8fdSPaul Kocialkowski 	{ 0x370a, 0x00 },
117811c0d8fdSPaul Kocialkowski 	{ 0x370b, 0xb8 },
117911c0d8fdSPaul Kocialkowski 	{ 0x370c, 0x04 },
118011c0d8fdSPaul Kocialkowski 	{ 0x3718, 0x12 },
118111c0d8fdSPaul Kocialkowski 	{ 0x3719, 0x31 },
118211c0d8fdSPaul Kocialkowski 	{ 0x3712, 0x42 },
118311c0d8fdSPaul Kocialkowski 	{ 0x3714, 0x12 },
118411c0d8fdSPaul Kocialkowski 	{ 0x371e, 0x19 },
118511c0d8fdSPaul Kocialkowski 	{ 0x371f, 0x40 },
118611c0d8fdSPaul Kocialkowski 	{ 0x3720, 0x05 },
118711c0d8fdSPaul Kocialkowski 	{ 0x3721, 0x05 },
118811c0d8fdSPaul Kocialkowski 	{ 0x3724, 0x02 },
118911c0d8fdSPaul Kocialkowski 	{ 0x3725, 0x02 },
119011c0d8fdSPaul Kocialkowski 	{ 0x3726, 0x06 },
119111c0d8fdSPaul Kocialkowski 	{ 0x3728, 0x05 },
119211c0d8fdSPaul Kocialkowski 	{ 0x3729, 0x02 },
119311c0d8fdSPaul Kocialkowski 	{ 0x372a, 0x03 },
119411c0d8fdSPaul Kocialkowski 	{ 0x372b, 0x53 },
119511c0d8fdSPaul Kocialkowski 	{ 0x372c, 0xa3 },
119611c0d8fdSPaul Kocialkowski 	{ 0x372d, 0x53 },
119711c0d8fdSPaul Kocialkowski 	{ 0x372e, 0x06 },
119811c0d8fdSPaul Kocialkowski 	{ 0x372f, 0x10 },
119911c0d8fdSPaul Kocialkowski 	{ 0x3730, 0x01 },
120011c0d8fdSPaul Kocialkowski 	{ 0x3731, 0x06 },
120111c0d8fdSPaul Kocialkowski 	{ 0x3732, 0x14 },
120211c0d8fdSPaul Kocialkowski 	{ 0x3733, 0x10 },
120311c0d8fdSPaul Kocialkowski 	{ 0x3734, 0x40 },
120411c0d8fdSPaul Kocialkowski 	{ 0x3736, 0x20 },
120511c0d8fdSPaul Kocialkowski 	{ 0x373a, 0x02 },
120611c0d8fdSPaul Kocialkowski 	{ 0x373b, 0x0c },
120711c0d8fdSPaul Kocialkowski 	{ 0x373c, 0x0a },
120811c0d8fdSPaul Kocialkowski 	{ 0x373e, 0x03 },
120911c0d8fdSPaul Kocialkowski 	{ 0x3755, 0x40 },
121011c0d8fdSPaul Kocialkowski 	{ 0x3758, 0x00 },
121111c0d8fdSPaul Kocialkowski 	{ 0x3759, 0x4c },
121211c0d8fdSPaul Kocialkowski 	{ 0x375a, 0x06 },
121311c0d8fdSPaul Kocialkowski 	{ 0x375b, 0x13 },
121411c0d8fdSPaul Kocialkowski 	{ 0x375c, 0x40 },
121511c0d8fdSPaul Kocialkowski 	{ 0x375d, 0x02 },
121611c0d8fdSPaul Kocialkowski 	{ 0x375e, 0x00 },
121711c0d8fdSPaul Kocialkowski 	{ 0x375f, 0x14 },
121811c0d8fdSPaul Kocialkowski 	{ 0x3767, 0x1c },
121911c0d8fdSPaul Kocialkowski 	{ 0x3768, 0x04 },
122011c0d8fdSPaul Kocialkowski 	{ 0x3769, 0x20 },
122111c0d8fdSPaul Kocialkowski 	{ 0x376c, 0xc0 },
122211c0d8fdSPaul Kocialkowski 	{ 0x376d, 0xc0 },
122311c0d8fdSPaul Kocialkowski 	{ 0x376a, 0x08 },
122411c0d8fdSPaul Kocialkowski 	{ 0x3761, 0x00 },
122511c0d8fdSPaul Kocialkowski 	{ 0x3762, 0x00 },
122611c0d8fdSPaul Kocialkowski 	{ 0x3763, 0x00 },
122711c0d8fdSPaul Kocialkowski 	{ 0x3766, 0xff },
122811c0d8fdSPaul Kocialkowski 	{ 0x376b, 0x42 },
122911c0d8fdSPaul Kocialkowski 	{ 0x3772, 0x23 },
123011c0d8fdSPaul Kocialkowski 	{ 0x3773, 0x02 },
123111c0d8fdSPaul Kocialkowski 	{ 0x3774, 0x16 },
123211c0d8fdSPaul Kocialkowski 	{ 0x3775, 0x12 },
123311c0d8fdSPaul Kocialkowski 	{ 0x3776, 0x08 },
123411c0d8fdSPaul Kocialkowski 	{ 0x37a0, 0x44 },
123511c0d8fdSPaul Kocialkowski 	{ 0x37a1, 0x3d },
123611c0d8fdSPaul Kocialkowski 	{ 0x37a2, 0x3d },
123711c0d8fdSPaul Kocialkowski 	{ 0x37a3, 0x01 },
123811c0d8fdSPaul Kocialkowski 	{ 0x37a4, 0x00 },
123911c0d8fdSPaul Kocialkowski 	{ 0x37a5, 0x08 },
124011c0d8fdSPaul Kocialkowski 	{ 0x37a6, 0x00 },
124111c0d8fdSPaul Kocialkowski 	{ 0x37a7, 0x44 },
124211c0d8fdSPaul Kocialkowski 	{ 0x37a8, 0x58 },
124311c0d8fdSPaul Kocialkowski 	{ 0x37a9, 0x58 },
124411c0d8fdSPaul Kocialkowski 	{ 0x3760, 0x00 },
124511c0d8fdSPaul Kocialkowski 	{ 0x376f, 0x01 },
124611c0d8fdSPaul Kocialkowski 	{ 0x37aa, 0x44 },
124711c0d8fdSPaul Kocialkowski 	{ 0x37ab, 0x2e },
124811c0d8fdSPaul Kocialkowski 	{ 0x37ac, 0x2e },
124911c0d8fdSPaul Kocialkowski 	{ 0x37ad, 0x33 },
125011c0d8fdSPaul Kocialkowski 	{ 0x37ae, 0x0d },
125111c0d8fdSPaul Kocialkowski 	{ 0x37af, 0x0d },
125211c0d8fdSPaul Kocialkowski 	{ 0x37b0, 0x00 },
125311c0d8fdSPaul Kocialkowski 	{ 0x37b1, 0x00 },
125411c0d8fdSPaul Kocialkowski 	{ 0x37b2, 0x00 },
125511c0d8fdSPaul Kocialkowski 	{ 0x37b3, 0x42 },
125611c0d8fdSPaul Kocialkowski 	{ 0x37b4, 0x42 },
125711c0d8fdSPaul Kocialkowski 	{ 0x37b5, 0x33 },
125811c0d8fdSPaul Kocialkowski 	{ 0x37b6, 0x00 },
125911c0d8fdSPaul Kocialkowski 	{ 0x37b7, 0x00 },
126011c0d8fdSPaul Kocialkowski 	{ 0x37b8, 0x00 },
126111c0d8fdSPaul Kocialkowski 	{ 0x37b9, 0xff },
126211c0d8fdSPaul Kocialkowski 
126311c0d8fdSPaul Kocialkowski 	/* ADC Sync */
126411c0d8fdSPaul Kocialkowski 
126511c0d8fdSPaul Kocialkowski 	{ 0x4503, 0x10 },
126611c0d8fdSPaul Kocialkowski };
126711c0d8fdSPaul Kocialkowski 
126811c0d8fdSPaul Kocialkowski static const s64 ov8865_link_freq_menu[] = {
126911c0d8fdSPaul Kocialkowski 	360000000,
127011c0d8fdSPaul Kocialkowski };
127111c0d8fdSPaul Kocialkowski 
127211c0d8fdSPaul Kocialkowski static const char *const ov8865_test_pattern_menu[] = {
127311c0d8fdSPaul Kocialkowski 	"Disabled",
127411c0d8fdSPaul Kocialkowski 	"Random data",
127511c0d8fdSPaul Kocialkowski 	"Color bars",
127611c0d8fdSPaul Kocialkowski 	"Color bars with rolling bar",
127711c0d8fdSPaul Kocialkowski 	"Color squares",
127811c0d8fdSPaul Kocialkowski 	"Color squares with rolling bar"
127911c0d8fdSPaul Kocialkowski };
128011c0d8fdSPaul Kocialkowski 
128111c0d8fdSPaul Kocialkowski static const u8 ov8865_test_pattern_bits[] = {
128211c0d8fdSPaul Kocialkowski 	0,
128311c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA,
128411c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_BARS,
128511c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN |
128611c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_COLOR_BARS,
128711c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES,
128811c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN |
128911c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES,
129011c0d8fdSPaul Kocialkowski };
129111c0d8fdSPaul Kocialkowski 
129211c0d8fdSPaul Kocialkowski /* Input/Output */
129311c0d8fdSPaul Kocialkowski 
129411c0d8fdSPaul Kocialkowski static int ov8865_read(struct ov8865_sensor *sensor, u16 address, u8 *value)
129511c0d8fdSPaul Kocialkowski {
129611c0d8fdSPaul Kocialkowski 	unsigned char data[2] = { address >> 8, address & 0xff };
129711c0d8fdSPaul Kocialkowski 	struct i2c_client *client = sensor->i2c_client;
129811c0d8fdSPaul Kocialkowski 	int ret;
129911c0d8fdSPaul Kocialkowski 
130011c0d8fdSPaul Kocialkowski 	ret = i2c_master_send(client, data, sizeof(data));
130111c0d8fdSPaul Kocialkowski 	if (ret < 0) {
130211c0d8fdSPaul Kocialkowski 		dev_dbg(&client->dev, "i2c send error at address %#04x\n",
130311c0d8fdSPaul Kocialkowski 			address);
130411c0d8fdSPaul Kocialkowski 		return ret;
130511c0d8fdSPaul Kocialkowski 	}
130611c0d8fdSPaul Kocialkowski 
130711c0d8fdSPaul Kocialkowski 	ret = i2c_master_recv(client, value, 1);
130811c0d8fdSPaul Kocialkowski 	if (ret < 0) {
130911c0d8fdSPaul Kocialkowski 		dev_dbg(&client->dev, "i2c recv error at address %#04x\n",
131011c0d8fdSPaul Kocialkowski 			address);
131111c0d8fdSPaul Kocialkowski 		return ret;
131211c0d8fdSPaul Kocialkowski 	}
131311c0d8fdSPaul Kocialkowski 
131411c0d8fdSPaul Kocialkowski 	return 0;
131511c0d8fdSPaul Kocialkowski }
131611c0d8fdSPaul Kocialkowski 
131711c0d8fdSPaul Kocialkowski static int ov8865_write(struct ov8865_sensor *sensor, u16 address, u8 value)
131811c0d8fdSPaul Kocialkowski {
131911c0d8fdSPaul Kocialkowski 	unsigned char data[3] = { address >> 8, address & 0xff, value };
132011c0d8fdSPaul Kocialkowski 	struct i2c_client *client = sensor->i2c_client;
132111c0d8fdSPaul Kocialkowski 	int ret;
132211c0d8fdSPaul Kocialkowski 
132311c0d8fdSPaul Kocialkowski 	ret = i2c_master_send(client, data, sizeof(data));
132411c0d8fdSPaul Kocialkowski 	if (ret < 0) {
132511c0d8fdSPaul Kocialkowski 		dev_dbg(&client->dev, "i2c send error at address %#04x\n",
132611c0d8fdSPaul Kocialkowski 			address);
132711c0d8fdSPaul Kocialkowski 		return ret;
132811c0d8fdSPaul Kocialkowski 	}
132911c0d8fdSPaul Kocialkowski 
133011c0d8fdSPaul Kocialkowski 	return 0;
133111c0d8fdSPaul Kocialkowski }
133211c0d8fdSPaul Kocialkowski 
133311c0d8fdSPaul Kocialkowski static int ov8865_write_sequence(struct ov8865_sensor *sensor,
133411c0d8fdSPaul Kocialkowski 				 const struct ov8865_register_value *sequence,
133511c0d8fdSPaul Kocialkowski 				 unsigned int sequence_count)
133611c0d8fdSPaul Kocialkowski {
133711c0d8fdSPaul Kocialkowski 	unsigned int i;
133811c0d8fdSPaul Kocialkowski 	int ret = 0;
133911c0d8fdSPaul Kocialkowski 
134011c0d8fdSPaul Kocialkowski 	for (i = 0; i < sequence_count; i++) {
134111c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, sequence[i].address,
134211c0d8fdSPaul Kocialkowski 				   sequence[i].value);
134311c0d8fdSPaul Kocialkowski 		if (ret)
134411c0d8fdSPaul Kocialkowski 			break;
134511c0d8fdSPaul Kocialkowski 
134611c0d8fdSPaul Kocialkowski 		if (sequence[i].delay_ms)
134711c0d8fdSPaul Kocialkowski 			msleep(sequence[i].delay_ms);
134811c0d8fdSPaul Kocialkowski 	}
134911c0d8fdSPaul Kocialkowski 
135011c0d8fdSPaul Kocialkowski 	return ret;
135111c0d8fdSPaul Kocialkowski }
135211c0d8fdSPaul Kocialkowski 
135311c0d8fdSPaul Kocialkowski static int ov8865_update_bits(struct ov8865_sensor *sensor, u16 address,
135411c0d8fdSPaul Kocialkowski 			      u8 mask, u8 bits)
135511c0d8fdSPaul Kocialkowski {
135611c0d8fdSPaul Kocialkowski 	u8 value = 0;
135711c0d8fdSPaul Kocialkowski 	int ret;
135811c0d8fdSPaul Kocialkowski 
135911c0d8fdSPaul Kocialkowski 	ret = ov8865_read(sensor, address, &value);
136011c0d8fdSPaul Kocialkowski 	if (ret)
136111c0d8fdSPaul Kocialkowski 		return ret;
136211c0d8fdSPaul Kocialkowski 
136311c0d8fdSPaul Kocialkowski 	value &= ~mask;
136411c0d8fdSPaul Kocialkowski 	value |= bits;
136511c0d8fdSPaul Kocialkowski 
136611c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, address, value);
136711c0d8fdSPaul Kocialkowski }
136811c0d8fdSPaul Kocialkowski 
136911c0d8fdSPaul Kocialkowski /* Sensor */
137011c0d8fdSPaul Kocialkowski 
137111c0d8fdSPaul Kocialkowski static int ov8865_sw_reset(struct ov8865_sensor *sensor)
137211c0d8fdSPaul Kocialkowski {
137311c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_SW_RESET_REG, OV8865_SW_RESET_RESET);
137411c0d8fdSPaul Kocialkowski }
137511c0d8fdSPaul Kocialkowski 
137611c0d8fdSPaul Kocialkowski static int ov8865_sw_standby(struct ov8865_sensor *sensor, int standby)
137711c0d8fdSPaul Kocialkowski {
137811c0d8fdSPaul Kocialkowski 	u8 value = 0;
137911c0d8fdSPaul Kocialkowski 
138011c0d8fdSPaul Kocialkowski 	if (!standby)
138111c0d8fdSPaul Kocialkowski 		value = OV8865_SW_STANDBY_STREAM_ON;
138211c0d8fdSPaul Kocialkowski 
138311c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_SW_STANDBY_REG, value);
138411c0d8fdSPaul Kocialkowski }
138511c0d8fdSPaul Kocialkowski 
138611c0d8fdSPaul Kocialkowski static int ov8865_chip_id_check(struct ov8865_sensor *sensor)
138711c0d8fdSPaul Kocialkowski {
138811c0d8fdSPaul Kocialkowski 	u16 regs[] = { OV8865_CHIP_ID_HH_REG, OV8865_CHIP_ID_H_REG,
138911c0d8fdSPaul Kocialkowski 		       OV8865_CHIP_ID_L_REG };
139011c0d8fdSPaul Kocialkowski 	u8 values[] = { OV8865_CHIP_ID_HH_VALUE, OV8865_CHIP_ID_H_VALUE,
139111c0d8fdSPaul Kocialkowski 			OV8865_CHIP_ID_L_VALUE };
139211c0d8fdSPaul Kocialkowski 	unsigned int i;
139311c0d8fdSPaul Kocialkowski 	u8 value;
139411c0d8fdSPaul Kocialkowski 	int ret;
139511c0d8fdSPaul Kocialkowski 
139611c0d8fdSPaul Kocialkowski 	for (i = 0; i < ARRAY_SIZE(regs); i++) {
139711c0d8fdSPaul Kocialkowski 		ret = ov8865_read(sensor, regs[i], &value);
139811c0d8fdSPaul Kocialkowski 		if (ret < 0)
139911c0d8fdSPaul Kocialkowski 			return ret;
140011c0d8fdSPaul Kocialkowski 
140111c0d8fdSPaul Kocialkowski 		if (value != values[i]) {
140211c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev,
140311c0d8fdSPaul Kocialkowski 				"chip id value mismatch: %#x instead of %#x\n",
140411c0d8fdSPaul Kocialkowski 				value, values[i]);
140511c0d8fdSPaul Kocialkowski 			return -EINVAL;
140611c0d8fdSPaul Kocialkowski 		}
140711c0d8fdSPaul Kocialkowski 	}
140811c0d8fdSPaul Kocialkowski 
140911c0d8fdSPaul Kocialkowski 	return 0;
141011c0d8fdSPaul Kocialkowski }
141111c0d8fdSPaul Kocialkowski 
141211c0d8fdSPaul Kocialkowski static int ov8865_charge_pump_configure(struct ov8865_sensor *sensor)
141311c0d8fdSPaul Kocialkowski {
141411c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_PUMP_CLK_DIV_REG,
141511c0d8fdSPaul Kocialkowski 			    OV8865_PUMP_CLK_DIV_PUMP_P(1));
141611c0d8fdSPaul Kocialkowski }
141711c0d8fdSPaul Kocialkowski 
141811c0d8fdSPaul Kocialkowski static int ov8865_mipi_configure(struct ov8865_sensor *sensor)
141911c0d8fdSPaul Kocialkowski {
142011c0d8fdSPaul Kocialkowski 	struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 =
142111c0d8fdSPaul Kocialkowski 		&sensor->endpoint.bus.mipi_csi2;
142211c0d8fdSPaul Kocialkowski 	unsigned int lanes_count = bus_mipi_csi2->num_data_lanes;
142311c0d8fdSPaul Kocialkowski 	int ret;
142411c0d8fdSPaul Kocialkowski 
142511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL0_REG,
142611c0d8fdSPaul Kocialkowski 			   OV8865_MIPI_SC_CTRL0_LANES(lanes_count) |
142711c0d8fdSPaul Kocialkowski 			   OV8865_MIPI_SC_CTRL0_MIPI_EN |
142811c0d8fdSPaul Kocialkowski 			   OV8865_MIPI_SC_CTRL0_UNKNOWN);
142911c0d8fdSPaul Kocialkowski 	if (ret)
143011c0d8fdSPaul Kocialkowski 		return ret;
143111c0d8fdSPaul Kocialkowski 
143211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL2_REG,
143311c0d8fdSPaul Kocialkowski 			   OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC);
143411c0d8fdSPaul Kocialkowski 	if (ret)
143511c0d8fdSPaul Kocialkowski 		return ret;
143611c0d8fdSPaul Kocialkowski 
143711c0d8fdSPaul Kocialkowski 	if (lanes_count >= 2) {
143811c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL01_REG,
143911c0d8fdSPaul Kocialkowski 				   OV8865_MIPI_LANE_SEL01_LANE0(0) |
144011c0d8fdSPaul Kocialkowski 				   OV8865_MIPI_LANE_SEL01_LANE1(1));
144111c0d8fdSPaul Kocialkowski 		if (ret)
144211c0d8fdSPaul Kocialkowski 			return ret;
144311c0d8fdSPaul Kocialkowski 	}
144411c0d8fdSPaul Kocialkowski 
144511c0d8fdSPaul Kocialkowski 	if (lanes_count >= 4) {
144611c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL23_REG,
144711c0d8fdSPaul Kocialkowski 				   OV8865_MIPI_LANE_SEL23_LANE2(2) |
144811c0d8fdSPaul Kocialkowski 				   OV8865_MIPI_LANE_SEL23_LANE3(3));
144911c0d8fdSPaul Kocialkowski 		if (ret)
145011c0d8fdSPaul Kocialkowski 			return ret;
145111c0d8fdSPaul Kocialkowski 	}
145211c0d8fdSPaul Kocialkowski 
145311c0d8fdSPaul Kocialkowski 	ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG,
145411c0d8fdSPaul Kocialkowski 				 OV8865_CLK_SEL1_MIPI_EOF,
145511c0d8fdSPaul Kocialkowski 				 OV8865_CLK_SEL1_MIPI_EOF);
145611c0d8fdSPaul Kocialkowski 	if (ret)
145711c0d8fdSPaul Kocialkowski 		return ret;
145811c0d8fdSPaul Kocialkowski 
145911c0d8fdSPaul Kocialkowski 	/*
146011c0d8fdSPaul Kocialkowski 	 * This value might need to change depending on PCLK rate,
146111c0d8fdSPaul Kocialkowski 	 * but it's unclear how. This value seems to generally work
146211c0d8fdSPaul Kocialkowski 	 * while the default value was found to cause transmission errors.
146311c0d8fdSPaul Kocialkowski 	 */
146411c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_MIPI_PCLK_PERIOD_REG, 0x16);
146511c0d8fdSPaul Kocialkowski }
146611c0d8fdSPaul Kocialkowski 
146711c0d8fdSPaul Kocialkowski static int ov8865_black_level_configure(struct ov8865_sensor *sensor)
146811c0d8fdSPaul Kocialkowski {
146911c0d8fdSPaul Kocialkowski 	int ret;
147011c0d8fdSPaul Kocialkowski 
147111c0d8fdSPaul Kocialkowski 	/* Trigger BLC on relevant events and enable filter. */
147211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_CTRL0_REG,
147311c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_TRIG_RANGE_EN |
147411c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_TRIG_FORMAT_EN |
147511c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_TRIG_GAIN_EN |
147611c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN |
147711c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_FILTER_EN);
147811c0d8fdSPaul Kocialkowski 	if (ret)
147911c0d8fdSPaul Kocialkowski 		return ret;
148011c0d8fdSPaul Kocialkowski 
148111c0d8fdSPaul Kocialkowski 	/* Lower BLC offset trigger threshold. */
148211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_CTRLD_REG,
148311c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRLD_OFFSET_TRIGGER(16));
148411c0d8fdSPaul Kocialkowski 	if (ret)
148511c0d8fdSPaul Kocialkowski 		return ret;
148611c0d8fdSPaul Kocialkowski 
148711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_CTRL1F_REG, 0);
148811c0d8fdSPaul Kocialkowski 	if (ret)
148911c0d8fdSPaul Kocialkowski 		return ret;
149011c0d8fdSPaul Kocialkowski 
149111c0d8fdSPaul Kocialkowski 	/* Increase BLC offset maximum limit. */
149211c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_BLC_OFFSET_LIMIT_REG,
149311c0d8fdSPaul Kocialkowski 			    OV8865_BLC_OFFSET_LIMIT(63));
149411c0d8fdSPaul Kocialkowski }
149511c0d8fdSPaul Kocialkowski 
149611c0d8fdSPaul Kocialkowski static int ov8865_isp_configure(struct ov8865_sensor *sensor)
149711c0d8fdSPaul Kocialkowski {
149811c0d8fdSPaul Kocialkowski 	int ret;
149911c0d8fdSPaul Kocialkowski 
150011c0d8fdSPaul Kocialkowski 	/* Disable lens correction. */
150111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ISP_CTRL0_REG,
150211c0d8fdSPaul Kocialkowski 			   OV8865_ISP_CTRL0_WHITE_BALANCE_EN |
150311c0d8fdSPaul Kocialkowski 			   OV8865_ISP_CTRL0_DPC_BLACK_EN |
150411c0d8fdSPaul Kocialkowski 			   OV8865_ISP_CTRL0_DPC_WHITE_EN);
150511c0d8fdSPaul Kocialkowski 	if (ret)
150611c0d8fdSPaul Kocialkowski 		return ret;
150711c0d8fdSPaul Kocialkowski 
150811c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_ISP_CTRL1_REG,
150911c0d8fdSPaul Kocialkowski 			    OV8865_ISP_CTRL1_BLC_EN);
151011c0d8fdSPaul Kocialkowski }
151111c0d8fdSPaul Kocialkowski 
151211c0d8fdSPaul Kocialkowski static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor,
151311c0d8fdSPaul Kocialkowski 					   const struct ov8865_mode *mode)
151411c0d8fdSPaul Kocialkowski {
151511c0d8fdSPaul Kocialkowski 	const struct ov8865_pll1_config *config = mode->pll1_config;
151611c0d8fdSPaul Kocialkowski 	unsigned long extclk_rate;
151711c0d8fdSPaul Kocialkowski 	unsigned long pll1_rate;
151811c0d8fdSPaul Kocialkowski 
151911c0d8fdSPaul Kocialkowski 	extclk_rate = clk_get_rate(sensor->extclk);
152011c0d8fdSPaul Kocialkowski 	pll1_rate = extclk_rate * config->pll_mul / config->pll_pre_div_half;
152111c0d8fdSPaul Kocialkowski 
152211c0d8fdSPaul Kocialkowski 	switch (config->pll_pre_div) {
152311c0d8fdSPaul Kocialkowski 	case 0:
152411c0d8fdSPaul Kocialkowski 		break;
152511c0d8fdSPaul Kocialkowski 	case 1:
152611c0d8fdSPaul Kocialkowski 		pll1_rate *= 3;
152711c0d8fdSPaul Kocialkowski 		pll1_rate /= 2;
152811c0d8fdSPaul Kocialkowski 		break;
152911c0d8fdSPaul Kocialkowski 	case 3:
153011c0d8fdSPaul Kocialkowski 		pll1_rate *= 5;
153111c0d8fdSPaul Kocialkowski 		pll1_rate /= 2;
153211c0d8fdSPaul Kocialkowski 		break;
153311c0d8fdSPaul Kocialkowski 	case 4:
153411c0d8fdSPaul Kocialkowski 		pll1_rate /= 3;
153511c0d8fdSPaul Kocialkowski 		break;
153611c0d8fdSPaul Kocialkowski 	case 5:
153711c0d8fdSPaul Kocialkowski 		pll1_rate /= 4;
153811c0d8fdSPaul Kocialkowski 		break;
153911c0d8fdSPaul Kocialkowski 	case 7:
154011c0d8fdSPaul Kocialkowski 		pll1_rate /= 8;
154111c0d8fdSPaul Kocialkowski 		break;
154211c0d8fdSPaul Kocialkowski 	default:
154311c0d8fdSPaul Kocialkowski 		pll1_rate /= config->pll_pre_div;
154411c0d8fdSPaul Kocialkowski 		break;
154511c0d8fdSPaul Kocialkowski 	}
154611c0d8fdSPaul Kocialkowski 
154711c0d8fdSPaul Kocialkowski 	return pll1_rate;
154811c0d8fdSPaul Kocialkowski }
154911c0d8fdSPaul Kocialkowski 
155011c0d8fdSPaul Kocialkowski static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,
155111c0d8fdSPaul Kocialkowski 				      const struct ov8865_mode *mode,
155211c0d8fdSPaul Kocialkowski 				      u32 mbus_code)
155311c0d8fdSPaul Kocialkowski {
155411c0d8fdSPaul Kocialkowski 	const struct ov8865_pll1_config *config = mode->pll1_config;
155511c0d8fdSPaul Kocialkowski 	u8 value;
155611c0d8fdSPaul Kocialkowski 	int ret;
155711c0d8fdSPaul Kocialkowski 
155811c0d8fdSPaul Kocialkowski 	switch (mbus_code) {
155911c0d8fdSPaul Kocialkowski 	case MEDIA_BUS_FMT_SBGGR10_1X10:
156011c0d8fdSPaul Kocialkowski 		value = OV8865_MIPI_BIT_SEL(10);
156111c0d8fdSPaul Kocialkowski 		break;
156211c0d8fdSPaul Kocialkowski 	default:
156311c0d8fdSPaul Kocialkowski 		return -EINVAL;
156411c0d8fdSPaul Kocialkowski 	}
156511c0d8fdSPaul Kocialkowski 
156611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_MIPI_BIT_SEL_REG, value);
156711c0d8fdSPaul Kocialkowski 	if (ret)
156811c0d8fdSPaul Kocialkowski 		return ret;
156911c0d8fdSPaul Kocialkowski 
157011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLA_REG,
157111c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLA_PRE_DIV_HALF(config->pll_pre_div_half));
157211c0d8fdSPaul Kocialkowski 	if (ret)
157311c0d8fdSPaul Kocialkowski 		return ret;
157411c0d8fdSPaul Kocialkowski 
157511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL0_REG,
157611c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL0_PRE_DIV(config->pll_pre_div));
157711c0d8fdSPaul Kocialkowski 	if (ret)
157811c0d8fdSPaul Kocialkowski 		return ret;
157911c0d8fdSPaul Kocialkowski 
158011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL1_REG,
158111c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL1_MUL_H(config->pll_mul));
158211c0d8fdSPaul Kocialkowski 	if (ret)
158311c0d8fdSPaul Kocialkowski 		return ret;
158411c0d8fdSPaul Kocialkowski 
158511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL2_REG,
158611c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL2_MUL_L(config->pll_mul));
158711c0d8fdSPaul Kocialkowski 	if (ret)
158811c0d8fdSPaul Kocialkowski 		return ret;
158911c0d8fdSPaul Kocialkowski 
159011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL3_REG,
159111c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL3_M_DIV(config->m_div));
159211c0d8fdSPaul Kocialkowski 	if (ret)
159311c0d8fdSPaul Kocialkowski 		return ret;
159411c0d8fdSPaul Kocialkowski 
159511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL4_REG,
159611c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL4_MIPI_DIV(config->mipi_div));
159711c0d8fdSPaul Kocialkowski 	if (ret)
159811c0d8fdSPaul Kocialkowski 		return ret;
159911c0d8fdSPaul Kocialkowski 
160011c0d8fdSPaul Kocialkowski 	ret = ov8865_update_bits(sensor, OV8865_PCLK_SEL_REG,
160111c0d8fdSPaul Kocialkowski 				 OV8865_PCLK_SEL_PCLK_DIV_MASK,
160211c0d8fdSPaul Kocialkowski 				 OV8865_PCLK_SEL_PCLK_DIV(config->pclk_div));
160311c0d8fdSPaul Kocialkowski 	if (ret)
160411c0d8fdSPaul Kocialkowski 		return ret;
160511c0d8fdSPaul Kocialkowski 
160611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL5_REG,
160711c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL5_SYS_PRE_DIV(config->sys_pre_div));
160811c0d8fdSPaul Kocialkowski 	if (ret)
160911c0d8fdSPaul Kocialkowski 		return ret;
161011c0d8fdSPaul Kocialkowski 
161111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL6_REG,
161211c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL6_SYS_DIV(config->sys_div));
161311c0d8fdSPaul Kocialkowski 	if (ret)
161411c0d8fdSPaul Kocialkowski 		return ret;
161511c0d8fdSPaul Kocialkowski 
161611c0d8fdSPaul Kocialkowski 	return ov8865_update_bits(sensor, OV8865_PLL_CTRL1E_REG,
161711c0d8fdSPaul Kocialkowski 				  OV8865_PLL_CTRL1E_PLL1_NO_LAT,
161811c0d8fdSPaul Kocialkowski 				  OV8865_PLL_CTRL1E_PLL1_NO_LAT);
161911c0d8fdSPaul Kocialkowski }
162011c0d8fdSPaul Kocialkowski 
162111c0d8fdSPaul Kocialkowski static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor,
162211c0d8fdSPaul Kocialkowski 				      const struct ov8865_mode *mode)
162311c0d8fdSPaul Kocialkowski {
162411c0d8fdSPaul Kocialkowski 	const struct ov8865_pll2_config *config = mode->pll2_config;
162511c0d8fdSPaul Kocialkowski 	int ret;
162611c0d8fdSPaul Kocialkowski 
162711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG,
162811c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) |
162911c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL12_DAC_DIV(config->dac_div));
163011c0d8fdSPaul Kocialkowski 	if (ret)
163111c0d8fdSPaul Kocialkowski 		return ret;
163211c0d8fdSPaul Kocialkowski 
163311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLB_REG,
163411c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLB_PRE_DIV(config->pll_pre_div));
163511c0d8fdSPaul Kocialkowski 	if (ret)
163611c0d8fdSPaul Kocialkowski 		return ret;
163711c0d8fdSPaul Kocialkowski 
163811c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLC_REG,
163911c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLC_MUL_H(config->pll_mul));
164011c0d8fdSPaul Kocialkowski 	if (ret)
164111c0d8fdSPaul Kocialkowski 		return ret;
164211c0d8fdSPaul Kocialkowski 
164311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLD_REG,
164411c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLD_MUL_L(config->pll_mul));
164511c0d8fdSPaul Kocialkowski 	if (ret)
164611c0d8fdSPaul Kocialkowski 		return ret;
164711c0d8fdSPaul Kocialkowski 
164811c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLF_REG,
164911c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLF_SYS_PRE_DIV(config->sys_pre_div));
165011c0d8fdSPaul Kocialkowski 	if (ret)
165111c0d8fdSPaul Kocialkowski 		return ret;
165211c0d8fdSPaul Kocialkowski 
165311c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_PLL_CTRLE_REG,
165411c0d8fdSPaul Kocialkowski 			    OV8865_PLL_CTRLE_SYS_DIV(config->sys_div));
165511c0d8fdSPaul Kocialkowski }
165611c0d8fdSPaul Kocialkowski 
165711c0d8fdSPaul Kocialkowski static int ov8865_mode_sclk_configure(struct ov8865_sensor *sensor,
165811c0d8fdSPaul Kocialkowski 				      const struct ov8865_mode *mode)
165911c0d8fdSPaul Kocialkowski {
166011c0d8fdSPaul Kocialkowski 	const struct ov8865_sclk_config *config = mode->sclk_config;
166111c0d8fdSPaul Kocialkowski 	int ret;
166211c0d8fdSPaul Kocialkowski 
166311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_CLK_SEL0_REG,
166411c0d8fdSPaul Kocialkowski 			   OV8865_CLK_SEL0_PLL1_SYS_SEL(config->sys_sel));
166511c0d8fdSPaul Kocialkowski 	if (ret)
166611c0d8fdSPaul Kocialkowski 		return ret;
166711c0d8fdSPaul Kocialkowski 
166811c0d8fdSPaul Kocialkowski 	ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG,
166911c0d8fdSPaul Kocialkowski 				 OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK,
167011c0d8fdSPaul Kocialkowski 				 OV8865_CLK_SEL1_PLL_SCLK_SEL(config->sclk_sel));
167111c0d8fdSPaul Kocialkowski 	if (ret)
167211c0d8fdSPaul Kocialkowski 		return ret;
167311c0d8fdSPaul Kocialkowski 
167411c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_SCLK_CTRL_REG,
167511c0d8fdSPaul Kocialkowski 			    OV8865_SCLK_CTRL_UNKNOWN |
167611c0d8fdSPaul Kocialkowski 			    OV8865_SCLK_CTRL_SCLK_DIV(config->sclk_div) |
167711c0d8fdSPaul Kocialkowski 			    OV8865_SCLK_CTRL_SCLK_PRE_DIV(config->sclk_pre_div));
167811c0d8fdSPaul Kocialkowski }
167911c0d8fdSPaul Kocialkowski 
168011c0d8fdSPaul Kocialkowski static int ov8865_mode_binning_configure(struct ov8865_sensor *sensor,
168111c0d8fdSPaul Kocialkowski 					 const struct ov8865_mode *mode)
168211c0d8fdSPaul Kocialkowski {
168311c0d8fdSPaul Kocialkowski 	unsigned int variopixel_hsub_coef, variopixel_vsub_coef;
168411c0d8fdSPaul Kocialkowski 	u8 value;
168511c0d8fdSPaul Kocialkowski 	int ret;
168611c0d8fdSPaul Kocialkowski 
168711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_FORMAT1_REG, 0);
168811c0d8fdSPaul Kocialkowski 	if (ret)
168911c0d8fdSPaul Kocialkowski 		return ret;
169011c0d8fdSPaul Kocialkowski 
169111c0d8fdSPaul Kocialkowski 	value = OV8865_FORMAT2_HSYNC_EN;
169211c0d8fdSPaul Kocialkowski 
169311c0d8fdSPaul Kocialkowski 	if (mode->binning_x)
169411c0d8fdSPaul Kocialkowski 		value |= OV8865_FORMAT2_FST_HBIN_EN;
169511c0d8fdSPaul Kocialkowski 
169611c0d8fdSPaul Kocialkowski 	if (mode->binning_y)
169711c0d8fdSPaul Kocialkowski 		value |= OV8865_FORMAT2_FST_VBIN_EN;
169811c0d8fdSPaul Kocialkowski 
169911c0d8fdSPaul Kocialkowski 	if (mode->sync_hbin)
170011c0d8fdSPaul Kocialkowski 		value |= OV8865_FORMAT2_SYNC_HBIN_EN;
170111c0d8fdSPaul Kocialkowski 
170211c0d8fdSPaul Kocialkowski 	if (mode->horz_var2)
170311c0d8fdSPaul Kocialkowski 		value |= OV8865_FORMAT2_ISP_HORZ_VAR2_EN;
170411c0d8fdSPaul Kocialkowski 
170511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_FORMAT2_REG, value);
170611c0d8fdSPaul Kocialkowski 	if (ret)
170711c0d8fdSPaul Kocialkowski 		return ret;
170811c0d8fdSPaul Kocialkowski 
170911c0d8fdSPaul Kocialkowski 	ret = ov8865_update_bits(sensor, OV8865_ISP_CTRL2_REG,
171011c0d8fdSPaul Kocialkowski 				 OV8865_ISP_CTRL2_VARIOPIXEL_EN,
171111c0d8fdSPaul Kocialkowski 				 mode->variopixel ?
171211c0d8fdSPaul Kocialkowski 				 OV8865_ISP_CTRL2_VARIOPIXEL_EN : 0);
171311c0d8fdSPaul Kocialkowski 	if (ret)
171411c0d8fdSPaul Kocialkowski 		return ret;
171511c0d8fdSPaul Kocialkowski 
171611c0d8fdSPaul Kocialkowski 	if (mode->variopixel) {
171711c0d8fdSPaul Kocialkowski 		/* VarioPixel coefs needs to be > 1. */
171811c0d8fdSPaul Kocialkowski 		variopixel_hsub_coef = mode->variopixel_hsub_coef;
171911c0d8fdSPaul Kocialkowski 		variopixel_vsub_coef = mode->variopixel_vsub_coef;
172011c0d8fdSPaul Kocialkowski 	} else {
172111c0d8fdSPaul Kocialkowski 		variopixel_hsub_coef = 1;
172211c0d8fdSPaul Kocialkowski 		variopixel_vsub_coef = 1;
172311c0d8fdSPaul Kocialkowski 	}
172411c0d8fdSPaul Kocialkowski 
172511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VAP_CTRL1_REG,
172611c0d8fdSPaul Kocialkowski 			   OV8865_VAP_CTRL1_HSUB_COEF(variopixel_hsub_coef) |
172711c0d8fdSPaul Kocialkowski 			   OV8865_VAP_CTRL1_VSUB_COEF(variopixel_vsub_coef));
172811c0d8fdSPaul Kocialkowski 	if (ret)
172911c0d8fdSPaul Kocialkowski 		return ret;
173011c0d8fdSPaul Kocialkowski 
173111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_INC_X_ODD_REG,
173211c0d8fdSPaul Kocialkowski 			   OV8865_INC_X_ODD(mode->inc_x_odd));
173311c0d8fdSPaul Kocialkowski 	if (ret)
173411c0d8fdSPaul Kocialkowski 		return ret;
173511c0d8fdSPaul Kocialkowski 
173611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_INC_X_EVEN_REG,
173711c0d8fdSPaul Kocialkowski 			   OV8865_INC_X_EVEN(mode->inc_x_even));
173811c0d8fdSPaul Kocialkowski 	if (ret)
173911c0d8fdSPaul Kocialkowski 		return ret;
174011c0d8fdSPaul Kocialkowski 
174111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_INC_Y_ODD_REG,
174211c0d8fdSPaul Kocialkowski 			   OV8865_INC_Y_ODD(mode->inc_y_odd));
174311c0d8fdSPaul Kocialkowski 	if (ret)
174411c0d8fdSPaul Kocialkowski 		return ret;
174511c0d8fdSPaul Kocialkowski 
174611c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_INC_Y_EVEN_REG,
174711c0d8fdSPaul Kocialkowski 			    OV8865_INC_Y_EVEN(mode->inc_y_even));
174811c0d8fdSPaul Kocialkowski }
174911c0d8fdSPaul Kocialkowski 
175011c0d8fdSPaul Kocialkowski static int ov8865_mode_black_level_configure(struct ov8865_sensor *sensor,
175111c0d8fdSPaul Kocialkowski 					     const struct ov8865_mode *mode)
175211c0d8fdSPaul Kocialkowski {
175311c0d8fdSPaul Kocialkowski 	int ret;
175411c0d8fdSPaul Kocialkowski 
175511c0d8fdSPaul Kocialkowski 	/* Note that a zero value for blc_col_shift_mask is the default 256. */
175611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_CTRL1_REG,
175711c0d8fdSPaul Kocialkowski 			   mode->blc_col_shift_mask |
175811c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL1_OFFSET_LIMIT_EN);
175911c0d8fdSPaul Kocialkowski 	if (ret)
176011c0d8fdSPaul Kocialkowski 		return ret;
176111c0d8fdSPaul Kocialkowski 
176211c0d8fdSPaul Kocialkowski 	/* BLC top zero line */
176311c0d8fdSPaul Kocialkowski 
176411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_START_REG,
176511c0d8fdSPaul Kocialkowski 			   OV8865_BLC_TOP_ZLINE_START(mode->blc_top_zero_line_start));
176611c0d8fdSPaul Kocialkowski 	if (ret)
176711c0d8fdSPaul Kocialkowski 		return ret;
176811c0d8fdSPaul Kocialkowski 
176911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_NUM_REG,
177011c0d8fdSPaul Kocialkowski 			   OV8865_BLC_TOP_ZLINE_NUM(mode->blc_top_zero_line_num));
177111c0d8fdSPaul Kocialkowski 	if (ret)
177211c0d8fdSPaul Kocialkowski 		return ret;
177311c0d8fdSPaul Kocialkowski 
177411c0d8fdSPaul Kocialkowski 	/* BLC top black line */
177511c0d8fdSPaul Kocialkowski 
177611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_START_REG,
177711c0d8fdSPaul Kocialkowski 			   OV8865_BLC_TOP_BLKLINE_START(mode->blc_top_black_line_start));
177811c0d8fdSPaul Kocialkowski 	if (ret)
177911c0d8fdSPaul Kocialkowski 		return ret;
178011c0d8fdSPaul Kocialkowski 
178111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_NUM_REG,
178211c0d8fdSPaul Kocialkowski 			   OV8865_BLC_TOP_BLKLINE_NUM(mode->blc_top_black_line_num));
178311c0d8fdSPaul Kocialkowski 	if (ret)
178411c0d8fdSPaul Kocialkowski 		return ret;
178511c0d8fdSPaul Kocialkowski 
178611c0d8fdSPaul Kocialkowski 	/* BLC bottom zero line */
178711c0d8fdSPaul Kocialkowski 
178811c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_START_REG,
178911c0d8fdSPaul Kocialkowski 			   OV8865_BLC_BOT_ZLINE_START(mode->blc_bottom_zero_line_start));
179011c0d8fdSPaul Kocialkowski 	if (ret)
179111c0d8fdSPaul Kocialkowski 		return ret;
179211c0d8fdSPaul Kocialkowski 
179311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_NUM_REG,
179411c0d8fdSPaul Kocialkowski 			   OV8865_BLC_BOT_ZLINE_NUM(mode->blc_bottom_zero_line_num));
179511c0d8fdSPaul Kocialkowski 	if (ret)
179611c0d8fdSPaul Kocialkowski 		return ret;
179711c0d8fdSPaul Kocialkowski 
179811c0d8fdSPaul Kocialkowski 	/* BLC bottom black line */
179911c0d8fdSPaul Kocialkowski 
180011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_START_REG,
180111c0d8fdSPaul Kocialkowski 			   OV8865_BLC_BOT_BLKLINE_START(mode->blc_bottom_black_line_start));
180211c0d8fdSPaul Kocialkowski 	if (ret)
180311c0d8fdSPaul Kocialkowski 		return ret;
180411c0d8fdSPaul Kocialkowski 
180511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_NUM_REG,
180611c0d8fdSPaul Kocialkowski 			   OV8865_BLC_BOT_BLKLINE_NUM(mode->blc_bottom_black_line_num));
180711c0d8fdSPaul Kocialkowski 	if (ret)
180811c0d8fdSPaul Kocialkowski 		return ret;
180911c0d8fdSPaul Kocialkowski 
181011c0d8fdSPaul Kocialkowski 	/* BLC anchor */
181111c0d8fdSPaul Kocialkowski 
181211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_H_REG,
181311c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_LEFT_START_H(mode->blc_anchor_left_start));
181411c0d8fdSPaul Kocialkowski 	if (ret)
181511c0d8fdSPaul Kocialkowski 		return ret;
181611c0d8fdSPaul Kocialkowski 
181711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_L_REG,
181811c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_LEFT_START_L(mode->blc_anchor_left_start));
181911c0d8fdSPaul Kocialkowski 	if (ret)
182011c0d8fdSPaul Kocialkowski 		return ret;
182111c0d8fdSPaul Kocialkowski 
182211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_H_REG,
182311c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_LEFT_END_H(mode->blc_anchor_left_end));
182411c0d8fdSPaul Kocialkowski 	if (ret)
182511c0d8fdSPaul Kocialkowski 		return ret;
182611c0d8fdSPaul Kocialkowski 
182711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_L_REG,
182811c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_LEFT_END_L(mode->blc_anchor_left_end));
182911c0d8fdSPaul Kocialkowski 	if (ret)
183011c0d8fdSPaul Kocialkowski 		return ret;
183111c0d8fdSPaul Kocialkowski 
183211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_H_REG,
183311c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_RIGHT_START_H(mode->blc_anchor_right_start));
183411c0d8fdSPaul Kocialkowski 	if (ret)
183511c0d8fdSPaul Kocialkowski 		return ret;
183611c0d8fdSPaul Kocialkowski 
183711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_L_REG,
183811c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_RIGHT_START_L(mode->blc_anchor_right_start));
183911c0d8fdSPaul Kocialkowski 	if (ret)
184011c0d8fdSPaul Kocialkowski 		return ret;
184111c0d8fdSPaul Kocialkowski 
184211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_H_REG,
184311c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_RIGHT_END_H(mode->blc_anchor_right_end));
184411c0d8fdSPaul Kocialkowski 	if (ret)
184511c0d8fdSPaul Kocialkowski 		return ret;
184611c0d8fdSPaul Kocialkowski 
184711c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_L_REG,
184811c0d8fdSPaul Kocialkowski 			    OV8865_BLC_ANCHOR_RIGHT_END_L(mode->blc_anchor_right_end));
184911c0d8fdSPaul Kocialkowski }
185011c0d8fdSPaul Kocialkowski 
185111c0d8fdSPaul Kocialkowski static int ov8865_mode_configure(struct ov8865_sensor *sensor,
185211c0d8fdSPaul Kocialkowski 				 const struct ov8865_mode *mode, u32 mbus_code)
185311c0d8fdSPaul Kocialkowski {
185411c0d8fdSPaul Kocialkowski 	int ret;
185511c0d8fdSPaul Kocialkowski 
185611c0d8fdSPaul Kocialkowski 	/* Output Size X */
185711c0d8fdSPaul Kocialkowski 
185811c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_H_REG,
185911c0d8fdSPaul Kocialkowski 			   OV8865_OUTPUT_SIZE_X_H(mode->output_size_x));
186011c0d8fdSPaul Kocialkowski 	if (ret)
186111c0d8fdSPaul Kocialkowski 		return ret;
186211c0d8fdSPaul Kocialkowski 
186311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_L_REG,
186411c0d8fdSPaul Kocialkowski 			   OV8865_OUTPUT_SIZE_X_L(mode->output_size_x));
186511c0d8fdSPaul Kocialkowski 	if (ret)
186611c0d8fdSPaul Kocialkowski 		return ret;
186711c0d8fdSPaul Kocialkowski 
186811c0d8fdSPaul Kocialkowski 	/* Horizontal Total Size */
186911c0d8fdSPaul Kocialkowski 
187011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_HTS_H_REG, OV8865_HTS_H(mode->hts));
187111c0d8fdSPaul Kocialkowski 	if (ret)
187211c0d8fdSPaul Kocialkowski 		return ret;
187311c0d8fdSPaul Kocialkowski 
187411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_HTS_L_REG, OV8865_HTS_L(mode->hts));
187511c0d8fdSPaul Kocialkowski 	if (ret)
187611c0d8fdSPaul Kocialkowski 		return ret;
187711c0d8fdSPaul Kocialkowski 
187811c0d8fdSPaul Kocialkowski 	/* Output Size Y */
187911c0d8fdSPaul Kocialkowski 
188011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_H_REG,
188111c0d8fdSPaul Kocialkowski 			   OV8865_OUTPUT_SIZE_Y_H(mode->output_size_y));
188211c0d8fdSPaul Kocialkowski 	if (ret)
188311c0d8fdSPaul Kocialkowski 		return ret;
188411c0d8fdSPaul Kocialkowski 
188511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_L_REG,
188611c0d8fdSPaul Kocialkowski 			   OV8865_OUTPUT_SIZE_Y_L(mode->output_size_y));
188711c0d8fdSPaul Kocialkowski 	if (ret)
188811c0d8fdSPaul Kocialkowski 		return ret;
188911c0d8fdSPaul Kocialkowski 
189011c0d8fdSPaul Kocialkowski 	/* Vertical Total Size */
189111c0d8fdSPaul Kocialkowski 
189211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(mode->vts));
189311c0d8fdSPaul Kocialkowski 	if (ret)
189411c0d8fdSPaul Kocialkowski 		return ret;
189511c0d8fdSPaul Kocialkowski 
189611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(mode->vts));
189711c0d8fdSPaul Kocialkowski 	if (ret)
189811c0d8fdSPaul Kocialkowski 		return ret;
189911c0d8fdSPaul Kocialkowski 
190011c0d8fdSPaul Kocialkowski 	if (mode->size_auto) {
190111c0d8fdSPaul Kocialkowski 		/* Auto Size */
190211c0d8fdSPaul Kocialkowski 
190311c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_AUTO_SIZE_CTRL_REG,
190411c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG |
190511c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG |
190611c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG |
190711c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG |
190811c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG |
190911c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG);
191011c0d8fdSPaul Kocialkowski 		if (ret)
191111c0d8fdSPaul Kocialkowski 			return ret;
191211c0d8fdSPaul Kocialkowski 
191311c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_AUTO_SIZE_BOUNDARIES_REG,
191411c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_BOUNDARIES_Y(mode->size_auto_boundary_y) |
191511c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_BOUNDARIES_X(mode->size_auto_boundary_x));
191611c0d8fdSPaul Kocialkowski 		if (ret)
191711c0d8fdSPaul Kocialkowski 			return ret;
191811c0d8fdSPaul Kocialkowski 	} else {
191911c0d8fdSPaul Kocialkowski 		/* Crop Start X */
192011c0d8fdSPaul Kocialkowski 
192111c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_START_X_H_REG,
192211c0d8fdSPaul Kocialkowski 				   OV8865_CROP_START_X_H(mode->crop_start_x));
192311c0d8fdSPaul Kocialkowski 		if (ret)
192411c0d8fdSPaul Kocialkowski 			return ret;
192511c0d8fdSPaul Kocialkowski 
192611c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_START_X_L_REG,
192711c0d8fdSPaul Kocialkowski 				   OV8865_CROP_START_X_L(mode->crop_start_x));
192811c0d8fdSPaul Kocialkowski 		if (ret)
192911c0d8fdSPaul Kocialkowski 			return ret;
193011c0d8fdSPaul Kocialkowski 
193111c0d8fdSPaul Kocialkowski 		/* Offset X */
193211c0d8fdSPaul Kocialkowski 
193311c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_OFFSET_X_H_REG,
193411c0d8fdSPaul Kocialkowski 				   OV8865_OFFSET_X_H(mode->offset_x));
193511c0d8fdSPaul Kocialkowski 		if (ret)
193611c0d8fdSPaul Kocialkowski 			return ret;
193711c0d8fdSPaul Kocialkowski 
193811c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_OFFSET_X_L_REG,
193911c0d8fdSPaul Kocialkowski 				   OV8865_OFFSET_X_L(mode->offset_x));
194011c0d8fdSPaul Kocialkowski 		if (ret)
194111c0d8fdSPaul Kocialkowski 			return ret;
194211c0d8fdSPaul Kocialkowski 
194311c0d8fdSPaul Kocialkowski 		/* Crop End X */
194411c0d8fdSPaul Kocialkowski 
194511c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_END_X_H_REG,
194611c0d8fdSPaul Kocialkowski 				   OV8865_CROP_END_X_H(mode->crop_end_x));
194711c0d8fdSPaul Kocialkowski 		if (ret)
194811c0d8fdSPaul Kocialkowski 			return ret;
194911c0d8fdSPaul Kocialkowski 
195011c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_END_X_L_REG,
195111c0d8fdSPaul Kocialkowski 				   OV8865_CROP_END_X_L(mode->crop_end_x));
195211c0d8fdSPaul Kocialkowski 		if (ret)
195311c0d8fdSPaul Kocialkowski 			return ret;
195411c0d8fdSPaul Kocialkowski 
195511c0d8fdSPaul Kocialkowski 		/* Crop Start Y */
195611c0d8fdSPaul Kocialkowski 
195711c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_START_Y_H_REG,
195811c0d8fdSPaul Kocialkowski 				   OV8865_CROP_START_Y_H(mode->crop_start_y));
195911c0d8fdSPaul Kocialkowski 		if (ret)
196011c0d8fdSPaul Kocialkowski 			return ret;
196111c0d8fdSPaul Kocialkowski 
196211c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_START_Y_L_REG,
196311c0d8fdSPaul Kocialkowski 				   OV8865_CROP_START_Y_L(mode->crop_start_y));
196411c0d8fdSPaul Kocialkowski 		if (ret)
196511c0d8fdSPaul Kocialkowski 			return ret;
196611c0d8fdSPaul Kocialkowski 
196711c0d8fdSPaul Kocialkowski 		/* Offset Y */
196811c0d8fdSPaul Kocialkowski 
196911c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_OFFSET_Y_H_REG,
197011c0d8fdSPaul Kocialkowski 				   OV8865_OFFSET_Y_H(mode->offset_y));
197111c0d8fdSPaul Kocialkowski 		if (ret)
197211c0d8fdSPaul Kocialkowski 			return ret;
197311c0d8fdSPaul Kocialkowski 
197411c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_OFFSET_Y_L_REG,
197511c0d8fdSPaul Kocialkowski 				   OV8865_OFFSET_Y_L(mode->offset_y));
197611c0d8fdSPaul Kocialkowski 		if (ret)
197711c0d8fdSPaul Kocialkowski 			return ret;
197811c0d8fdSPaul Kocialkowski 
197911c0d8fdSPaul Kocialkowski 		/* Crop End Y */
198011c0d8fdSPaul Kocialkowski 
198111c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_END_Y_H_REG,
198211c0d8fdSPaul Kocialkowski 				   OV8865_CROP_END_Y_H(mode->crop_end_y));
198311c0d8fdSPaul Kocialkowski 		if (ret)
198411c0d8fdSPaul Kocialkowski 			return ret;
198511c0d8fdSPaul Kocialkowski 
198611c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_END_Y_L_REG,
198711c0d8fdSPaul Kocialkowski 				   OV8865_CROP_END_Y_L(mode->crop_end_y));
198811c0d8fdSPaul Kocialkowski 		if (ret)
198911c0d8fdSPaul Kocialkowski 			return ret;
199011c0d8fdSPaul Kocialkowski 	}
199111c0d8fdSPaul Kocialkowski 
199211c0d8fdSPaul Kocialkowski 	/* VFIFO */
199311c0d8fdSPaul Kocialkowski 
199411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_H_REG,
199511c0d8fdSPaul Kocialkowski 			   OV8865_VFIFO_READ_START_H(mode->vfifo_read_start));
199611c0d8fdSPaul Kocialkowski 	if (ret)
199711c0d8fdSPaul Kocialkowski 		return ret;
199811c0d8fdSPaul Kocialkowski 
199911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_L_REG,
200011c0d8fdSPaul Kocialkowski 			   OV8865_VFIFO_READ_START_L(mode->vfifo_read_start));
200111c0d8fdSPaul Kocialkowski 	if (ret)
200211c0d8fdSPaul Kocialkowski 		return ret;
200311c0d8fdSPaul Kocialkowski 
200411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ABLC_NUM_REG,
200511c0d8fdSPaul Kocialkowski 			   OV8865_ABLC_NUM(mode->ablc_num));
200611c0d8fdSPaul Kocialkowski 	if (ret)
200711c0d8fdSPaul Kocialkowski 		return ret;
200811c0d8fdSPaul Kocialkowski 
200911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ZLINE_NUM_REG,
201011c0d8fdSPaul Kocialkowski 			   OV8865_ZLINE_NUM(mode->zline_num));
201111c0d8fdSPaul Kocialkowski 	if (ret)
201211c0d8fdSPaul Kocialkowski 		return ret;
201311c0d8fdSPaul Kocialkowski 
201411c0d8fdSPaul Kocialkowski 	/* Binning */
201511c0d8fdSPaul Kocialkowski 
201611c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_binning_configure(sensor, mode);
201711c0d8fdSPaul Kocialkowski 	if (ret)
201811c0d8fdSPaul Kocialkowski 		return ret;
201911c0d8fdSPaul Kocialkowski 
202011c0d8fdSPaul Kocialkowski 	/* Black Level */
202111c0d8fdSPaul Kocialkowski 
202211c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_black_level_configure(sensor, mode);
202311c0d8fdSPaul Kocialkowski 	if (ret)
202411c0d8fdSPaul Kocialkowski 		return ret;
202511c0d8fdSPaul Kocialkowski 
202611c0d8fdSPaul Kocialkowski 	/* PLLs */
202711c0d8fdSPaul Kocialkowski 
202811c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_pll1_configure(sensor, mode, mbus_code);
202911c0d8fdSPaul Kocialkowski 	if (ret)
203011c0d8fdSPaul Kocialkowski 		return ret;
203111c0d8fdSPaul Kocialkowski 
203211c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_pll2_configure(sensor, mode);
203311c0d8fdSPaul Kocialkowski 	if (ret)
203411c0d8fdSPaul Kocialkowski 		return ret;
203511c0d8fdSPaul Kocialkowski 
203611c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_sclk_configure(sensor, mode);
203711c0d8fdSPaul Kocialkowski 	if (ret)
203811c0d8fdSPaul Kocialkowski 		return ret;
203911c0d8fdSPaul Kocialkowski 
204011c0d8fdSPaul Kocialkowski 	/* Extra registers */
204111c0d8fdSPaul Kocialkowski 
204211c0d8fdSPaul Kocialkowski 	if (mode->register_values) {
204311c0d8fdSPaul Kocialkowski 		ret = ov8865_write_sequence(sensor, mode->register_values,
204411c0d8fdSPaul Kocialkowski 					    mode->register_values_count);
204511c0d8fdSPaul Kocialkowski 		if (ret)
204611c0d8fdSPaul Kocialkowski 			return ret;
204711c0d8fdSPaul Kocialkowski 	}
204811c0d8fdSPaul Kocialkowski 
204911c0d8fdSPaul Kocialkowski 	return 0;
205011c0d8fdSPaul Kocialkowski }
205111c0d8fdSPaul Kocialkowski 
205211c0d8fdSPaul Kocialkowski static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor,
205311c0d8fdSPaul Kocialkowski 					       const struct ov8865_mode *mode)
205411c0d8fdSPaul Kocialkowski {
205511c0d8fdSPaul Kocialkowski 	const struct ov8865_pll1_config *config = mode->pll1_config;
205611c0d8fdSPaul Kocialkowski 	unsigned long pll1_rate;
205711c0d8fdSPaul Kocialkowski 
205811c0d8fdSPaul Kocialkowski 	pll1_rate = ov8865_mode_pll1_rate(sensor, mode);
205911c0d8fdSPaul Kocialkowski 
206011c0d8fdSPaul Kocialkowski 	return pll1_rate / config->m_div / 2;
206111c0d8fdSPaul Kocialkowski }
206211c0d8fdSPaul Kocialkowski 
206311c0d8fdSPaul Kocialkowski /* Exposure */
206411c0d8fdSPaul Kocialkowski 
206511c0d8fdSPaul Kocialkowski static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure)
206611c0d8fdSPaul Kocialkowski {
206711c0d8fdSPaul Kocialkowski 	int ret;
206811c0d8fdSPaul Kocialkowski 
206911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_HH_REG,
207011c0d8fdSPaul Kocialkowski 			   OV8865_EXPOSURE_CTRL_HH(exposure));
207111c0d8fdSPaul Kocialkowski 	if (ret)
207211c0d8fdSPaul Kocialkowski 		return ret;
207311c0d8fdSPaul Kocialkowski 
207411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_H_REG,
207511c0d8fdSPaul Kocialkowski 			   OV8865_EXPOSURE_CTRL_H(exposure));
207611c0d8fdSPaul Kocialkowski 	if (ret)
207711c0d8fdSPaul Kocialkowski 		return ret;
207811c0d8fdSPaul Kocialkowski 
207911c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_EXPOSURE_CTRL_L_REG,
208011c0d8fdSPaul Kocialkowski 			    OV8865_EXPOSURE_CTRL_L(exposure));
208111c0d8fdSPaul Kocialkowski }
208211c0d8fdSPaul Kocialkowski 
208311c0d8fdSPaul Kocialkowski /* Gain */
208411c0d8fdSPaul Kocialkowski 
208511c0d8fdSPaul Kocialkowski static int ov8865_gain_configure(struct ov8865_sensor *sensor, u32 gain)
208611c0d8fdSPaul Kocialkowski {
208711c0d8fdSPaul Kocialkowski 	int ret;
208811c0d8fdSPaul Kocialkowski 
208911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_GAIN_CTRL_H_REG,
209011c0d8fdSPaul Kocialkowski 			   OV8865_GAIN_CTRL_H(gain));
209111c0d8fdSPaul Kocialkowski 	if (ret)
209211c0d8fdSPaul Kocialkowski 		return ret;
209311c0d8fdSPaul Kocialkowski 
209411c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_GAIN_CTRL_L_REG,
209511c0d8fdSPaul Kocialkowski 			    OV8865_GAIN_CTRL_L(gain));
209611c0d8fdSPaul Kocialkowski }
209711c0d8fdSPaul Kocialkowski 
209811c0d8fdSPaul Kocialkowski /* White Balance */
209911c0d8fdSPaul Kocialkowski 
210011c0d8fdSPaul Kocialkowski static int ov8865_red_balance_configure(struct ov8865_sensor *sensor,
210111c0d8fdSPaul Kocialkowski 					u32 red_balance)
210211c0d8fdSPaul Kocialkowski {
210311c0d8fdSPaul Kocialkowski 	int ret;
210411c0d8fdSPaul Kocialkowski 
210511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ISP_GAIN_RED_H_REG,
210611c0d8fdSPaul Kocialkowski 			   OV8865_ISP_GAIN_RED_H(red_balance));
210711c0d8fdSPaul Kocialkowski 	if (ret)
210811c0d8fdSPaul Kocialkowski 		return ret;
210911c0d8fdSPaul Kocialkowski 
211011c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_ISP_GAIN_RED_L_REG,
211111c0d8fdSPaul Kocialkowski 			    OV8865_ISP_GAIN_RED_L(red_balance));
211211c0d8fdSPaul Kocialkowski }
211311c0d8fdSPaul Kocialkowski 
211411c0d8fdSPaul Kocialkowski static int ov8865_blue_balance_configure(struct ov8865_sensor *sensor,
211511c0d8fdSPaul Kocialkowski 					 u32 blue_balance)
211611c0d8fdSPaul Kocialkowski {
211711c0d8fdSPaul Kocialkowski 	int ret;
211811c0d8fdSPaul Kocialkowski 
211911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_H_REG,
212011c0d8fdSPaul Kocialkowski 			   OV8865_ISP_GAIN_BLUE_H(blue_balance));
212111c0d8fdSPaul Kocialkowski 	if (ret)
212211c0d8fdSPaul Kocialkowski 		return ret;
212311c0d8fdSPaul Kocialkowski 
212411c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_L_REG,
212511c0d8fdSPaul Kocialkowski 			    OV8865_ISP_GAIN_BLUE_L(blue_balance));
212611c0d8fdSPaul Kocialkowski }
212711c0d8fdSPaul Kocialkowski 
212811c0d8fdSPaul Kocialkowski /* Flip */
212911c0d8fdSPaul Kocialkowski 
213011c0d8fdSPaul Kocialkowski static int ov8865_flip_vert_configure(struct ov8865_sensor *sensor, bool enable)
213111c0d8fdSPaul Kocialkowski {
213211c0d8fdSPaul Kocialkowski 	u8 bits = OV8865_FORMAT1_FLIP_VERT_ISP_EN |
213311c0d8fdSPaul Kocialkowski 		  OV8865_FORMAT1_FLIP_VERT_SENSOR_EN;
213411c0d8fdSPaul Kocialkowski 
213511c0d8fdSPaul Kocialkowski 	return ov8865_update_bits(sensor, OV8865_FORMAT1_REG, bits,
213611c0d8fdSPaul Kocialkowski 				  enable ? bits : 0);
213711c0d8fdSPaul Kocialkowski }
213811c0d8fdSPaul Kocialkowski 
213911c0d8fdSPaul Kocialkowski static int ov8865_flip_horz_configure(struct ov8865_sensor *sensor, bool enable)
214011c0d8fdSPaul Kocialkowski {
214111c0d8fdSPaul Kocialkowski 	u8 bits = OV8865_FORMAT2_FLIP_HORZ_ISP_EN |
214211c0d8fdSPaul Kocialkowski 		  OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN;
214311c0d8fdSPaul Kocialkowski 
214411c0d8fdSPaul Kocialkowski 	return ov8865_update_bits(sensor, OV8865_FORMAT2_REG, bits,
214511c0d8fdSPaul Kocialkowski 				  enable ? bits : 0);
214611c0d8fdSPaul Kocialkowski }
214711c0d8fdSPaul Kocialkowski 
214811c0d8fdSPaul Kocialkowski /* Test Pattern */
214911c0d8fdSPaul Kocialkowski 
215011c0d8fdSPaul Kocialkowski static int ov8865_test_pattern_configure(struct ov8865_sensor *sensor,
215111c0d8fdSPaul Kocialkowski 					 unsigned int index)
215211c0d8fdSPaul Kocialkowski {
215311c0d8fdSPaul Kocialkowski 	if (index >= ARRAY_SIZE(ov8865_test_pattern_bits))
215411c0d8fdSPaul Kocialkowski 		return -EINVAL;
215511c0d8fdSPaul Kocialkowski 
215611c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_PRE_CTRL0_REG,
215711c0d8fdSPaul Kocialkowski 			    ov8865_test_pattern_bits[index]);
215811c0d8fdSPaul Kocialkowski }
215911c0d8fdSPaul Kocialkowski 
216011c0d8fdSPaul Kocialkowski /* State */
216111c0d8fdSPaul Kocialkowski 
216211c0d8fdSPaul Kocialkowski static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor,
216311c0d8fdSPaul Kocialkowski 				       const struct ov8865_mode *mode,
216411c0d8fdSPaul Kocialkowski 				       u32 mbus_code)
216511c0d8fdSPaul Kocialkowski {
216611c0d8fdSPaul Kocialkowski 	struct ov8865_ctrls *ctrls = &sensor->ctrls;
216711c0d8fdSPaul Kocialkowski 	struct v4l2_fwnode_bus_mipi_csi2 *bus_mipi_csi2 =
216811c0d8fdSPaul Kocialkowski 		&sensor->endpoint.bus.mipi_csi2;
216911c0d8fdSPaul Kocialkowski 	unsigned long mipi_clk_rate;
217011c0d8fdSPaul Kocialkowski 	unsigned int bits_per_sample;
217111c0d8fdSPaul Kocialkowski 	unsigned int lanes_count;
217211c0d8fdSPaul Kocialkowski 	unsigned int i, j;
217311c0d8fdSPaul Kocialkowski 	s64 mipi_pixel_rate;
217411c0d8fdSPaul Kocialkowski 
217511c0d8fdSPaul Kocialkowski 	mipi_clk_rate = ov8865_mode_mipi_clk_rate(sensor, mode);
217611c0d8fdSPaul Kocialkowski 	if (!mipi_clk_rate)
217711c0d8fdSPaul Kocialkowski 		return -EINVAL;
217811c0d8fdSPaul Kocialkowski 
217911c0d8fdSPaul Kocialkowski 	for (i = 0; i < ARRAY_SIZE(ov8865_link_freq_menu); i++) {
218011c0d8fdSPaul Kocialkowski 		s64 freq = ov8865_link_freq_menu[i];
218111c0d8fdSPaul Kocialkowski 
218211c0d8fdSPaul Kocialkowski 		if (freq == mipi_clk_rate)
218311c0d8fdSPaul Kocialkowski 			break;
218411c0d8fdSPaul Kocialkowski 	}
218511c0d8fdSPaul Kocialkowski 
218611c0d8fdSPaul Kocialkowski 	for (j = 0; j < sensor->endpoint.nr_of_link_frequencies; j++) {
218711c0d8fdSPaul Kocialkowski 		u64 freq = sensor->endpoint.link_frequencies[j];
218811c0d8fdSPaul Kocialkowski 
218911c0d8fdSPaul Kocialkowski 		if (freq == mipi_clk_rate)
219011c0d8fdSPaul Kocialkowski 			break;
219111c0d8fdSPaul Kocialkowski 	}
219211c0d8fdSPaul Kocialkowski 
219311c0d8fdSPaul Kocialkowski 	if (i == ARRAY_SIZE(ov8865_link_freq_menu)) {
219411c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev,
219511c0d8fdSPaul Kocialkowski 			"failed to find %lu clk rate in link freq\n",
219611c0d8fdSPaul Kocialkowski 			mipi_clk_rate);
219711c0d8fdSPaul Kocialkowski 	} else if (j == sensor->endpoint.nr_of_link_frequencies) {
219811c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev,
219911c0d8fdSPaul Kocialkowski 			"failed to find %lu clk rate in endpoint link-frequencies\n",
220011c0d8fdSPaul Kocialkowski 			mipi_clk_rate);
220111c0d8fdSPaul Kocialkowski 	} else {
220211c0d8fdSPaul Kocialkowski 		__v4l2_ctrl_s_ctrl(ctrls->link_freq, i);
220311c0d8fdSPaul Kocialkowski 	}
220411c0d8fdSPaul Kocialkowski 
220511c0d8fdSPaul Kocialkowski 	switch (mbus_code) {
220611c0d8fdSPaul Kocialkowski 	case MEDIA_BUS_FMT_SBGGR10_1X10:
220711c0d8fdSPaul Kocialkowski 		bits_per_sample = 10;
220811c0d8fdSPaul Kocialkowski 		break;
220911c0d8fdSPaul Kocialkowski 	default:
221011c0d8fdSPaul Kocialkowski 		return -EINVAL;
221111c0d8fdSPaul Kocialkowski 	}
221211c0d8fdSPaul Kocialkowski 
221311c0d8fdSPaul Kocialkowski 	lanes_count = bus_mipi_csi2->num_data_lanes;
221411c0d8fdSPaul Kocialkowski 	mipi_pixel_rate = mipi_clk_rate * 2 * lanes_count / bits_per_sample;
221511c0d8fdSPaul Kocialkowski 
221611c0d8fdSPaul Kocialkowski 	__v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mipi_pixel_rate);
221711c0d8fdSPaul Kocialkowski 
221811c0d8fdSPaul Kocialkowski 	return 0;
221911c0d8fdSPaul Kocialkowski }
222011c0d8fdSPaul Kocialkowski 
222111c0d8fdSPaul Kocialkowski static int ov8865_state_configure(struct ov8865_sensor *sensor,
222211c0d8fdSPaul Kocialkowski 				  const struct ov8865_mode *mode,
222311c0d8fdSPaul Kocialkowski 				  u32 mbus_code)
222411c0d8fdSPaul Kocialkowski {
222511c0d8fdSPaul Kocialkowski 	int ret;
222611c0d8fdSPaul Kocialkowski 
222711c0d8fdSPaul Kocialkowski 	if (sensor->state.streaming)
222811c0d8fdSPaul Kocialkowski 		return -EBUSY;
222911c0d8fdSPaul Kocialkowski 
223011c0d8fdSPaul Kocialkowski 	/* State will be configured at first power on otherwise. */
223111c0d8fdSPaul Kocialkowski 	if (pm_runtime_enabled(sensor->dev) &&
223211c0d8fdSPaul Kocialkowski 	    !pm_runtime_suspended(sensor->dev)) {
223311c0d8fdSPaul Kocialkowski 		ret = ov8865_mode_configure(sensor, mode, mbus_code);
223411c0d8fdSPaul Kocialkowski 		if (ret)
223511c0d8fdSPaul Kocialkowski 			return ret;
223611c0d8fdSPaul Kocialkowski 	}
223711c0d8fdSPaul Kocialkowski 
223811c0d8fdSPaul Kocialkowski 	ret = ov8865_state_mipi_configure(sensor, mode, mbus_code);
223911c0d8fdSPaul Kocialkowski 	if (ret)
224011c0d8fdSPaul Kocialkowski 		return ret;
224111c0d8fdSPaul Kocialkowski 
224211c0d8fdSPaul Kocialkowski 	sensor->state.mode = mode;
224311c0d8fdSPaul Kocialkowski 	sensor->state.mbus_code = mbus_code;
224411c0d8fdSPaul Kocialkowski 
224511c0d8fdSPaul Kocialkowski 	return 0;
224611c0d8fdSPaul Kocialkowski }
224711c0d8fdSPaul Kocialkowski 
224811c0d8fdSPaul Kocialkowski static int ov8865_state_init(struct ov8865_sensor *sensor)
224911c0d8fdSPaul Kocialkowski {
225011c0d8fdSPaul Kocialkowski 	return ov8865_state_configure(sensor, &ov8865_modes[0],
225111c0d8fdSPaul Kocialkowski 				      ov8865_mbus_codes[0]);
225211c0d8fdSPaul Kocialkowski }
225311c0d8fdSPaul Kocialkowski 
225411c0d8fdSPaul Kocialkowski /* Sensor Base */
225511c0d8fdSPaul Kocialkowski 
225611c0d8fdSPaul Kocialkowski static int ov8865_sensor_init(struct ov8865_sensor *sensor)
225711c0d8fdSPaul Kocialkowski {
225811c0d8fdSPaul Kocialkowski 	int ret;
225911c0d8fdSPaul Kocialkowski 
226011c0d8fdSPaul Kocialkowski 	ret = ov8865_sw_reset(sensor);
226111c0d8fdSPaul Kocialkowski 	if (ret) {
226211c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to perform sw reset\n");
226311c0d8fdSPaul Kocialkowski 		return ret;
226411c0d8fdSPaul Kocialkowski 	}
226511c0d8fdSPaul Kocialkowski 
226611c0d8fdSPaul Kocialkowski 	ret = ov8865_sw_standby(sensor, 1);
226711c0d8fdSPaul Kocialkowski 	if (ret) {
226811c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to set sensor standby\n");
226911c0d8fdSPaul Kocialkowski 		return ret;
227011c0d8fdSPaul Kocialkowski 	}
227111c0d8fdSPaul Kocialkowski 
227211c0d8fdSPaul Kocialkowski 	ret = ov8865_chip_id_check(sensor);
227311c0d8fdSPaul Kocialkowski 	if (ret) {
227411c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to check sensor chip id\n");
227511c0d8fdSPaul Kocialkowski 		return ret;
227611c0d8fdSPaul Kocialkowski 	}
227711c0d8fdSPaul Kocialkowski 
227811c0d8fdSPaul Kocialkowski 	ret = ov8865_write_sequence(sensor, ov8865_init_sequence,
227911c0d8fdSPaul Kocialkowski 				    ARRAY_SIZE(ov8865_init_sequence));
228011c0d8fdSPaul Kocialkowski 	if (ret) {
228111c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to write init sequence\n");
228211c0d8fdSPaul Kocialkowski 		return ret;
228311c0d8fdSPaul Kocialkowski 	}
228411c0d8fdSPaul Kocialkowski 
228511c0d8fdSPaul Kocialkowski 	ret = ov8865_charge_pump_configure(sensor);
228611c0d8fdSPaul Kocialkowski 	if (ret) {
228711c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure pad\n");
228811c0d8fdSPaul Kocialkowski 		return ret;
228911c0d8fdSPaul Kocialkowski 	}
229011c0d8fdSPaul Kocialkowski 
229111c0d8fdSPaul Kocialkowski 	ret = ov8865_mipi_configure(sensor);
229211c0d8fdSPaul Kocialkowski 	if (ret) {
229311c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure MIPI\n");
229411c0d8fdSPaul Kocialkowski 		return ret;
229511c0d8fdSPaul Kocialkowski 	}
229611c0d8fdSPaul Kocialkowski 
229711c0d8fdSPaul Kocialkowski 	ret = ov8865_isp_configure(sensor);
229811c0d8fdSPaul Kocialkowski 	if (ret) {
229911c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure ISP\n");
230011c0d8fdSPaul Kocialkowski 		return ret;
230111c0d8fdSPaul Kocialkowski 	}
230211c0d8fdSPaul Kocialkowski 
230311c0d8fdSPaul Kocialkowski 	ret = ov8865_black_level_configure(sensor);
230411c0d8fdSPaul Kocialkowski 	if (ret) {
230511c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure black level\n");
230611c0d8fdSPaul Kocialkowski 		return ret;
230711c0d8fdSPaul Kocialkowski 	}
230811c0d8fdSPaul Kocialkowski 
230911c0d8fdSPaul Kocialkowski 	/* Configure current mode. */
231011c0d8fdSPaul Kocialkowski 	ret = ov8865_state_configure(sensor, sensor->state.mode,
231111c0d8fdSPaul Kocialkowski 				     sensor->state.mbus_code);
231211c0d8fdSPaul Kocialkowski 	if (ret) {
231311c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure state\n");
231411c0d8fdSPaul Kocialkowski 		return ret;
231511c0d8fdSPaul Kocialkowski 	}
231611c0d8fdSPaul Kocialkowski 
231711c0d8fdSPaul Kocialkowski 	return 0;
231811c0d8fdSPaul Kocialkowski }
231911c0d8fdSPaul Kocialkowski 
232011c0d8fdSPaul Kocialkowski static int ov8865_sensor_power(struct ov8865_sensor *sensor, bool on)
232111c0d8fdSPaul Kocialkowski {
232211c0d8fdSPaul Kocialkowski 	/* Keep initialized to zero for disable label. */
232311c0d8fdSPaul Kocialkowski 	int ret = 0;
232411c0d8fdSPaul Kocialkowski 
232511c0d8fdSPaul Kocialkowski 	if (on) {
232611c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->reset, 1);
232711c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->powerdown, 1);
232811c0d8fdSPaul Kocialkowski 
232911c0d8fdSPaul Kocialkowski 		ret = regulator_enable(sensor->dovdd);
233011c0d8fdSPaul Kocialkowski 		if (ret) {
233111c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev,
233211c0d8fdSPaul Kocialkowski 				"failed to enable DOVDD regulator\n");
233311c0d8fdSPaul Kocialkowski 			goto disable;
233411c0d8fdSPaul Kocialkowski 		}
233511c0d8fdSPaul Kocialkowski 
233611c0d8fdSPaul Kocialkowski 		ret = regulator_enable(sensor->avdd);
233711c0d8fdSPaul Kocialkowski 		if (ret) {
233811c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev,
233911c0d8fdSPaul Kocialkowski 				"failed to enable AVDD regulator\n");
234011c0d8fdSPaul Kocialkowski 			goto disable;
234111c0d8fdSPaul Kocialkowski 		}
234211c0d8fdSPaul Kocialkowski 
234311c0d8fdSPaul Kocialkowski 		ret = regulator_enable(sensor->dvdd);
234411c0d8fdSPaul Kocialkowski 		if (ret) {
234511c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev,
234611c0d8fdSPaul Kocialkowski 				"failed to enable DVDD regulator\n");
234711c0d8fdSPaul Kocialkowski 			goto disable;
234811c0d8fdSPaul Kocialkowski 		}
234911c0d8fdSPaul Kocialkowski 
235011c0d8fdSPaul Kocialkowski 		ret = clk_prepare_enable(sensor->extclk);
235111c0d8fdSPaul Kocialkowski 		if (ret) {
235211c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev, "failed to enable EXTCLK clock\n");
235311c0d8fdSPaul Kocialkowski 			goto disable;
235411c0d8fdSPaul Kocialkowski 		}
235511c0d8fdSPaul Kocialkowski 
235611c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->reset, 0);
235711c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->powerdown, 0);
235811c0d8fdSPaul Kocialkowski 
235911c0d8fdSPaul Kocialkowski 		/* Time to enter streaming mode according to power timings. */
236011c0d8fdSPaul Kocialkowski 		usleep_range(10000, 12000);
236111c0d8fdSPaul Kocialkowski 	} else {
236211c0d8fdSPaul Kocialkowski disable:
236311c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->powerdown, 1);
236411c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->reset, 1);
236511c0d8fdSPaul Kocialkowski 
236611c0d8fdSPaul Kocialkowski 		clk_disable_unprepare(sensor->extclk);
236711c0d8fdSPaul Kocialkowski 
236811c0d8fdSPaul Kocialkowski 		regulator_disable(sensor->dvdd);
236911c0d8fdSPaul Kocialkowski 		regulator_disable(sensor->avdd);
237011c0d8fdSPaul Kocialkowski 		regulator_disable(sensor->dovdd);
237111c0d8fdSPaul Kocialkowski 	}
237211c0d8fdSPaul Kocialkowski 
237311c0d8fdSPaul Kocialkowski 	return ret;
237411c0d8fdSPaul Kocialkowski }
237511c0d8fdSPaul Kocialkowski 
237611c0d8fdSPaul Kocialkowski /* Controls */
237711c0d8fdSPaul Kocialkowski 
237811c0d8fdSPaul Kocialkowski static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl)
237911c0d8fdSPaul Kocialkowski {
238011c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev = ov8865_ctrl_subdev(ctrl);
238111c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
238211c0d8fdSPaul Kocialkowski 	unsigned int index;
238311c0d8fdSPaul Kocialkowski 	int ret;
238411c0d8fdSPaul Kocialkowski 
238511c0d8fdSPaul Kocialkowski 	/* Wait for the sensor to be on before setting controls. */
238611c0d8fdSPaul Kocialkowski 	if (pm_runtime_suspended(sensor->dev))
238711c0d8fdSPaul Kocialkowski 		return 0;
238811c0d8fdSPaul Kocialkowski 
238911c0d8fdSPaul Kocialkowski 	switch (ctrl->id) {
239011c0d8fdSPaul Kocialkowski 	case V4L2_CID_EXPOSURE:
239111c0d8fdSPaul Kocialkowski 		ret = ov8865_exposure_configure(sensor, ctrl->val);
239211c0d8fdSPaul Kocialkowski 		if (ret)
239311c0d8fdSPaul Kocialkowski 			return ret;
239411c0d8fdSPaul Kocialkowski 		break;
239511c0d8fdSPaul Kocialkowski 	case V4L2_CID_GAIN:
239611c0d8fdSPaul Kocialkowski 		ret = ov8865_gain_configure(sensor, ctrl->val);
239711c0d8fdSPaul Kocialkowski 		if (ret)
239811c0d8fdSPaul Kocialkowski 			return ret;
239911c0d8fdSPaul Kocialkowski 		break;
240011c0d8fdSPaul Kocialkowski 	case V4L2_CID_RED_BALANCE:
240111c0d8fdSPaul Kocialkowski 		return ov8865_red_balance_configure(sensor, ctrl->val);
240211c0d8fdSPaul Kocialkowski 	case V4L2_CID_BLUE_BALANCE:
240311c0d8fdSPaul Kocialkowski 		return ov8865_blue_balance_configure(sensor, ctrl->val);
240411c0d8fdSPaul Kocialkowski 	case V4L2_CID_HFLIP:
240511c0d8fdSPaul Kocialkowski 		return ov8865_flip_horz_configure(sensor, !!ctrl->val);
240611c0d8fdSPaul Kocialkowski 	case V4L2_CID_VFLIP:
240711c0d8fdSPaul Kocialkowski 		return ov8865_flip_vert_configure(sensor, !!ctrl->val);
240811c0d8fdSPaul Kocialkowski 	case V4L2_CID_TEST_PATTERN:
240911c0d8fdSPaul Kocialkowski 		index = (unsigned int)ctrl->val;
241011c0d8fdSPaul Kocialkowski 		return ov8865_test_pattern_configure(sensor, index);
241111c0d8fdSPaul Kocialkowski 	default:
241211c0d8fdSPaul Kocialkowski 		return -EINVAL;
241311c0d8fdSPaul Kocialkowski 	}
241411c0d8fdSPaul Kocialkowski 
241511c0d8fdSPaul Kocialkowski 	return 0;
241611c0d8fdSPaul Kocialkowski }
241711c0d8fdSPaul Kocialkowski 
241811c0d8fdSPaul Kocialkowski static const struct v4l2_ctrl_ops ov8865_ctrl_ops = {
241911c0d8fdSPaul Kocialkowski 	.s_ctrl			= ov8865_s_ctrl,
242011c0d8fdSPaul Kocialkowski };
242111c0d8fdSPaul Kocialkowski 
242211c0d8fdSPaul Kocialkowski static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
242311c0d8fdSPaul Kocialkowski {
242411c0d8fdSPaul Kocialkowski 	struct ov8865_ctrls *ctrls = &sensor->ctrls;
242511c0d8fdSPaul Kocialkowski 	struct v4l2_ctrl_handler *handler = &ctrls->handler;
242611c0d8fdSPaul Kocialkowski 	const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops;
242711c0d8fdSPaul Kocialkowski 	int ret;
242811c0d8fdSPaul Kocialkowski 
242911c0d8fdSPaul Kocialkowski 	v4l2_ctrl_handler_init(handler, 32);
243011c0d8fdSPaul Kocialkowski 
243111c0d8fdSPaul Kocialkowski 	/* Use our mutex for ctrl locking. */
243211c0d8fdSPaul Kocialkowski 	handler->lock = &sensor->mutex;
243311c0d8fdSPaul Kocialkowski 
243411c0d8fdSPaul Kocialkowski 	/* Exposure */
243511c0d8fdSPaul Kocialkowski 
243611c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 16, 1048575, 16,
243711c0d8fdSPaul Kocialkowski 			  512);
243811c0d8fdSPaul Kocialkowski 
243911c0d8fdSPaul Kocialkowski 	/* Gain */
244011c0d8fdSPaul Kocialkowski 
244111c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_GAIN, 128, 8191, 128, 128);
244211c0d8fdSPaul Kocialkowski 
244311c0d8fdSPaul Kocialkowski 	/* White Balance */
244411c0d8fdSPaul Kocialkowski 
244511c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_RED_BALANCE, 1, 32767, 1,
244611c0d8fdSPaul Kocialkowski 			  1024);
244711c0d8fdSPaul Kocialkowski 
244811c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_BLUE_BALANCE, 1, 32767, 1,
244911c0d8fdSPaul Kocialkowski 			  1024);
245011c0d8fdSPaul Kocialkowski 
245111c0d8fdSPaul Kocialkowski 	/* Flip */
245211c0d8fdSPaul Kocialkowski 
245311c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
245411c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
245511c0d8fdSPaul Kocialkowski 
245611c0d8fdSPaul Kocialkowski 	/* Test Pattern */
245711c0d8fdSPaul Kocialkowski 
245811c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std_menu_items(handler, ops, V4L2_CID_TEST_PATTERN,
245911c0d8fdSPaul Kocialkowski 				     ARRAY_SIZE(ov8865_test_pattern_menu) - 1,
246011c0d8fdSPaul Kocialkowski 				     0, 0, ov8865_test_pattern_menu);
246111c0d8fdSPaul Kocialkowski 
246211c0d8fdSPaul Kocialkowski 	/* MIPI CSI-2 */
246311c0d8fdSPaul Kocialkowski 
246411c0d8fdSPaul Kocialkowski 	ctrls->link_freq =
246511c0d8fdSPaul Kocialkowski 		v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
246611c0d8fdSPaul Kocialkowski 				       ARRAY_SIZE(ov8865_link_freq_menu) - 1,
246711c0d8fdSPaul Kocialkowski 				       0, ov8865_link_freq_menu);
246811c0d8fdSPaul Kocialkowski 
246911c0d8fdSPaul Kocialkowski 	ctrls->pixel_rate =
247011c0d8fdSPaul Kocialkowski 		v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 1,
247111c0d8fdSPaul Kocialkowski 				  INT_MAX, 1, 1);
247211c0d8fdSPaul Kocialkowski 
247311c0d8fdSPaul Kocialkowski 	if (handler->error) {
247411c0d8fdSPaul Kocialkowski 		ret = handler->error;
247511c0d8fdSPaul Kocialkowski 		goto error_ctrls;
247611c0d8fdSPaul Kocialkowski 	}
247711c0d8fdSPaul Kocialkowski 
247811c0d8fdSPaul Kocialkowski 	ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
247911c0d8fdSPaul Kocialkowski 	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
248011c0d8fdSPaul Kocialkowski 
248111c0d8fdSPaul Kocialkowski 	sensor->subdev.ctrl_handler = handler;
248211c0d8fdSPaul Kocialkowski 
248311c0d8fdSPaul Kocialkowski 	return 0;
248411c0d8fdSPaul Kocialkowski 
248511c0d8fdSPaul Kocialkowski error_ctrls:
248611c0d8fdSPaul Kocialkowski 	v4l2_ctrl_handler_free(handler);
248711c0d8fdSPaul Kocialkowski 
248811c0d8fdSPaul Kocialkowski 	return ret;
248911c0d8fdSPaul Kocialkowski }
249011c0d8fdSPaul Kocialkowski 
249111c0d8fdSPaul Kocialkowski /* Subdev Video Operations */
249211c0d8fdSPaul Kocialkowski 
249311c0d8fdSPaul Kocialkowski static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable)
249411c0d8fdSPaul Kocialkowski {
249511c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
249611c0d8fdSPaul Kocialkowski 	struct ov8865_state *state = &sensor->state;
249711c0d8fdSPaul Kocialkowski 	int ret;
249811c0d8fdSPaul Kocialkowski 
249911c0d8fdSPaul Kocialkowski 	if (enable) {
2500586ee057SMauro Carvalho Chehab 		ret = pm_runtime_resume_and_get(sensor->dev);
2501586ee057SMauro Carvalho Chehab 		if (ret < 0)
250211c0d8fdSPaul Kocialkowski 			return ret;
250311c0d8fdSPaul Kocialkowski 	}
250411c0d8fdSPaul Kocialkowski 
250511c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
250611c0d8fdSPaul Kocialkowski 	ret = ov8865_sw_standby(sensor, !enable);
250711c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
250811c0d8fdSPaul Kocialkowski 
250911c0d8fdSPaul Kocialkowski 	if (ret)
251011c0d8fdSPaul Kocialkowski 		return ret;
251111c0d8fdSPaul Kocialkowski 
251211c0d8fdSPaul Kocialkowski 	state->streaming = !!enable;
251311c0d8fdSPaul Kocialkowski 
251411c0d8fdSPaul Kocialkowski 	if (!enable)
251511c0d8fdSPaul Kocialkowski 		pm_runtime_put(sensor->dev);
251611c0d8fdSPaul Kocialkowski 
251711c0d8fdSPaul Kocialkowski 	return 0;
251811c0d8fdSPaul Kocialkowski }
251911c0d8fdSPaul Kocialkowski 
252011c0d8fdSPaul Kocialkowski static int ov8865_g_frame_interval(struct v4l2_subdev *subdev,
252111c0d8fdSPaul Kocialkowski 				   struct v4l2_subdev_frame_interval *interval)
252211c0d8fdSPaul Kocialkowski {
252311c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
252411c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode;
252511c0d8fdSPaul Kocialkowski 
252611c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
252711c0d8fdSPaul Kocialkowski 
252811c0d8fdSPaul Kocialkowski 	mode = sensor->state.mode;
252911c0d8fdSPaul Kocialkowski 	interval->interval = mode->frame_interval;
253011c0d8fdSPaul Kocialkowski 
253111c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
253211c0d8fdSPaul Kocialkowski 
2533a1946cafSYang Li 	return 0;
253411c0d8fdSPaul Kocialkowski }
253511c0d8fdSPaul Kocialkowski 
253611c0d8fdSPaul Kocialkowski static const struct v4l2_subdev_video_ops ov8865_subdev_video_ops = {
253711c0d8fdSPaul Kocialkowski 	.s_stream		= ov8865_s_stream,
253811c0d8fdSPaul Kocialkowski 	.g_frame_interval	= ov8865_g_frame_interval,
253911c0d8fdSPaul Kocialkowski 	.s_frame_interval	= ov8865_g_frame_interval,
254011c0d8fdSPaul Kocialkowski };
254111c0d8fdSPaul Kocialkowski 
254211c0d8fdSPaul Kocialkowski /* Subdev Pad Operations */
254311c0d8fdSPaul Kocialkowski 
254411c0d8fdSPaul Kocialkowski static int ov8865_enum_mbus_code(struct v4l2_subdev *subdev,
25450d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
254611c0d8fdSPaul Kocialkowski 				 struct v4l2_subdev_mbus_code_enum *code_enum)
254711c0d8fdSPaul Kocialkowski {
254811c0d8fdSPaul Kocialkowski 	if (code_enum->index >= ARRAY_SIZE(ov8865_mbus_codes))
254911c0d8fdSPaul Kocialkowski 		return -EINVAL;
255011c0d8fdSPaul Kocialkowski 
255111c0d8fdSPaul Kocialkowski 	code_enum->code = ov8865_mbus_codes[code_enum->index];
255211c0d8fdSPaul Kocialkowski 
255311c0d8fdSPaul Kocialkowski 	return 0;
255411c0d8fdSPaul Kocialkowski }
255511c0d8fdSPaul Kocialkowski 
255611c0d8fdSPaul Kocialkowski static void ov8865_mbus_format_fill(struct v4l2_mbus_framefmt *mbus_format,
255711c0d8fdSPaul Kocialkowski 				    u32 mbus_code,
255811c0d8fdSPaul Kocialkowski 				    const struct ov8865_mode *mode)
255911c0d8fdSPaul Kocialkowski {
256011c0d8fdSPaul Kocialkowski 	mbus_format->width = mode->output_size_x;
256111c0d8fdSPaul Kocialkowski 	mbus_format->height = mode->output_size_y;
256211c0d8fdSPaul Kocialkowski 	mbus_format->code = mbus_code;
256311c0d8fdSPaul Kocialkowski 
256411c0d8fdSPaul Kocialkowski 	mbus_format->field = V4L2_FIELD_NONE;
256511c0d8fdSPaul Kocialkowski 	mbus_format->colorspace = V4L2_COLORSPACE_RAW;
256611c0d8fdSPaul Kocialkowski 	mbus_format->ycbcr_enc =
256711c0d8fdSPaul Kocialkowski 		V4L2_MAP_YCBCR_ENC_DEFAULT(mbus_format->colorspace);
256811c0d8fdSPaul Kocialkowski 	mbus_format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
256911c0d8fdSPaul Kocialkowski 	mbus_format->xfer_func =
257011c0d8fdSPaul Kocialkowski 		V4L2_MAP_XFER_FUNC_DEFAULT(mbus_format->colorspace);
257111c0d8fdSPaul Kocialkowski }
257211c0d8fdSPaul Kocialkowski 
257311c0d8fdSPaul Kocialkowski static int ov8865_get_fmt(struct v4l2_subdev *subdev,
25740d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
257511c0d8fdSPaul Kocialkowski 			  struct v4l2_subdev_format *format)
257611c0d8fdSPaul Kocialkowski {
257711c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
257811c0d8fdSPaul Kocialkowski 	struct v4l2_mbus_framefmt *mbus_format = &format->format;
257911c0d8fdSPaul Kocialkowski 
258011c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
258111c0d8fdSPaul Kocialkowski 
258211c0d8fdSPaul Kocialkowski 	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
25830d346d2aSTomi Valkeinen 		*mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state,
258411c0d8fdSPaul Kocialkowski 							   format->pad);
258511c0d8fdSPaul Kocialkowski 	else
258611c0d8fdSPaul Kocialkowski 		ov8865_mbus_format_fill(mbus_format, sensor->state.mbus_code,
258711c0d8fdSPaul Kocialkowski 					sensor->state.mode);
258811c0d8fdSPaul Kocialkowski 
258911c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
259011c0d8fdSPaul Kocialkowski 
259111c0d8fdSPaul Kocialkowski 	return 0;
259211c0d8fdSPaul Kocialkowski }
259311c0d8fdSPaul Kocialkowski 
259411c0d8fdSPaul Kocialkowski static int ov8865_set_fmt(struct v4l2_subdev *subdev,
25950d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
259611c0d8fdSPaul Kocialkowski 			  struct v4l2_subdev_format *format)
259711c0d8fdSPaul Kocialkowski {
259811c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
259911c0d8fdSPaul Kocialkowski 	struct v4l2_mbus_framefmt *mbus_format = &format->format;
260011c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode;
260111c0d8fdSPaul Kocialkowski 	u32 mbus_code = 0;
260211c0d8fdSPaul Kocialkowski 	unsigned int index;
260311c0d8fdSPaul Kocialkowski 	int ret = 0;
260411c0d8fdSPaul Kocialkowski 
260511c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
260611c0d8fdSPaul Kocialkowski 
260711c0d8fdSPaul Kocialkowski 	if (sensor->state.streaming) {
260811c0d8fdSPaul Kocialkowski 		ret = -EBUSY;
260911c0d8fdSPaul Kocialkowski 		goto complete;
261011c0d8fdSPaul Kocialkowski 	}
261111c0d8fdSPaul Kocialkowski 
261211c0d8fdSPaul Kocialkowski 	/* Try to find requested mbus code. */
261311c0d8fdSPaul Kocialkowski 	for (index = 0; index < ARRAY_SIZE(ov8865_mbus_codes); index++) {
261411c0d8fdSPaul Kocialkowski 		if (ov8865_mbus_codes[index] == mbus_format->code) {
261511c0d8fdSPaul Kocialkowski 			mbus_code = mbus_format->code;
261611c0d8fdSPaul Kocialkowski 			break;
261711c0d8fdSPaul Kocialkowski 		}
261811c0d8fdSPaul Kocialkowski 	}
261911c0d8fdSPaul Kocialkowski 
262011c0d8fdSPaul Kocialkowski 	/* Fallback to default. */
262111c0d8fdSPaul Kocialkowski 	if (!mbus_code)
262211c0d8fdSPaul Kocialkowski 		mbus_code = ov8865_mbus_codes[0];
262311c0d8fdSPaul Kocialkowski 
262411c0d8fdSPaul Kocialkowski 	/* Find the mode with nearest dimensions. */
262511c0d8fdSPaul Kocialkowski 	mode = v4l2_find_nearest_size(ov8865_modes, ARRAY_SIZE(ov8865_modes),
262611c0d8fdSPaul Kocialkowski 				      output_size_x, output_size_y,
262711c0d8fdSPaul Kocialkowski 				      mbus_format->width, mbus_format->height);
262811c0d8fdSPaul Kocialkowski 	if (!mode) {
262911c0d8fdSPaul Kocialkowski 		ret = -EINVAL;
263011c0d8fdSPaul Kocialkowski 		goto complete;
263111c0d8fdSPaul Kocialkowski 	}
263211c0d8fdSPaul Kocialkowski 
263311c0d8fdSPaul Kocialkowski 	ov8865_mbus_format_fill(mbus_format, mbus_code, mode);
263411c0d8fdSPaul Kocialkowski 
263511c0d8fdSPaul Kocialkowski 	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
26360d346d2aSTomi Valkeinen 		*v4l2_subdev_get_try_format(subdev, sd_state, format->pad) =
263711c0d8fdSPaul Kocialkowski 			*mbus_format;
263811c0d8fdSPaul Kocialkowski 	else if (sensor->state.mode != mode ||
263911c0d8fdSPaul Kocialkowski 		 sensor->state.mbus_code != mbus_code)
264011c0d8fdSPaul Kocialkowski 		ret = ov8865_state_configure(sensor, mode, mbus_code);
264111c0d8fdSPaul Kocialkowski 
264211c0d8fdSPaul Kocialkowski complete:
264311c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
264411c0d8fdSPaul Kocialkowski 
264511c0d8fdSPaul Kocialkowski 	return ret;
264611c0d8fdSPaul Kocialkowski }
264711c0d8fdSPaul Kocialkowski 
264811c0d8fdSPaul Kocialkowski static int ov8865_enum_frame_size(struct v4l2_subdev *subdev,
26490d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
265011c0d8fdSPaul Kocialkowski 				  struct v4l2_subdev_frame_size_enum *size_enum)
265111c0d8fdSPaul Kocialkowski {
265211c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode;
265311c0d8fdSPaul Kocialkowski 
265411c0d8fdSPaul Kocialkowski 	if (size_enum->index >= ARRAY_SIZE(ov8865_modes))
265511c0d8fdSPaul Kocialkowski 		return -EINVAL;
265611c0d8fdSPaul Kocialkowski 
265711c0d8fdSPaul Kocialkowski 	mode = &ov8865_modes[size_enum->index];
265811c0d8fdSPaul Kocialkowski 
265911c0d8fdSPaul Kocialkowski 	size_enum->min_width = size_enum->max_width = mode->output_size_x;
266011c0d8fdSPaul Kocialkowski 	size_enum->min_height = size_enum->max_height = mode->output_size_y;
266111c0d8fdSPaul Kocialkowski 
266211c0d8fdSPaul Kocialkowski 	return 0;
266311c0d8fdSPaul Kocialkowski }
266411c0d8fdSPaul Kocialkowski 
266511c0d8fdSPaul Kocialkowski static int ov8865_enum_frame_interval(struct v4l2_subdev *subdev,
26660d346d2aSTomi Valkeinen 				      struct v4l2_subdev_state *sd_state,
266711c0d8fdSPaul Kocialkowski 				      struct v4l2_subdev_frame_interval_enum *interval_enum)
266811c0d8fdSPaul Kocialkowski {
266911c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode = NULL;
267011c0d8fdSPaul Kocialkowski 	unsigned int mode_index;
267111c0d8fdSPaul Kocialkowski 	unsigned int interval_index;
267211c0d8fdSPaul Kocialkowski 
267311c0d8fdSPaul Kocialkowski 	if (interval_enum->index > 0)
267411c0d8fdSPaul Kocialkowski 		return -EINVAL;
267511c0d8fdSPaul Kocialkowski 	/*
267611c0d8fdSPaul Kocialkowski 	 * Multiple modes with the same dimensions may have different frame
267711c0d8fdSPaul Kocialkowski 	 * intervals, so look up each relevant mode.
267811c0d8fdSPaul Kocialkowski 	 */
267911c0d8fdSPaul Kocialkowski 	for (mode_index = 0, interval_index = 0;
268011c0d8fdSPaul Kocialkowski 	     mode_index < ARRAY_SIZE(ov8865_modes); mode_index++) {
268111c0d8fdSPaul Kocialkowski 		mode = &ov8865_modes[mode_index];
268211c0d8fdSPaul Kocialkowski 
268311c0d8fdSPaul Kocialkowski 		if (mode->output_size_x == interval_enum->width &&
268411c0d8fdSPaul Kocialkowski 		    mode->output_size_y == interval_enum->height) {
268511c0d8fdSPaul Kocialkowski 			if (interval_index == interval_enum->index)
268611c0d8fdSPaul Kocialkowski 				break;
268711c0d8fdSPaul Kocialkowski 
268811c0d8fdSPaul Kocialkowski 			interval_index++;
268911c0d8fdSPaul Kocialkowski 		}
269011c0d8fdSPaul Kocialkowski 	}
269111c0d8fdSPaul Kocialkowski 
269245dbd70cSPaul Kocialkowski 	if (mode_index == ARRAY_SIZE(ov8865_modes))
269311c0d8fdSPaul Kocialkowski 		return -EINVAL;
269411c0d8fdSPaul Kocialkowski 
269511c0d8fdSPaul Kocialkowski 	interval_enum->interval = mode->frame_interval;
269611c0d8fdSPaul Kocialkowski 
269711c0d8fdSPaul Kocialkowski 	return 0;
269811c0d8fdSPaul Kocialkowski }
269911c0d8fdSPaul Kocialkowski 
270011c0d8fdSPaul Kocialkowski static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = {
270111c0d8fdSPaul Kocialkowski 	.enum_mbus_code		= ov8865_enum_mbus_code,
270211c0d8fdSPaul Kocialkowski 	.get_fmt		= ov8865_get_fmt,
270311c0d8fdSPaul Kocialkowski 	.set_fmt		= ov8865_set_fmt,
270411c0d8fdSPaul Kocialkowski 	.enum_frame_size	= ov8865_enum_frame_size,
270511c0d8fdSPaul Kocialkowski 	.enum_frame_interval	= ov8865_enum_frame_interval,
270611c0d8fdSPaul Kocialkowski };
270711c0d8fdSPaul Kocialkowski 
270811c0d8fdSPaul Kocialkowski static const struct v4l2_subdev_ops ov8865_subdev_ops = {
270911c0d8fdSPaul Kocialkowski 	.video		= &ov8865_subdev_video_ops,
271011c0d8fdSPaul Kocialkowski 	.pad		= &ov8865_subdev_pad_ops,
271111c0d8fdSPaul Kocialkowski };
271211c0d8fdSPaul Kocialkowski 
271311c0d8fdSPaul Kocialkowski static int ov8865_suspend(struct device *dev)
271411c0d8fdSPaul Kocialkowski {
271511c0d8fdSPaul Kocialkowski 	struct i2c_client *client = to_i2c_client(dev);
271611c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
271711c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
271811c0d8fdSPaul Kocialkowski 	struct ov8865_state *state = &sensor->state;
271911c0d8fdSPaul Kocialkowski 	int ret = 0;
272011c0d8fdSPaul Kocialkowski 
272111c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
272211c0d8fdSPaul Kocialkowski 
272311c0d8fdSPaul Kocialkowski 	if (state->streaming) {
272411c0d8fdSPaul Kocialkowski 		ret = ov8865_sw_standby(sensor, true);
272511c0d8fdSPaul Kocialkowski 		if (ret)
272611c0d8fdSPaul Kocialkowski 			goto complete;
272711c0d8fdSPaul Kocialkowski 	}
272811c0d8fdSPaul Kocialkowski 
272911c0d8fdSPaul Kocialkowski 	ret = ov8865_sensor_power(sensor, false);
273011c0d8fdSPaul Kocialkowski 	if (ret)
273111c0d8fdSPaul Kocialkowski 		ov8865_sw_standby(sensor, false);
273211c0d8fdSPaul Kocialkowski 
273311c0d8fdSPaul Kocialkowski complete:
273411c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
273511c0d8fdSPaul Kocialkowski 
273611c0d8fdSPaul Kocialkowski 	return ret;
273711c0d8fdSPaul Kocialkowski }
273811c0d8fdSPaul Kocialkowski 
273911c0d8fdSPaul Kocialkowski static int ov8865_resume(struct device *dev)
274011c0d8fdSPaul Kocialkowski {
274111c0d8fdSPaul Kocialkowski 	struct i2c_client *client = to_i2c_client(dev);
274211c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
274311c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
274411c0d8fdSPaul Kocialkowski 	struct ov8865_state *state = &sensor->state;
274511c0d8fdSPaul Kocialkowski 	int ret = 0;
274611c0d8fdSPaul Kocialkowski 
274711c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
274811c0d8fdSPaul Kocialkowski 
274911c0d8fdSPaul Kocialkowski 	ret = ov8865_sensor_power(sensor, true);
275011c0d8fdSPaul Kocialkowski 	if (ret)
275111c0d8fdSPaul Kocialkowski 		goto complete;
275211c0d8fdSPaul Kocialkowski 
275311c0d8fdSPaul Kocialkowski 	ret = ov8865_sensor_init(sensor);
275411c0d8fdSPaul Kocialkowski 	if (ret)
275511c0d8fdSPaul Kocialkowski 		goto error_power;
275611c0d8fdSPaul Kocialkowski 
275711c0d8fdSPaul Kocialkowski 	ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
275811c0d8fdSPaul Kocialkowski 	if (ret)
275911c0d8fdSPaul Kocialkowski 		goto error_power;
276011c0d8fdSPaul Kocialkowski 
276111c0d8fdSPaul Kocialkowski 	if (state->streaming) {
276211c0d8fdSPaul Kocialkowski 		ret = ov8865_sw_standby(sensor, false);
276311c0d8fdSPaul Kocialkowski 		if (ret)
276411c0d8fdSPaul Kocialkowski 			goto error_power;
276511c0d8fdSPaul Kocialkowski 	}
276611c0d8fdSPaul Kocialkowski 
276711c0d8fdSPaul Kocialkowski 	goto complete;
276811c0d8fdSPaul Kocialkowski 
276911c0d8fdSPaul Kocialkowski error_power:
277011c0d8fdSPaul Kocialkowski 	ov8865_sensor_power(sensor, false);
277111c0d8fdSPaul Kocialkowski 
277211c0d8fdSPaul Kocialkowski complete:
277311c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
277411c0d8fdSPaul Kocialkowski 
277511c0d8fdSPaul Kocialkowski 	return ret;
277611c0d8fdSPaul Kocialkowski }
277711c0d8fdSPaul Kocialkowski 
277811c0d8fdSPaul Kocialkowski static int ov8865_probe(struct i2c_client *client)
277911c0d8fdSPaul Kocialkowski {
278011c0d8fdSPaul Kocialkowski 	struct device *dev = &client->dev;
278111c0d8fdSPaul Kocialkowski 	struct fwnode_handle *handle;
278211c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor;
278311c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev;
278411c0d8fdSPaul Kocialkowski 	struct media_pad *pad;
278511c0d8fdSPaul Kocialkowski 	unsigned long rate;
278611c0d8fdSPaul Kocialkowski 	int ret;
278711c0d8fdSPaul Kocialkowski 
278811c0d8fdSPaul Kocialkowski 	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
278911c0d8fdSPaul Kocialkowski 	if (!sensor)
279011c0d8fdSPaul Kocialkowski 		return -ENOMEM;
279111c0d8fdSPaul Kocialkowski 
279211c0d8fdSPaul Kocialkowski 	sensor->dev = dev;
279311c0d8fdSPaul Kocialkowski 	sensor->i2c_client = client;
279411c0d8fdSPaul Kocialkowski 
279511c0d8fdSPaul Kocialkowski 	/* Graph Endpoint */
279611c0d8fdSPaul Kocialkowski 
279711c0d8fdSPaul Kocialkowski 	handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
279811c0d8fdSPaul Kocialkowski 	if (!handle) {
2799ea12d248SColin Ian King 		dev_err(dev, "unable to find endpoint node\n");
280011c0d8fdSPaul Kocialkowski 		return -EINVAL;
280111c0d8fdSPaul Kocialkowski 	}
280211c0d8fdSPaul Kocialkowski 
280311c0d8fdSPaul Kocialkowski 	sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY;
280411c0d8fdSPaul Kocialkowski 
280511c0d8fdSPaul Kocialkowski 	ret = v4l2_fwnode_endpoint_alloc_parse(handle, &sensor->endpoint);
280611c0d8fdSPaul Kocialkowski 	fwnode_handle_put(handle);
280711c0d8fdSPaul Kocialkowski 	if (ret) {
280811c0d8fdSPaul Kocialkowski 		dev_err(dev, "failed to parse endpoint node\n");
280911c0d8fdSPaul Kocialkowski 		return ret;
281011c0d8fdSPaul Kocialkowski 	}
281111c0d8fdSPaul Kocialkowski 
281211c0d8fdSPaul Kocialkowski 	/* GPIOs */
281311c0d8fdSPaul Kocialkowski 
281411c0d8fdSPaul Kocialkowski 	sensor->powerdown = devm_gpiod_get_optional(dev, "powerdown",
281511c0d8fdSPaul Kocialkowski 						    GPIOD_OUT_HIGH);
281611c0d8fdSPaul Kocialkowski 	if (IS_ERR(sensor->powerdown)) {
281711c0d8fdSPaul Kocialkowski 		ret = PTR_ERR(sensor->powerdown);
281811c0d8fdSPaul Kocialkowski 		goto error_endpoint;
281911c0d8fdSPaul Kocialkowski 	}
282011c0d8fdSPaul Kocialkowski 
282111c0d8fdSPaul Kocialkowski 	sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
282211c0d8fdSPaul Kocialkowski 	if (IS_ERR(sensor->reset)) {
282311c0d8fdSPaul Kocialkowski 		ret = PTR_ERR(sensor->reset);
282411c0d8fdSPaul Kocialkowski 		goto error_endpoint;
282511c0d8fdSPaul Kocialkowski 	}
282611c0d8fdSPaul Kocialkowski 
282711c0d8fdSPaul Kocialkowski 	/* Regulators */
282811c0d8fdSPaul Kocialkowski 
282911c0d8fdSPaul Kocialkowski 	/* DVDD: digital core */
283011c0d8fdSPaul Kocialkowski 	sensor->dvdd = devm_regulator_get(dev, "dvdd");
283111c0d8fdSPaul Kocialkowski 	if (IS_ERR(sensor->dvdd)) {
283211c0d8fdSPaul Kocialkowski 		dev_err(dev, "cannot get DVDD (digital core) regulator\n");
283311c0d8fdSPaul Kocialkowski 		ret = PTR_ERR(sensor->dvdd);
283411c0d8fdSPaul Kocialkowski 		goto error_endpoint;
283511c0d8fdSPaul Kocialkowski 	}
283611c0d8fdSPaul Kocialkowski 
283711c0d8fdSPaul Kocialkowski 	/* DOVDD: digital I/O */
283811c0d8fdSPaul Kocialkowski 	sensor->dovdd = devm_regulator_get(dev, "dovdd");
28396e7cca27SHans Verkuil 	if (IS_ERR(sensor->dovdd)) {
284011c0d8fdSPaul Kocialkowski 		dev_err(dev, "cannot get DOVDD (digital I/O) regulator\n");
28416e7cca27SHans Verkuil 		ret = PTR_ERR(sensor->dovdd);
284211c0d8fdSPaul Kocialkowski 		goto error_endpoint;
284311c0d8fdSPaul Kocialkowski 	}
284411c0d8fdSPaul Kocialkowski 
284511c0d8fdSPaul Kocialkowski 	/* AVDD: analog */
284611c0d8fdSPaul Kocialkowski 	sensor->avdd = devm_regulator_get(dev, "avdd");
284711c0d8fdSPaul Kocialkowski 	if (IS_ERR(sensor->avdd)) {
284811c0d8fdSPaul Kocialkowski 		dev_err(dev, "cannot get AVDD (analog) regulator\n");
28496e7cca27SHans Verkuil 		ret = PTR_ERR(sensor->avdd);
285011c0d8fdSPaul Kocialkowski 		goto error_endpoint;
285111c0d8fdSPaul Kocialkowski 	}
285211c0d8fdSPaul Kocialkowski 
285311c0d8fdSPaul Kocialkowski 	/* External Clock */
285411c0d8fdSPaul Kocialkowski 
285511c0d8fdSPaul Kocialkowski 	sensor->extclk = devm_clk_get(dev, NULL);
285611c0d8fdSPaul Kocialkowski 	if (IS_ERR(sensor->extclk)) {
285711c0d8fdSPaul Kocialkowski 		dev_err(dev, "failed to get external clock\n");
285811c0d8fdSPaul Kocialkowski 		ret = PTR_ERR(sensor->extclk);
285911c0d8fdSPaul Kocialkowski 		goto error_endpoint;
286011c0d8fdSPaul Kocialkowski 	}
286111c0d8fdSPaul Kocialkowski 
286211c0d8fdSPaul Kocialkowski 	rate = clk_get_rate(sensor->extclk);
286311c0d8fdSPaul Kocialkowski 	if (rate != OV8865_EXTCLK_RATE) {
286411c0d8fdSPaul Kocialkowski 		dev_err(dev, "clock rate %lu Hz is unsupported\n", rate);
286511c0d8fdSPaul Kocialkowski 		ret = -EINVAL;
286611c0d8fdSPaul Kocialkowski 		goto error_endpoint;
286711c0d8fdSPaul Kocialkowski 	}
286811c0d8fdSPaul Kocialkowski 
286911c0d8fdSPaul Kocialkowski 	/* Subdev, entity and pad */
287011c0d8fdSPaul Kocialkowski 
287111c0d8fdSPaul Kocialkowski 	subdev = &sensor->subdev;
287211c0d8fdSPaul Kocialkowski 	v4l2_i2c_subdev_init(subdev, client, &ov8865_subdev_ops);
287311c0d8fdSPaul Kocialkowski 
287411c0d8fdSPaul Kocialkowski 	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
287511c0d8fdSPaul Kocialkowski 	subdev->entity.function = MEDIA_ENT_F_CAM_SENSOR;
287611c0d8fdSPaul Kocialkowski 
287711c0d8fdSPaul Kocialkowski 	pad = &sensor->pad;
287811c0d8fdSPaul Kocialkowski 	pad->flags = MEDIA_PAD_FL_SOURCE;
287911c0d8fdSPaul Kocialkowski 
288011c0d8fdSPaul Kocialkowski 	ret = media_entity_pads_init(&subdev->entity, 1, pad);
288111c0d8fdSPaul Kocialkowski 	if (ret)
288211c0d8fdSPaul Kocialkowski 		goto error_entity;
288311c0d8fdSPaul Kocialkowski 
288411c0d8fdSPaul Kocialkowski 	/* Mutex */
288511c0d8fdSPaul Kocialkowski 
288611c0d8fdSPaul Kocialkowski 	mutex_init(&sensor->mutex);
288711c0d8fdSPaul Kocialkowski 
288811c0d8fdSPaul Kocialkowski 	/* Sensor */
288911c0d8fdSPaul Kocialkowski 
289011c0d8fdSPaul Kocialkowski 	ret = ov8865_ctrls_init(sensor);
289111c0d8fdSPaul Kocialkowski 	if (ret)
289211c0d8fdSPaul Kocialkowski 		goto error_mutex;
289311c0d8fdSPaul Kocialkowski 
2894*6e1c9bc9SHans de Goede 	mutex_lock(&sensor->mutex);
289511c0d8fdSPaul Kocialkowski 	ret = ov8865_state_init(sensor);
2896*6e1c9bc9SHans de Goede 	mutex_unlock(&sensor->mutex);
289711c0d8fdSPaul Kocialkowski 	if (ret)
289811c0d8fdSPaul Kocialkowski 		goto error_ctrls;
289911c0d8fdSPaul Kocialkowski 
290011c0d8fdSPaul Kocialkowski 	/* Runtime PM */
290111c0d8fdSPaul Kocialkowski 
290211c0d8fdSPaul Kocialkowski 	pm_runtime_set_suspended(sensor->dev);
2903d2484fbfSDaniel Scally 	pm_runtime_enable(sensor->dev);
290411c0d8fdSPaul Kocialkowski 
290511c0d8fdSPaul Kocialkowski 	/* V4L2 subdev register */
290611c0d8fdSPaul Kocialkowski 
290715786f7bSSakari Ailus 	ret = v4l2_async_register_subdev_sensor(subdev);
290811c0d8fdSPaul Kocialkowski 	if (ret)
290911c0d8fdSPaul Kocialkowski 		goto error_pm;
291011c0d8fdSPaul Kocialkowski 
291111c0d8fdSPaul Kocialkowski 	return 0;
291211c0d8fdSPaul Kocialkowski 
291311c0d8fdSPaul Kocialkowski error_pm:
291411c0d8fdSPaul Kocialkowski 	pm_runtime_disable(sensor->dev);
291511c0d8fdSPaul Kocialkowski 
291611c0d8fdSPaul Kocialkowski error_ctrls:
291711c0d8fdSPaul Kocialkowski 	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
291811c0d8fdSPaul Kocialkowski 
291911c0d8fdSPaul Kocialkowski error_mutex:
292011c0d8fdSPaul Kocialkowski 	mutex_destroy(&sensor->mutex);
292111c0d8fdSPaul Kocialkowski 
292211c0d8fdSPaul Kocialkowski error_entity:
292311c0d8fdSPaul Kocialkowski 	media_entity_cleanup(&sensor->subdev.entity);
292411c0d8fdSPaul Kocialkowski 
292511c0d8fdSPaul Kocialkowski error_endpoint:
292611c0d8fdSPaul Kocialkowski 	v4l2_fwnode_endpoint_free(&sensor->endpoint);
292711c0d8fdSPaul Kocialkowski 
292811c0d8fdSPaul Kocialkowski 	return ret;
292911c0d8fdSPaul Kocialkowski }
293011c0d8fdSPaul Kocialkowski 
293111c0d8fdSPaul Kocialkowski static int ov8865_remove(struct i2c_client *client)
293211c0d8fdSPaul Kocialkowski {
293311c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
293411c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
293511c0d8fdSPaul Kocialkowski 
293611c0d8fdSPaul Kocialkowski 	v4l2_async_unregister_subdev(subdev);
293711c0d8fdSPaul Kocialkowski 	pm_runtime_disable(sensor->dev);
293811c0d8fdSPaul Kocialkowski 	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
293911c0d8fdSPaul Kocialkowski 	mutex_destroy(&sensor->mutex);
294011c0d8fdSPaul Kocialkowski 	media_entity_cleanup(&subdev->entity);
294111c0d8fdSPaul Kocialkowski 
294211c0d8fdSPaul Kocialkowski 	v4l2_fwnode_endpoint_free(&sensor->endpoint);
294311c0d8fdSPaul Kocialkowski 
294411c0d8fdSPaul Kocialkowski 	return 0;
294511c0d8fdSPaul Kocialkowski }
294611c0d8fdSPaul Kocialkowski 
294711c0d8fdSPaul Kocialkowski static const struct dev_pm_ops ov8865_pm_ops = {
294811c0d8fdSPaul Kocialkowski 	SET_RUNTIME_PM_OPS(ov8865_suspend, ov8865_resume, NULL)
294911c0d8fdSPaul Kocialkowski };
295011c0d8fdSPaul Kocialkowski 
295111c0d8fdSPaul Kocialkowski static const struct of_device_id ov8865_of_match[] = {
295211c0d8fdSPaul Kocialkowski 	{ .compatible = "ovti,ov8865" },
295311c0d8fdSPaul Kocialkowski 	{ }
295411c0d8fdSPaul Kocialkowski };
295511c0d8fdSPaul Kocialkowski MODULE_DEVICE_TABLE(of, ov8865_of_match);
295611c0d8fdSPaul Kocialkowski 
295711c0d8fdSPaul Kocialkowski static struct i2c_driver ov8865_driver = {
295811c0d8fdSPaul Kocialkowski 	.driver = {
295911c0d8fdSPaul Kocialkowski 		.name = "ov8865",
296011c0d8fdSPaul Kocialkowski 		.of_match_table = ov8865_of_match,
296111c0d8fdSPaul Kocialkowski 		.pm = &ov8865_pm_ops,
296211c0d8fdSPaul Kocialkowski 	},
296311c0d8fdSPaul Kocialkowski 	.probe_new = ov8865_probe,
296411c0d8fdSPaul Kocialkowski 	.remove	 = ov8865_remove,
296511c0d8fdSPaul Kocialkowski };
296611c0d8fdSPaul Kocialkowski 
296711c0d8fdSPaul Kocialkowski module_i2c_driver(ov8865_driver);
296811c0d8fdSPaul Kocialkowski 
296911c0d8fdSPaul Kocialkowski MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
297011c0d8fdSPaul Kocialkowski MODULE_DESCRIPTION("V4L2 driver for the OmniVision OV8865 image sensor");
297111c0d8fdSPaul Kocialkowski MODULE_LICENSE("GPL v2");
2972