xref: /openbmc/linux/drivers/media/i2c/ov8865.c (revision aaeb31c0)
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>
12dc69bc7aSDaniel Scally #include <linux/mod_devicetable.h>
1311c0d8fdSPaul Kocialkowski #include <linux/module.h>
1411c0d8fdSPaul Kocialkowski #include <linux/of_graph.h>
1511c0d8fdSPaul Kocialkowski #include <linux/pm_runtime.h>
1611c0d8fdSPaul Kocialkowski #include <linux/regulator/consumer.h>
1711c0d8fdSPaul Kocialkowski #include <linux/videodev2.h>
1811c0d8fdSPaul Kocialkowski #include <media/v4l2-ctrls.h>
1911c0d8fdSPaul Kocialkowski #include <media/v4l2-device.h>
2011c0d8fdSPaul Kocialkowski #include <media/v4l2-fwnode.h>
2111c0d8fdSPaul Kocialkowski #include <media/v4l2-image-sizes.h>
2211c0d8fdSPaul Kocialkowski #include <media/v4l2-mediabus.h>
2311c0d8fdSPaul Kocialkowski 
2411c0d8fdSPaul Kocialkowski /* Register definitions */
2511c0d8fdSPaul Kocialkowski 
2611c0d8fdSPaul Kocialkowski /* System */
2711c0d8fdSPaul Kocialkowski 
2811c0d8fdSPaul Kocialkowski #define OV8865_SW_STANDBY_REG			0x100
2911c0d8fdSPaul Kocialkowski #define OV8865_SW_STANDBY_STREAM_ON		BIT(0)
3011c0d8fdSPaul Kocialkowski 
3111c0d8fdSPaul Kocialkowski #define OV8865_SW_RESET_REG			0x103
3211c0d8fdSPaul Kocialkowski #define OV8865_SW_RESET_RESET			BIT(0)
3311c0d8fdSPaul Kocialkowski 
3411c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL0_REG			0x300
3511c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL0_PRE_DIV(v)		((v) & GENMASK(2, 0))
3611c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1_REG			0x301
3711c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1_MUL_H(v)		(((v) & GENMASK(9, 8)) >> 8)
3811c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL2_REG			0x302
3911c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL2_MUL_L(v)		((v) & GENMASK(7, 0))
4011c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL3_REG			0x303
4111c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL3_M_DIV(v)		(((v) - 1) & GENMASK(3, 0))
4211c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL4_REG			0x304
4311c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL4_MIPI_DIV(v)		((v) & GENMASK(1, 0))
4411c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL5_REG			0x305
4511c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL5_SYS_PRE_DIV(v)		((v) & GENMASK(1, 0))
4611c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL6_REG			0x306
4711c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL6_SYS_DIV(v)		(((v) - 1) & BIT(0))
4811c0d8fdSPaul Kocialkowski 
4911c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL8_REG			0x308
5011c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL9_REG			0x309
5111c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLA_REG			0x30a
5211c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLA_PRE_DIV_HALF(v)	(((v) - 1) & BIT(0))
5311c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLB_REG			0x30b
5411c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLB_PRE_DIV(v)		((v) & GENMASK(2, 0))
5511c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLC_REG			0x30c
5611c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLC_MUL_H(v)		(((v) & GENMASK(9, 8)) >> 8)
5711c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLD_REG			0x30d
5811c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLD_MUL_L(v)		((v) & GENMASK(7, 0))
5911c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLE_REG			0x30e
6011c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLE_SYS_DIV(v)		((v) & GENMASK(2, 0))
6111c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLF_REG			0x30f
6211c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRLF_SYS_PRE_DIV(v)		(((v) - 1) & GENMASK(3, 0))
6311c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL10_REG			0x310
6411c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL11_REG			0x311
6511c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL12_REG			0x312
6611c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL12_PRE_DIV_HALF(v)	((((v) - 1) << 4) & BIT(4))
6711c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL12_DAC_DIV(v)		(((v) - 1) & GENMASK(3, 0))
6811c0d8fdSPaul Kocialkowski 
6911c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1B_REG			0x31b
7011c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1C_REG			0x31c
7111c0d8fdSPaul Kocialkowski 
7211c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1E_REG			0x31e
7311c0d8fdSPaul Kocialkowski #define OV8865_PLL_CTRL1E_PLL1_NO_LAT		BIT(3)
7411c0d8fdSPaul Kocialkowski 
7511c0d8fdSPaul Kocialkowski #define OV8865_PAD_OEN0_REG			0x3000
7611c0d8fdSPaul Kocialkowski 
7711c0d8fdSPaul Kocialkowski #define OV8865_PAD_OEN2_REG			0x3002
7811c0d8fdSPaul Kocialkowski 
7911c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST5_REG			0x3005
8011c0d8fdSPaul Kocialkowski 
8111c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_HH_REG			0x300a
8211c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_HH_VALUE			0x00
8311c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_H_REG			0x300b
8411c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_H_VALUE			0x88
8511c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_L_REG			0x300c
8611c0d8fdSPaul Kocialkowski #define OV8865_CHIP_ID_L_VALUE			0x65
8711c0d8fdSPaul Kocialkowski #define OV8865_PAD_OUT2_REG			0x300d
8811c0d8fdSPaul Kocialkowski 
8911c0d8fdSPaul Kocialkowski #define OV8865_PAD_SEL2_REG			0x3010
9011c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_REG			0x3011
9111c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_DRIVE_STRENGTH_1X		(0 << 5)
9211c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_DRIVE_STRENGTH_2X		(1 << 5)
9311c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_DRIVE_STRENGTH_3X		(2 << 5)
9411c0d8fdSPaul Kocialkowski #define OV8865_PAD_PK_DRIVE_STRENGTH_4X		(3 << 5)
9511c0d8fdSPaul Kocialkowski 
9611c0d8fdSPaul Kocialkowski #define OV8865_PUMP_CLK_DIV_REG			0x3015
9711c0d8fdSPaul Kocialkowski #define OV8865_PUMP_CLK_DIV_PUMP_N(v)		(((v) << 4) & GENMASK(6, 4))
9811c0d8fdSPaul Kocialkowski #define OV8865_PUMP_CLK_DIV_PUMP_P(v)		((v) & GENMASK(2, 0))
9911c0d8fdSPaul Kocialkowski 
10011c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_REG		0x3018
10111c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_LANES(v)		((((v) - 1) << 5) & \
10211c0d8fdSPaul Kocialkowski 						 GENMASK(7, 5))
10311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_MIPI_EN		BIT(4)
10411c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_UNKNOWN		BIT(1)
10511c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL0_LANES_PD_MIPI	BIT(0)
10611c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL1_REG		0x3019
10711c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST0_REG			0x301a
10811c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST1_REG			0x301b
10911c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST2_REG			0x301c
11011c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST3_REG			0x301d
11111c0d8fdSPaul Kocialkowski #define OV8865_CLK_RST4_REG			0x301e
11211c0d8fdSPaul Kocialkowski 
11311c0d8fdSPaul Kocialkowski #define OV8865_PCLK_SEL_REG			0x3020
11411c0d8fdSPaul Kocialkowski #define OV8865_PCLK_SEL_PCLK_DIV_MASK		BIT(3)
11511c0d8fdSPaul Kocialkowski #define OV8865_PCLK_SEL_PCLK_DIV(v)		((((v) - 1) << 3) & BIT(3))
11611c0d8fdSPaul Kocialkowski 
11711c0d8fdSPaul Kocialkowski #define OV8865_MISC_CTRL_REG			0x3021
11811c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL2_REG		0x3022
11911c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL2_CLK_LANES_PD_MIPI	BIT(1)
12011c0d8fdSPaul Kocialkowski #define OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC	BIT(0)
12111c0d8fdSPaul Kocialkowski 
12211c0d8fdSPaul Kocialkowski #define OV8865_MIPI_BIT_SEL_REG			0x3031
12311c0d8fdSPaul Kocialkowski #define OV8865_MIPI_BIT_SEL(v)			(((v) << 0) & GENMASK(4, 0))
12411c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL0_REG			0x3032
12511c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL0_PLL1_SYS_SEL(v)		(((v) << 7) & BIT(7))
12611c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_REG			0x3033
12711c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_MIPI_EOF		BIT(5)
12811c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_UNKNOWN			BIT(2)
12911c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK	BIT(1)
13011c0d8fdSPaul Kocialkowski #define OV8865_CLK_SEL1_PLL_SCLK_SEL(v)		(((v) << 1) & BIT(1))
13111c0d8fdSPaul Kocialkowski 
13211c0d8fdSPaul Kocialkowski #define OV8865_SCLK_CTRL_REG			0x3106
13311c0d8fdSPaul Kocialkowski #define OV8865_SCLK_CTRL_SCLK_DIV(v)		(((v) << 4) & GENMASK(7, 4))
13411c0d8fdSPaul Kocialkowski #define OV8865_SCLK_CTRL_SCLK_PRE_DIV(v)	(((v) << 2) & GENMASK(3, 2))
13511c0d8fdSPaul Kocialkowski #define OV8865_SCLK_CTRL_UNKNOWN		BIT(0)
13611c0d8fdSPaul Kocialkowski 
13711c0d8fdSPaul Kocialkowski /* Exposure/gain */
13811c0d8fdSPaul Kocialkowski 
13911c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_HH_REG		0x3500
14011c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_HH(v)		(((v) & GENMASK(19, 16)) >> 16)
14111c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_H_REG		0x3501
14211c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_H(v)		(((v) & GENMASK(15, 8)) >> 8)
14311c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_L_REG		0x3502
14411c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_CTRL_L(v)		((v) & GENMASK(7, 0))
14511c0d8fdSPaul Kocialkowski #define OV8865_EXPOSURE_GAIN_MANUAL_REG		0x3503
146ca28690eSDaniel Scally #define OV8865_INTEGRATION_TIME_MARGIN		8
14711c0d8fdSPaul Kocialkowski 
14811c0d8fdSPaul Kocialkowski #define OV8865_GAIN_CTRL_H_REG			0x3508
14911c0d8fdSPaul Kocialkowski #define OV8865_GAIN_CTRL_H(v)			(((v) & GENMASK(12, 8)) >> 8)
15011c0d8fdSPaul Kocialkowski #define OV8865_GAIN_CTRL_L_REG			0x3509
15111c0d8fdSPaul Kocialkowski #define OV8865_GAIN_CTRL_L(v)			((v) & GENMASK(7, 0))
15211c0d8fdSPaul Kocialkowski 
15311c0d8fdSPaul Kocialkowski /* Timing */
15411c0d8fdSPaul Kocialkowski 
15511c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_X_H_REG		0x3800
15611c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_X_H(v)		(((v) & GENMASK(11, 8)) >> 8)
15711c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_X_L_REG		0x3801
15811c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_X_L(v)		((v) & GENMASK(7, 0))
15911c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_Y_H_REG		0x3802
16011c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_Y_H(v)		(((v) & GENMASK(11, 8)) >> 8)
16111c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_Y_L_REG		0x3803
16211c0d8fdSPaul Kocialkowski #define OV8865_CROP_START_Y_L(v)		((v) & GENMASK(7, 0))
16311c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_X_H_REG			0x3804
16411c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_X_H(v)			(((v) & GENMASK(11, 8)) >> 8)
16511c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_X_L_REG			0x3805
16611c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_X_L(v)			((v) & GENMASK(7, 0))
16711c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_Y_H_REG			0x3806
16811c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_Y_H(v)			(((v) & GENMASK(11, 8)) >> 8)
16911c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_Y_L_REG			0x3807
17011c0d8fdSPaul Kocialkowski #define OV8865_CROP_END_Y_L(v)			((v) & GENMASK(7, 0))
17111c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_X_H_REG		0x3808
17211c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_X_H(v)		(((v) & GENMASK(11, 8)) >> 8)
17311c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_X_L_REG		0x3809
17411c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_X_L(v)		((v) & GENMASK(7, 0))
17511c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_Y_H_REG		0x380a
17611c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_Y_H(v)		(((v) & GENMASK(11, 8)) >> 8)
17711c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_Y_L_REG		0x380b
17811c0d8fdSPaul Kocialkowski #define OV8865_OUTPUT_SIZE_Y_L(v)		((v) & GENMASK(7, 0))
17911c0d8fdSPaul Kocialkowski #define OV8865_HTS_H_REG			0x380c
18011c0d8fdSPaul Kocialkowski #define OV8865_HTS_H(v)				(((v) & GENMASK(11, 8)) >> 8)
18111c0d8fdSPaul Kocialkowski #define OV8865_HTS_L_REG			0x380d
18211c0d8fdSPaul Kocialkowski #define OV8865_HTS_L(v)				((v) & GENMASK(7, 0))
18311c0d8fdSPaul Kocialkowski #define OV8865_VTS_H_REG			0x380e
18411c0d8fdSPaul Kocialkowski #define OV8865_VTS_H(v)				(((v) & GENMASK(11, 8)) >> 8)
18511c0d8fdSPaul Kocialkowski #define OV8865_VTS_L_REG			0x380f
18611c0d8fdSPaul Kocialkowski #define OV8865_VTS_L(v)				((v) & GENMASK(7, 0))
1879293aafeSDaniel Scally #define OV8865_TIMING_MAX_VTS			0xffff
1889293aafeSDaniel Scally #define OV8865_TIMING_MIN_VTS			0x04
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 
456acd25e22SDaniel Scally /* Pixel Array */
457acd25e22SDaniel Scally 
458acd25e22SDaniel Scally #define OV8865_NATIVE_WIDTH			3296
459acd25e22SDaniel Scally #define OV8865_NATIVE_HEIGHT			2528
4603d1e4228SDaniel Scally #define OV8865_ACTIVE_START_LEFT		16
4613d1e4228SDaniel Scally #define OV8865_ACTIVE_START_TOP			40
462acd25e22SDaniel Scally #define OV8865_ACTIVE_WIDTH			3264
463acd25e22SDaniel Scally #define OV8865_ACTIVE_HEIGHT			2448
464acd25e22SDaniel Scally 
46511c0d8fdSPaul Kocialkowski /* Macros */
46611c0d8fdSPaul Kocialkowski 
46711c0d8fdSPaul Kocialkowski #define ov8865_subdev_sensor(s) \
46811c0d8fdSPaul Kocialkowski 	container_of(s, struct ov8865_sensor, subdev)
46911c0d8fdSPaul Kocialkowski 
47011c0d8fdSPaul Kocialkowski #define ov8865_ctrl_subdev(c) \
47136e4f2b2SPaul Kocialkowski 	(&container_of((c)->handler, struct ov8865_sensor, \
47236e4f2b2SPaul Kocialkowski 		       ctrls.handler)->subdev)
47311c0d8fdSPaul Kocialkowski 
47411c0d8fdSPaul Kocialkowski /* Data structures */
47511c0d8fdSPaul Kocialkowski 
47611c0d8fdSPaul Kocialkowski struct ov8865_register_value {
47711c0d8fdSPaul Kocialkowski 	u16 address;
47811c0d8fdSPaul Kocialkowski 	u8 value;
47911c0d8fdSPaul Kocialkowski 	unsigned int delay_ms;
48011c0d8fdSPaul Kocialkowski };
48111c0d8fdSPaul Kocialkowski 
48211c0d8fdSPaul Kocialkowski /*
48311c0d8fdSPaul Kocialkowski  * PLL1 Clock Tree:
48411c0d8fdSPaul Kocialkowski  *
48511c0d8fdSPaul Kocialkowski  * +-< EXTCLK
48611c0d8fdSPaul Kocialkowski  * |
48711c0d8fdSPaul Kocialkowski  * +-+ pll_pre_div_half (0x30a [0])
48811c0d8fdSPaul Kocialkowski  *   |
48911c0d8fdSPaul Kocialkowski  *   +-+ pll_pre_div (0x300 [2:0], special values:
49011c0d8fdSPaul Kocialkowski  *     |              0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8)
49111c0d8fdSPaul Kocialkowski  *     +-+ pll_mul (0x301 [1:0], 0x302 [7:0])
49211c0d8fdSPaul Kocialkowski  *       |
49311c0d8fdSPaul Kocialkowski  *       +-+ m_div (0x303 [3:0])
49411c0d8fdSPaul Kocialkowski  *       | |
49511c0d8fdSPaul Kocialkowski  *       | +-> PHY_SCLK
49611c0d8fdSPaul Kocialkowski  *       | |
49711c0d8fdSPaul Kocialkowski  *       | +-+ mipi_div (0x304 [1:0], special values: 0: 4, 1: 5, 2: 6, 3: 8)
49811c0d8fdSPaul Kocialkowski  *       |   |
49911c0d8fdSPaul Kocialkowski  *       |   +-+ pclk_div (0x3020 [3])
50011c0d8fdSPaul Kocialkowski  *       |     |
50111c0d8fdSPaul Kocialkowski  *       |     +-> PCLK
50211c0d8fdSPaul Kocialkowski  *       |
50311c0d8fdSPaul Kocialkowski  *       +-+ sys_pre_div (0x305 [1:0], special values: 0: 3, 1: 4, 2: 5, 3: 6)
50411c0d8fdSPaul Kocialkowski  *         |
50511c0d8fdSPaul Kocialkowski  *         +-+ sys_div (0x306 [0])
50611c0d8fdSPaul Kocialkowski  *           |
50711c0d8fdSPaul Kocialkowski  *           +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2)
50811c0d8fdSPaul Kocialkowski  *             |
50911c0d8fdSPaul Kocialkowski  *             +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK)
51011c0d8fdSPaul Kocialkowski  *               |
51111c0d8fdSPaul Kocialkowski  *               +-+ sclk_pre_div (0x3106 [3:2], special values:
51211c0d8fdSPaul Kocialkowski  *                 |               0: 1, 1: 2, 2: 4, 3: 1)
51311c0d8fdSPaul Kocialkowski  *                 |
51411c0d8fdSPaul Kocialkowski  *                 +-+ sclk_div (0x3106 [7:4], special values: 0: 1)
51511c0d8fdSPaul Kocialkowski  *                   |
51611c0d8fdSPaul Kocialkowski  *                   +-> SCLK
51711c0d8fdSPaul Kocialkowski  */
51811c0d8fdSPaul Kocialkowski 
51911c0d8fdSPaul Kocialkowski struct ov8865_pll1_config {
52011c0d8fdSPaul Kocialkowski 	unsigned int pll_pre_div_half;
52111c0d8fdSPaul Kocialkowski 	unsigned int pll_pre_div;
52211c0d8fdSPaul Kocialkowski 	unsigned int pll_mul;
52311c0d8fdSPaul Kocialkowski 	unsigned int m_div;
52411c0d8fdSPaul Kocialkowski 	unsigned int mipi_div;
52511c0d8fdSPaul Kocialkowski 	unsigned int pclk_div;
52611c0d8fdSPaul Kocialkowski 	unsigned int sys_pre_div;
52711c0d8fdSPaul Kocialkowski 	unsigned int sys_div;
52811c0d8fdSPaul Kocialkowski };
52911c0d8fdSPaul Kocialkowski 
53011c0d8fdSPaul Kocialkowski /*
53111c0d8fdSPaul Kocialkowski  * PLL2 Clock Tree:
53211c0d8fdSPaul Kocialkowski  *
53311c0d8fdSPaul Kocialkowski  * +-< EXTCLK
53411c0d8fdSPaul Kocialkowski  * |
53511c0d8fdSPaul Kocialkowski  * +-+ pll_pre_div_half (0x312 [4])
53611c0d8fdSPaul Kocialkowski  *   |
53711c0d8fdSPaul Kocialkowski  *   +-+ pll_pre_div (0x30b [2:0], special values:
53811c0d8fdSPaul Kocialkowski  *     |              0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 4, 7: 8)
53911c0d8fdSPaul Kocialkowski  *     +-+ pll_mul (0x30c [1:0], 0x30d [7:0])
54011c0d8fdSPaul Kocialkowski  *       |
54111c0d8fdSPaul Kocialkowski  *       +-+ dac_div (0x312 [3:0])
54211c0d8fdSPaul Kocialkowski  *       | |
54311c0d8fdSPaul Kocialkowski  *       | +-> DAC_CLK
54411c0d8fdSPaul Kocialkowski  *       |
54511c0d8fdSPaul Kocialkowski  *       +-+ sys_pre_div (0x30f [3:0])
54611c0d8fdSPaul Kocialkowski  *         |
54711c0d8fdSPaul Kocialkowski  *         +-+ sys_div (0x30e [2:0], special values:
54811c0d8fdSPaul Kocialkowski  *           |          0: 1, 1: 1.5, 3: 2.5, 4: 3, 5: 3.5, 6: 4, 7:5)
54911c0d8fdSPaul Kocialkowski  *           |
55011c0d8fdSPaul Kocialkowski  *           +-+ sys_sel (0x3032 [7], 0: PLL1, 1: PLL2)
55111c0d8fdSPaul Kocialkowski  *             |
55211c0d8fdSPaul Kocialkowski  *             +-+ sclk_sel (0x3033 [1], 0: sys_sel, 1: PLL2 DAC_CLK)
55311c0d8fdSPaul Kocialkowski  *               |
55411c0d8fdSPaul Kocialkowski  *               +-+ sclk_pre_div (0x3106 [3:2], special values:
55511c0d8fdSPaul Kocialkowski  *                 |               0: 1, 1: 2, 2: 4, 3: 1)
55611c0d8fdSPaul Kocialkowski  *                 |
55711c0d8fdSPaul Kocialkowski  *                 +-+ sclk_div (0x3106 [7:4], special values: 0: 1)
55811c0d8fdSPaul Kocialkowski  *                   |
55911c0d8fdSPaul Kocialkowski  *                   +-> SCLK
56011c0d8fdSPaul Kocialkowski  */
56111c0d8fdSPaul Kocialkowski 
56211c0d8fdSPaul Kocialkowski struct ov8865_pll2_config {
56311c0d8fdSPaul Kocialkowski 	unsigned int pll_pre_div_half;
56411c0d8fdSPaul Kocialkowski 	unsigned int pll_pre_div;
56511c0d8fdSPaul Kocialkowski 	unsigned int pll_mul;
56611c0d8fdSPaul Kocialkowski 	unsigned int dac_div;
56711c0d8fdSPaul Kocialkowski 	unsigned int sys_pre_div;
56811c0d8fdSPaul Kocialkowski 	unsigned int sys_div;
56911c0d8fdSPaul Kocialkowski };
57011c0d8fdSPaul Kocialkowski 
57111c0d8fdSPaul Kocialkowski struct ov8865_sclk_config {
57211c0d8fdSPaul Kocialkowski 	unsigned int sys_sel;
57311c0d8fdSPaul Kocialkowski 	unsigned int sclk_sel;
57411c0d8fdSPaul Kocialkowski 	unsigned int sclk_pre_div;
57511c0d8fdSPaul Kocialkowski 	unsigned int sclk_div;
57611c0d8fdSPaul Kocialkowski };
57711c0d8fdSPaul Kocialkowski 
57873dcffebSDaniel Scally struct ov8865_pll_configs {
57973dcffebSDaniel Scally 	const struct ov8865_pll1_config *pll1_config;
58073dcffebSDaniel Scally 	const struct ov8865_pll2_config *pll2_config_native;
58173dcffebSDaniel Scally 	const struct ov8865_pll2_config *pll2_config_binning;
58273dcffebSDaniel Scally };
58373dcffebSDaniel Scally 
58473dcffebSDaniel Scally /* Clock rate */
58573dcffebSDaniel Scally 
58673dcffebSDaniel Scally enum extclk_rate {
58773dcffebSDaniel Scally 	OV8865_19_2_MHZ,
58873dcffebSDaniel Scally 	OV8865_24_MHZ,
58973dcffebSDaniel Scally 	OV8865_NUM_SUPPORTED_RATES
59073dcffebSDaniel Scally };
59173dcffebSDaniel Scally 
59273dcffebSDaniel Scally static const unsigned long supported_extclk_rates[] = {
59373dcffebSDaniel Scally 	[OV8865_19_2_MHZ] = 19200000,
59473dcffebSDaniel Scally 	[OV8865_24_MHZ] = 24000000,
59573dcffebSDaniel Scally };
59673dcffebSDaniel Scally 
59711c0d8fdSPaul Kocialkowski /*
59811c0d8fdSPaul Kocialkowski  * General formulas for (array-centered) mode calculation:
59911c0d8fdSPaul Kocialkowski  * - photo_array_width = 3296
60011c0d8fdSPaul Kocialkowski  * - crop_start_x = (photo_array_width - output_size_x) / 2
60111c0d8fdSPaul Kocialkowski  * - crop_end_x = crop_start_x + offset_x + output_size_x - 1
60211c0d8fdSPaul Kocialkowski  *
60311c0d8fdSPaul Kocialkowski  * - photo_array_height = 2480
60411c0d8fdSPaul Kocialkowski  * - crop_start_y = (photo_array_height - output_size_y) / 2
60511c0d8fdSPaul Kocialkowski  * - crop_end_y = crop_start_y + offset_y + output_size_y - 1
60611c0d8fdSPaul Kocialkowski  */
60711c0d8fdSPaul Kocialkowski 
60811c0d8fdSPaul Kocialkowski struct ov8865_mode {
60911c0d8fdSPaul Kocialkowski 	unsigned int crop_start_x;
61011c0d8fdSPaul Kocialkowski 	unsigned int offset_x;
61111c0d8fdSPaul Kocialkowski 	unsigned int output_size_x;
61211c0d8fdSPaul Kocialkowski 	unsigned int crop_end_x;
61311c0d8fdSPaul Kocialkowski 	unsigned int hts;
61411c0d8fdSPaul Kocialkowski 
61511c0d8fdSPaul Kocialkowski 	unsigned int crop_start_y;
61611c0d8fdSPaul Kocialkowski 	unsigned int offset_y;
61711c0d8fdSPaul Kocialkowski 	unsigned int output_size_y;
61811c0d8fdSPaul Kocialkowski 	unsigned int crop_end_y;
61911c0d8fdSPaul Kocialkowski 	unsigned int vts;
62011c0d8fdSPaul Kocialkowski 
62111c0d8fdSPaul Kocialkowski 	/* With auto size, only output and total sizes need to be set. */
62211c0d8fdSPaul Kocialkowski 	bool size_auto;
62311c0d8fdSPaul Kocialkowski 	unsigned int size_auto_boundary_x;
62411c0d8fdSPaul Kocialkowski 	unsigned int size_auto_boundary_y;
62511c0d8fdSPaul Kocialkowski 
62611c0d8fdSPaul Kocialkowski 	bool binning_x;
62711c0d8fdSPaul Kocialkowski 	bool binning_y;
62811c0d8fdSPaul Kocialkowski 	bool variopixel;
62911c0d8fdSPaul Kocialkowski 	unsigned int variopixel_hsub_coef;
63011c0d8fdSPaul Kocialkowski 	unsigned int variopixel_vsub_coef;
63111c0d8fdSPaul Kocialkowski 
63211c0d8fdSPaul Kocialkowski 	/* Bits for the format register, used for binning. */
63311c0d8fdSPaul Kocialkowski 	bool sync_hbin;
63411c0d8fdSPaul Kocialkowski 	bool horz_var2;
63511c0d8fdSPaul Kocialkowski 
63611c0d8fdSPaul Kocialkowski 	unsigned int inc_x_odd;
63711c0d8fdSPaul Kocialkowski 	unsigned int inc_x_even;
63811c0d8fdSPaul Kocialkowski 	unsigned int inc_y_odd;
63911c0d8fdSPaul Kocialkowski 	unsigned int inc_y_even;
64011c0d8fdSPaul Kocialkowski 
64111c0d8fdSPaul Kocialkowski 	unsigned int vfifo_read_start;
64211c0d8fdSPaul Kocialkowski 
64311c0d8fdSPaul Kocialkowski 	unsigned int ablc_num;
64411c0d8fdSPaul Kocialkowski 	unsigned int zline_num;
64511c0d8fdSPaul Kocialkowski 
64611c0d8fdSPaul Kocialkowski 	unsigned int blc_top_zero_line_start;
64711c0d8fdSPaul Kocialkowski 	unsigned int blc_top_zero_line_num;
64811c0d8fdSPaul Kocialkowski 	unsigned int blc_top_black_line_start;
64911c0d8fdSPaul Kocialkowski 	unsigned int blc_top_black_line_num;
65011c0d8fdSPaul Kocialkowski 
65111c0d8fdSPaul Kocialkowski 	unsigned int blc_bottom_zero_line_start;
65211c0d8fdSPaul Kocialkowski 	unsigned int blc_bottom_zero_line_num;
65311c0d8fdSPaul Kocialkowski 	unsigned int blc_bottom_black_line_start;
65411c0d8fdSPaul Kocialkowski 	unsigned int blc_bottom_black_line_num;
65511c0d8fdSPaul Kocialkowski 
65611c0d8fdSPaul Kocialkowski 	u8 blc_col_shift_mask;
65711c0d8fdSPaul Kocialkowski 
65811c0d8fdSPaul Kocialkowski 	unsigned int blc_anchor_left_start;
65911c0d8fdSPaul Kocialkowski 	unsigned int blc_anchor_left_end;
66011c0d8fdSPaul Kocialkowski 	unsigned int blc_anchor_right_start;
66111c0d8fdSPaul Kocialkowski 	unsigned int blc_anchor_right_end;
66211c0d8fdSPaul Kocialkowski 
66373dcffebSDaniel Scally 	bool pll2_binning;
66411c0d8fdSPaul Kocialkowski 
66511c0d8fdSPaul Kocialkowski 	const struct ov8865_register_value *register_values;
66611c0d8fdSPaul Kocialkowski 	unsigned int register_values_count;
66711c0d8fdSPaul Kocialkowski };
66811c0d8fdSPaul Kocialkowski 
66911c0d8fdSPaul Kocialkowski struct ov8865_state {
67011c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode;
67111c0d8fdSPaul Kocialkowski 	u32 mbus_code;
67211c0d8fdSPaul Kocialkowski 
67311c0d8fdSPaul Kocialkowski 	bool streaming;
67411c0d8fdSPaul Kocialkowski };
67511c0d8fdSPaul Kocialkowski 
67611c0d8fdSPaul Kocialkowski struct ov8865_ctrls {
67711c0d8fdSPaul Kocialkowski 	struct v4l2_ctrl *link_freq;
67811c0d8fdSPaul Kocialkowski 	struct v4l2_ctrl *pixel_rate;
679d84d4ceeSDaniel Scally 	struct v4l2_ctrl *hblank;
6809293aafeSDaniel Scally 	struct v4l2_ctrl *vblank;
681ca28690eSDaniel Scally 	struct v4l2_ctrl *exposure;
68211c0d8fdSPaul Kocialkowski 
68311c0d8fdSPaul Kocialkowski 	struct v4l2_ctrl_handler handler;
68411c0d8fdSPaul Kocialkowski };
68511c0d8fdSPaul Kocialkowski 
68611c0d8fdSPaul Kocialkowski struct ov8865_sensor {
68711c0d8fdSPaul Kocialkowski 	struct device *dev;
68811c0d8fdSPaul Kocialkowski 	struct i2c_client *i2c_client;
68911c0d8fdSPaul Kocialkowski 	struct gpio_desc *reset;
69011c0d8fdSPaul Kocialkowski 	struct gpio_desc *powerdown;
69111c0d8fdSPaul Kocialkowski 	struct regulator *avdd;
69211c0d8fdSPaul Kocialkowski 	struct regulator *dvdd;
69311c0d8fdSPaul Kocialkowski 	struct regulator *dovdd;
69473dcffebSDaniel Scally 
69573dcffebSDaniel Scally 	unsigned long extclk_rate;
69673dcffebSDaniel Scally 	const struct ov8865_pll_configs *pll_configs;
69711c0d8fdSPaul Kocialkowski 	struct clk *extclk;
69811c0d8fdSPaul Kocialkowski 
69911c0d8fdSPaul Kocialkowski 	struct v4l2_fwnode_endpoint endpoint;
70011c0d8fdSPaul Kocialkowski 	struct v4l2_subdev subdev;
70111c0d8fdSPaul Kocialkowski 	struct media_pad pad;
70211c0d8fdSPaul Kocialkowski 
70311c0d8fdSPaul Kocialkowski 	struct mutex mutex;
70411c0d8fdSPaul Kocialkowski 
70511c0d8fdSPaul Kocialkowski 	struct ov8865_state state;
70611c0d8fdSPaul Kocialkowski 	struct ov8865_ctrls ctrls;
70711c0d8fdSPaul Kocialkowski };
70811c0d8fdSPaul Kocialkowski 
70911c0d8fdSPaul Kocialkowski /* Static definitions */
71011c0d8fdSPaul Kocialkowski 
71111c0d8fdSPaul Kocialkowski /*
71211c0d8fdSPaul Kocialkowski  * PHY_SCLK = 720 MHz
71311c0d8fdSPaul Kocialkowski  * MIPI_PCLK = 90 MHz
71411c0d8fdSPaul Kocialkowski  */
71573dcffebSDaniel Scally 
71673dcffebSDaniel Scally static const struct ov8865_pll1_config ov8865_pll1_config_native_19_2mhz = {
71773dcffebSDaniel Scally 		.pll_pre_div_half	= 1,
71873dcffebSDaniel Scally 		.pll_pre_div		= 2,
71973dcffebSDaniel Scally 		.pll_mul		= 75,
72073dcffebSDaniel Scally 		.m_div			= 1,
72173dcffebSDaniel Scally 		.mipi_div		= 3,
72273dcffebSDaniel Scally 		.pclk_div		= 1,
72373dcffebSDaniel Scally 		.sys_pre_div		= 1,
72473dcffebSDaniel Scally 		.sys_div		= 2,
72573dcffebSDaniel Scally };
72673dcffebSDaniel Scally 
72773dcffebSDaniel Scally static const struct ov8865_pll1_config ov8865_pll1_config_native_24mhz = {
72811c0d8fdSPaul Kocialkowski 		.pll_pre_div_half	= 1,
72911c0d8fdSPaul Kocialkowski 		.pll_pre_div		= 0,
73011c0d8fdSPaul Kocialkowski 		.pll_mul		= 30,
73111c0d8fdSPaul Kocialkowski 		.m_div			= 1,
73211c0d8fdSPaul Kocialkowski 		.mipi_div		= 3,
73311c0d8fdSPaul Kocialkowski 		.pclk_div		= 1,
73411c0d8fdSPaul Kocialkowski 		.sys_pre_div		= 1,
73511c0d8fdSPaul Kocialkowski 		.sys_div		= 2,
73611c0d8fdSPaul Kocialkowski };
73711c0d8fdSPaul Kocialkowski 
73811c0d8fdSPaul Kocialkowski /*
73911c0d8fdSPaul Kocialkowski  * DAC_CLK = 360 MHz
74011c0d8fdSPaul Kocialkowski  * SCLK = 144 MHz
74111c0d8fdSPaul Kocialkowski  */
74211c0d8fdSPaul Kocialkowski 
74373dcffebSDaniel Scally static const struct ov8865_pll2_config ov8865_pll2_config_native_19_2mhz = {
74473dcffebSDaniel Scally 		.pll_pre_div_half	= 1,
74573dcffebSDaniel Scally 		.pll_pre_div		= 5,
74673dcffebSDaniel Scally 		.pll_mul		= 75,
74773dcffebSDaniel Scally 		.dac_div		= 1,
74873dcffebSDaniel Scally 		.sys_pre_div		= 1,
74973dcffebSDaniel Scally 		.sys_div		= 3,
75073dcffebSDaniel Scally };
75173dcffebSDaniel Scally 
75273dcffebSDaniel Scally static const struct ov8865_pll2_config ov8865_pll2_config_native_24mhz = {
75311c0d8fdSPaul Kocialkowski 		.pll_pre_div_half	= 1,
75411c0d8fdSPaul Kocialkowski 		.pll_pre_div		= 0,
75511c0d8fdSPaul Kocialkowski 		.pll_mul		= 30,
75611c0d8fdSPaul Kocialkowski 		.dac_div		= 2,
75711c0d8fdSPaul Kocialkowski 		.sys_pre_div		= 5,
75811c0d8fdSPaul Kocialkowski 		.sys_div		= 0,
75911c0d8fdSPaul Kocialkowski };
76011c0d8fdSPaul Kocialkowski 
76111c0d8fdSPaul Kocialkowski /*
76211c0d8fdSPaul Kocialkowski  * DAC_CLK = 360 MHz
763651d1f20SDaniel Scally  * SCLK = 72 MHz
76411c0d8fdSPaul Kocialkowski  */
76511c0d8fdSPaul Kocialkowski 
76673dcffebSDaniel Scally static const struct ov8865_pll2_config ov8865_pll2_config_binning_19_2mhz = {
76773dcffebSDaniel Scally 	.pll_pre_div_half	= 1,
76873dcffebSDaniel Scally 	.pll_pre_div		= 2,
76973dcffebSDaniel Scally 	.pll_mul		= 75,
77073dcffebSDaniel Scally 	.dac_div		= 2,
77173dcffebSDaniel Scally 	.sys_pre_div		= 10,
77273dcffebSDaniel Scally 	.sys_div		= 0,
77373dcffebSDaniel Scally };
77473dcffebSDaniel Scally 
77573dcffebSDaniel Scally static const struct ov8865_pll2_config ov8865_pll2_config_binning_24mhz = {
77611c0d8fdSPaul Kocialkowski 	.pll_pre_div_half	= 1,
77711c0d8fdSPaul Kocialkowski 	.pll_pre_div		= 0,
77811c0d8fdSPaul Kocialkowski 	.pll_mul		= 30,
77911c0d8fdSPaul Kocialkowski 	.dac_div		= 2,
78011c0d8fdSPaul Kocialkowski 	.sys_pre_div		= 10,
78111c0d8fdSPaul Kocialkowski 	.sys_div		= 0,
78211c0d8fdSPaul Kocialkowski };
78311c0d8fdSPaul Kocialkowski 
78473dcffebSDaniel Scally static const struct ov8865_pll_configs ov8865_pll_configs_19_2mhz = {
78573dcffebSDaniel Scally 	.pll1_config = &ov8865_pll1_config_native_19_2mhz,
78673dcffebSDaniel Scally 	.pll2_config_native = &ov8865_pll2_config_native_19_2mhz,
78773dcffebSDaniel Scally 	.pll2_config_binning = &ov8865_pll2_config_binning_19_2mhz,
78873dcffebSDaniel Scally };
78973dcffebSDaniel Scally 
79073dcffebSDaniel Scally static const struct ov8865_pll_configs ov8865_pll_configs_24mhz = {
79173dcffebSDaniel Scally 	.pll1_config = &ov8865_pll1_config_native_24mhz,
79273dcffebSDaniel Scally 	.pll2_config_native = &ov8865_pll2_config_native_24mhz,
79373dcffebSDaniel Scally 	.pll2_config_binning = &ov8865_pll2_config_binning_24mhz,
79473dcffebSDaniel Scally };
79573dcffebSDaniel Scally 
79673dcffebSDaniel Scally static const struct ov8865_pll_configs *ov8865_pll_configs[] = {
79773dcffebSDaniel Scally 	&ov8865_pll_configs_19_2mhz,
79873dcffebSDaniel Scally 	&ov8865_pll_configs_24mhz,
79973dcffebSDaniel Scally };
80073dcffebSDaniel Scally 
80111c0d8fdSPaul Kocialkowski static const struct ov8865_sclk_config ov8865_sclk_config_native = {
80211c0d8fdSPaul Kocialkowski 	.sys_sel		= 1,
80311c0d8fdSPaul Kocialkowski 	.sclk_sel		= 0,
80411c0d8fdSPaul Kocialkowski 	.sclk_pre_div		= 0,
80511c0d8fdSPaul Kocialkowski 	.sclk_div		= 0,
80611c0d8fdSPaul Kocialkowski };
80711c0d8fdSPaul Kocialkowski 
80811c0d8fdSPaul Kocialkowski static const struct ov8865_register_value ov8865_register_values_native[] = {
80911c0d8fdSPaul Kocialkowski 	/* Sensor */
81011c0d8fdSPaul Kocialkowski 
81111c0d8fdSPaul Kocialkowski 	{ 0x3700, 0x48 },
81211c0d8fdSPaul Kocialkowski 	{ 0x3701, 0x18 },
81311c0d8fdSPaul Kocialkowski 	{ 0x3702, 0x50 },
81411c0d8fdSPaul Kocialkowski 	{ 0x3703, 0x32 },
81511c0d8fdSPaul Kocialkowski 	{ 0x3704, 0x28 },
81611c0d8fdSPaul Kocialkowski 	{ 0x3706, 0x70 },
81711c0d8fdSPaul Kocialkowski 	{ 0x3707, 0x08 },
81811c0d8fdSPaul Kocialkowski 	{ 0x3708, 0x48 },
81911c0d8fdSPaul Kocialkowski 	{ 0x3709, 0x80 },
82011c0d8fdSPaul Kocialkowski 	{ 0x370a, 0x01 },
82111c0d8fdSPaul Kocialkowski 	{ 0x370b, 0x70 },
82211c0d8fdSPaul Kocialkowski 	{ 0x370c, 0x07 },
82311c0d8fdSPaul Kocialkowski 	{ 0x3718, 0x14 },
82411c0d8fdSPaul Kocialkowski 	{ 0x3712, 0x44 },
82511c0d8fdSPaul Kocialkowski 	{ 0x371e, 0x31 },
82611c0d8fdSPaul Kocialkowski 	{ 0x371f, 0x7f },
82711c0d8fdSPaul Kocialkowski 	{ 0x3720, 0x0a },
82811c0d8fdSPaul Kocialkowski 	{ 0x3721, 0x0a },
82911c0d8fdSPaul Kocialkowski 	{ 0x3724, 0x04 },
83011c0d8fdSPaul Kocialkowski 	{ 0x3725, 0x04 },
83111c0d8fdSPaul Kocialkowski 	{ 0x3726, 0x0c },
83211c0d8fdSPaul Kocialkowski 	{ 0x3728, 0x0a },
83311c0d8fdSPaul Kocialkowski 	{ 0x3729, 0x03 },
83411c0d8fdSPaul Kocialkowski 	{ 0x372a, 0x06 },
83511c0d8fdSPaul Kocialkowski 	{ 0x372b, 0xa6 },
83611c0d8fdSPaul Kocialkowski 	{ 0x372c, 0xa6 },
83711c0d8fdSPaul Kocialkowski 	{ 0x372d, 0xa6 },
83811c0d8fdSPaul Kocialkowski 	{ 0x372e, 0x0c },
83911c0d8fdSPaul Kocialkowski 	{ 0x372f, 0x20 },
84011c0d8fdSPaul Kocialkowski 	{ 0x3730, 0x02 },
84111c0d8fdSPaul Kocialkowski 	{ 0x3731, 0x0c },
84211c0d8fdSPaul Kocialkowski 	{ 0x3732, 0x28 },
84311c0d8fdSPaul Kocialkowski 	{ 0x3736, 0x30 },
84411c0d8fdSPaul Kocialkowski 	{ 0x373a, 0x04 },
84511c0d8fdSPaul Kocialkowski 	{ 0x373b, 0x18 },
84611c0d8fdSPaul Kocialkowski 	{ 0x373c, 0x14 },
84711c0d8fdSPaul Kocialkowski 	{ 0x373e, 0x06 },
84811c0d8fdSPaul Kocialkowski 	{ 0x375a, 0x0c },
84911c0d8fdSPaul Kocialkowski 	{ 0x375b, 0x26 },
85011c0d8fdSPaul Kocialkowski 	{ 0x375d, 0x04 },
85111c0d8fdSPaul Kocialkowski 	{ 0x375f, 0x28 },
85211c0d8fdSPaul Kocialkowski 	{ 0x3767, 0x1e },
85311c0d8fdSPaul Kocialkowski 	{ 0x3772, 0x46 },
85411c0d8fdSPaul Kocialkowski 	{ 0x3773, 0x04 },
85511c0d8fdSPaul Kocialkowski 	{ 0x3774, 0x2c },
85611c0d8fdSPaul Kocialkowski 	{ 0x3775, 0x13 },
85711c0d8fdSPaul Kocialkowski 	{ 0x3776, 0x10 },
85811c0d8fdSPaul Kocialkowski 	{ 0x37a0, 0x88 },
85911c0d8fdSPaul Kocialkowski 	{ 0x37a1, 0x7a },
86011c0d8fdSPaul Kocialkowski 	{ 0x37a2, 0x7a },
86111c0d8fdSPaul Kocialkowski 	{ 0x37a3, 0x02 },
86211c0d8fdSPaul Kocialkowski 	{ 0x37a5, 0x09 },
86311c0d8fdSPaul Kocialkowski 	{ 0x37a7, 0x88 },
86411c0d8fdSPaul Kocialkowski 	{ 0x37a8, 0xb0 },
86511c0d8fdSPaul Kocialkowski 	{ 0x37a9, 0xb0 },
86611c0d8fdSPaul Kocialkowski 	{ 0x37aa, 0x88 },
86711c0d8fdSPaul Kocialkowski 	{ 0x37ab, 0x5c },
86811c0d8fdSPaul Kocialkowski 	{ 0x37ac, 0x5c },
86911c0d8fdSPaul Kocialkowski 	{ 0x37ad, 0x55 },
87011c0d8fdSPaul Kocialkowski 	{ 0x37ae, 0x19 },
87111c0d8fdSPaul Kocialkowski 	{ 0x37af, 0x19 },
87211c0d8fdSPaul Kocialkowski 	{ 0x37b3, 0x84 },
87311c0d8fdSPaul Kocialkowski 	{ 0x37b4, 0x84 },
87411c0d8fdSPaul Kocialkowski 	{ 0x37b5, 0x66 },
87511c0d8fdSPaul Kocialkowski 
87611c0d8fdSPaul Kocialkowski 	/* PSRAM */
87711c0d8fdSPaul Kocialkowski 
87811c0d8fdSPaul Kocialkowski 	{ OV8865_PSRAM_CTRL8_REG, 0x16 },
87911c0d8fdSPaul Kocialkowski 
88011c0d8fdSPaul Kocialkowski 	/* ADC Sync */
88111c0d8fdSPaul Kocialkowski 
88211c0d8fdSPaul Kocialkowski 	{ 0x4500, 0x68 },
88311c0d8fdSPaul Kocialkowski };
88411c0d8fdSPaul Kocialkowski 
88511c0d8fdSPaul Kocialkowski static const struct ov8865_register_value ov8865_register_values_binning[] = {
88611c0d8fdSPaul Kocialkowski 	/* Sensor */
88711c0d8fdSPaul Kocialkowski 
88811c0d8fdSPaul Kocialkowski 	{ 0x3700, 0x24 },
88911c0d8fdSPaul Kocialkowski 	{ 0x3701, 0x0c },
89011c0d8fdSPaul Kocialkowski 	{ 0x3702, 0x28 },
89111c0d8fdSPaul Kocialkowski 	{ 0x3703, 0x19 },
89211c0d8fdSPaul Kocialkowski 	{ 0x3704, 0x14 },
89311c0d8fdSPaul Kocialkowski 	{ 0x3706, 0x38 },
89411c0d8fdSPaul Kocialkowski 	{ 0x3707, 0x04 },
89511c0d8fdSPaul Kocialkowski 	{ 0x3708, 0x24 },
89611c0d8fdSPaul Kocialkowski 	{ 0x3709, 0x40 },
89711c0d8fdSPaul Kocialkowski 	{ 0x370a, 0x00 },
89811c0d8fdSPaul Kocialkowski 	{ 0x370b, 0xb8 },
89911c0d8fdSPaul Kocialkowski 	{ 0x370c, 0x04 },
90011c0d8fdSPaul Kocialkowski 	{ 0x3718, 0x12 },
90111c0d8fdSPaul Kocialkowski 	{ 0x3712, 0x42 },
90211c0d8fdSPaul Kocialkowski 	{ 0x371e, 0x19 },
90311c0d8fdSPaul Kocialkowski 	{ 0x371f, 0x40 },
90411c0d8fdSPaul Kocialkowski 	{ 0x3720, 0x05 },
90511c0d8fdSPaul Kocialkowski 	{ 0x3721, 0x05 },
90611c0d8fdSPaul Kocialkowski 	{ 0x3724, 0x02 },
90711c0d8fdSPaul Kocialkowski 	{ 0x3725, 0x02 },
90811c0d8fdSPaul Kocialkowski 	{ 0x3726, 0x06 },
90911c0d8fdSPaul Kocialkowski 	{ 0x3728, 0x05 },
91011c0d8fdSPaul Kocialkowski 	{ 0x3729, 0x02 },
91111c0d8fdSPaul Kocialkowski 	{ 0x372a, 0x03 },
91211c0d8fdSPaul Kocialkowski 	{ 0x372b, 0x53 },
91311c0d8fdSPaul Kocialkowski 	{ 0x372c, 0xa3 },
91411c0d8fdSPaul Kocialkowski 	{ 0x372d, 0x53 },
91511c0d8fdSPaul Kocialkowski 	{ 0x372e, 0x06 },
91611c0d8fdSPaul Kocialkowski 	{ 0x372f, 0x10 },
91711c0d8fdSPaul Kocialkowski 	{ 0x3730, 0x01 },
91811c0d8fdSPaul Kocialkowski 	{ 0x3731, 0x06 },
91911c0d8fdSPaul Kocialkowski 	{ 0x3732, 0x14 },
92011c0d8fdSPaul Kocialkowski 	{ 0x3736, 0x20 },
92111c0d8fdSPaul Kocialkowski 	{ 0x373a, 0x02 },
92211c0d8fdSPaul Kocialkowski 	{ 0x373b, 0x0c },
92311c0d8fdSPaul Kocialkowski 	{ 0x373c, 0x0a },
92411c0d8fdSPaul Kocialkowski 	{ 0x373e, 0x03 },
92511c0d8fdSPaul Kocialkowski 	{ 0x375a, 0x06 },
92611c0d8fdSPaul Kocialkowski 	{ 0x375b, 0x13 },
92711c0d8fdSPaul Kocialkowski 	{ 0x375d, 0x02 },
92811c0d8fdSPaul Kocialkowski 	{ 0x375f, 0x14 },
92911c0d8fdSPaul Kocialkowski 	{ 0x3767, 0x1c },
93011c0d8fdSPaul Kocialkowski 	{ 0x3772, 0x23 },
93111c0d8fdSPaul Kocialkowski 	{ 0x3773, 0x02 },
93211c0d8fdSPaul Kocialkowski 	{ 0x3774, 0x16 },
93311c0d8fdSPaul Kocialkowski 	{ 0x3775, 0x12 },
93411c0d8fdSPaul Kocialkowski 	{ 0x3776, 0x08 },
93511c0d8fdSPaul Kocialkowski 	{ 0x37a0, 0x44 },
93611c0d8fdSPaul Kocialkowski 	{ 0x37a1, 0x3d },
93711c0d8fdSPaul Kocialkowski 	{ 0x37a2, 0x3d },
93811c0d8fdSPaul Kocialkowski 	{ 0x37a3, 0x01 },
93911c0d8fdSPaul Kocialkowski 	{ 0x37a5, 0x08 },
94011c0d8fdSPaul Kocialkowski 	{ 0x37a7, 0x44 },
94111c0d8fdSPaul Kocialkowski 	{ 0x37a8, 0x58 },
94211c0d8fdSPaul Kocialkowski 	{ 0x37a9, 0x58 },
94311c0d8fdSPaul Kocialkowski 	{ 0x37aa, 0x44 },
94411c0d8fdSPaul Kocialkowski 	{ 0x37ab, 0x2e },
94511c0d8fdSPaul Kocialkowski 	{ 0x37ac, 0x2e },
94611c0d8fdSPaul Kocialkowski 	{ 0x37ad, 0x33 },
94711c0d8fdSPaul Kocialkowski 	{ 0x37ae, 0x0d },
94811c0d8fdSPaul Kocialkowski 	{ 0x37af, 0x0d },
94911c0d8fdSPaul Kocialkowski 	{ 0x37b3, 0x42 },
95011c0d8fdSPaul Kocialkowski 	{ 0x37b4, 0x42 },
95111c0d8fdSPaul Kocialkowski 	{ 0x37b5, 0x33 },
95211c0d8fdSPaul Kocialkowski 
95311c0d8fdSPaul Kocialkowski 	/* PSRAM */
95411c0d8fdSPaul Kocialkowski 
95511c0d8fdSPaul Kocialkowski 	{ OV8865_PSRAM_CTRL8_REG, 0x0b },
95611c0d8fdSPaul Kocialkowski 
95711c0d8fdSPaul Kocialkowski 	/* ADC Sync */
95811c0d8fdSPaul Kocialkowski 
95911c0d8fdSPaul Kocialkowski 	{ 0x4500, 0x40 },
96011c0d8fdSPaul Kocialkowski };
96111c0d8fdSPaul Kocialkowski 
96211c0d8fdSPaul Kocialkowski static const struct ov8865_mode ov8865_modes[] = {
96311c0d8fdSPaul Kocialkowski 	/* 3264x2448 */
96411c0d8fdSPaul Kocialkowski 	{
96511c0d8fdSPaul Kocialkowski 		/* Horizontal */
96611c0d8fdSPaul Kocialkowski 		.output_size_x			= 3264,
967295786e5SDaniel Scally 		.hts				= 3888,
96811c0d8fdSPaul Kocialkowski 
96911c0d8fdSPaul Kocialkowski 		/* Vertical */
97011c0d8fdSPaul Kocialkowski 		.output_size_y			= 2448,
97111c0d8fdSPaul Kocialkowski 		.vts				= 2470,
97211c0d8fdSPaul Kocialkowski 
97311c0d8fdSPaul Kocialkowski 		.size_auto			= true,
97411c0d8fdSPaul Kocialkowski 		.size_auto_boundary_x		= 8,
97511c0d8fdSPaul Kocialkowski 		.size_auto_boundary_y		= 4,
97611c0d8fdSPaul Kocialkowski 
97711c0d8fdSPaul Kocialkowski 		/* Subsample increase */
97811c0d8fdSPaul Kocialkowski 		.inc_x_odd			= 1,
97911c0d8fdSPaul Kocialkowski 		.inc_x_even			= 1,
98011c0d8fdSPaul Kocialkowski 		.inc_y_odd			= 1,
98111c0d8fdSPaul Kocialkowski 		.inc_y_even			= 1,
98211c0d8fdSPaul Kocialkowski 
98311c0d8fdSPaul Kocialkowski 		/* VFIFO */
98411c0d8fdSPaul Kocialkowski 		.vfifo_read_start		= 16,
98511c0d8fdSPaul Kocialkowski 
98611c0d8fdSPaul Kocialkowski 		.ablc_num			= 4,
98711c0d8fdSPaul Kocialkowski 		.zline_num			= 1,
98811c0d8fdSPaul Kocialkowski 
98911c0d8fdSPaul Kocialkowski 		/* Black Level */
99011c0d8fdSPaul Kocialkowski 
99111c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_start	= 0,
99211c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_num		= 2,
99311c0d8fdSPaul Kocialkowski 		.blc_top_black_line_start	= 4,
99411c0d8fdSPaul Kocialkowski 		.blc_top_black_line_num		= 4,
99511c0d8fdSPaul Kocialkowski 
99611c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_start	= 2,
99711c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_num	= 2,
99811c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_start	= 8,
99911c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_num	= 2,
100011c0d8fdSPaul Kocialkowski 
100111c0d8fdSPaul Kocialkowski 		.blc_anchor_left_start		= 576,
100211c0d8fdSPaul Kocialkowski 		.blc_anchor_left_end		= 831,
100311c0d8fdSPaul Kocialkowski 		.blc_anchor_right_start		= 1984,
100411c0d8fdSPaul Kocialkowski 		.blc_anchor_right_end		= 2239,
100511c0d8fdSPaul Kocialkowski 
100611c0d8fdSPaul Kocialkowski 		/* PLL */
100773dcffebSDaniel Scally 		.pll2_binning			= false,
100811c0d8fdSPaul Kocialkowski 
100911c0d8fdSPaul Kocialkowski 		/* Registers */
101011c0d8fdSPaul Kocialkowski 		.register_values	= ov8865_register_values_native,
101111c0d8fdSPaul Kocialkowski 		.register_values_count	=
101211c0d8fdSPaul Kocialkowski 			ARRAY_SIZE(ov8865_register_values_native),
101311c0d8fdSPaul Kocialkowski 	},
101411c0d8fdSPaul Kocialkowski 	/* 3264x1836 */
101511c0d8fdSPaul Kocialkowski 	{
101611c0d8fdSPaul Kocialkowski 		/* Horizontal */
101711c0d8fdSPaul Kocialkowski 		.output_size_x			= 3264,
1018295786e5SDaniel Scally 		.hts				= 3888,
101911c0d8fdSPaul Kocialkowski 
102011c0d8fdSPaul Kocialkowski 		/* Vertical */
102111c0d8fdSPaul Kocialkowski 		.output_size_y			= 1836,
1022295786e5SDaniel Scally 		.vts				= 2470,
102311c0d8fdSPaul Kocialkowski 
102411c0d8fdSPaul Kocialkowski 		.size_auto			= true,
102511c0d8fdSPaul Kocialkowski 		.size_auto_boundary_x		= 8,
102611c0d8fdSPaul Kocialkowski 		.size_auto_boundary_y		= 4,
102711c0d8fdSPaul Kocialkowski 
102811c0d8fdSPaul Kocialkowski 		/* Subsample increase */
102911c0d8fdSPaul Kocialkowski 		.inc_x_odd			= 1,
103011c0d8fdSPaul Kocialkowski 		.inc_x_even			= 1,
103111c0d8fdSPaul Kocialkowski 		.inc_y_odd			= 1,
103211c0d8fdSPaul Kocialkowski 		.inc_y_even			= 1,
103311c0d8fdSPaul Kocialkowski 
103411c0d8fdSPaul Kocialkowski 		/* VFIFO */
103511c0d8fdSPaul Kocialkowski 		.vfifo_read_start		= 16,
103611c0d8fdSPaul Kocialkowski 
103711c0d8fdSPaul Kocialkowski 		.ablc_num			= 4,
103811c0d8fdSPaul Kocialkowski 		.zline_num			= 1,
103911c0d8fdSPaul Kocialkowski 
104011c0d8fdSPaul Kocialkowski 		/* Black Level */
104111c0d8fdSPaul Kocialkowski 
104211c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_start	= 0,
104311c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_num		= 2,
104411c0d8fdSPaul Kocialkowski 		.blc_top_black_line_start	= 4,
104511c0d8fdSPaul Kocialkowski 		.blc_top_black_line_num		= 4,
104611c0d8fdSPaul Kocialkowski 
104711c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_start	= 2,
104811c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_num	= 2,
104911c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_start	= 8,
105011c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_num	= 2,
105111c0d8fdSPaul Kocialkowski 
105211c0d8fdSPaul Kocialkowski 		.blc_anchor_left_start		= 576,
105311c0d8fdSPaul Kocialkowski 		.blc_anchor_left_end		= 831,
105411c0d8fdSPaul Kocialkowski 		.blc_anchor_right_start		= 1984,
105511c0d8fdSPaul Kocialkowski 		.blc_anchor_right_end		= 2239,
105611c0d8fdSPaul Kocialkowski 
105711c0d8fdSPaul Kocialkowski 		/* PLL */
105873dcffebSDaniel Scally 		.pll2_binning			= false,
105911c0d8fdSPaul Kocialkowski 
106011c0d8fdSPaul Kocialkowski 		/* Registers */
106111c0d8fdSPaul Kocialkowski 		.register_values	= ov8865_register_values_native,
106211c0d8fdSPaul Kocialkowski 		.register_values_count	=
106311c0d8fdSPaul Kocialkowski 			ARRAY_SIZE(ov8865_register_values_native),
106411c0d8fdSPaul Kocialkowski 	},
106511c0d8fdSPaul Kocialkowski 	/* 1632x1224 */
106611c0d8fdSPaul Kocialkowski 	{
106711c0d8fdSPaul Kocialkowski 		/* Horizontal */
106811c0d8fdSPaul Kocialkowski 		.output_size_x			= 1632,
106911c0d8fdSPaul Kocialkowski 		.hts				= 1923,
107011c0d8fdSPaul Kocialkowski 
107111c0d8fdSPaul Kocialkowski 		/* Vertical */
107211c0d8fdSPaul Kocialkowski 		.output_size_y			= 1224,
107311c0d8fdSPaul Kocialkowski 		.vts				= 1248,
107411c0d8fdSPaul Kocialkowski 
107511c0d8fdSPaul Kocialkowski 		.size_auto			= true,
107611c0d8fdSPaul Kocialkowski 		.size_auto_boundary_x		= 8,
107711c0d8fdSPaul Kocialkowski 		.size_auto_boundary_y		= 8,
107811c0d8fdSPaul Kocialkowski 
107911c0d8fdSPaul Kocialkowski 		/* Subsample increase */
108011c0d8fdSPaul Kocialkowski 		.inc_x_odd			= 3,
108111c0d8fdSPaul Kocialkowski 		.inc_x_even			= 1,
108211c0d8fdSPaul Kocialkowski 		.inc_y_odd			= 3,
108311c0d8fdSPaul Kocialkowski 		.inc_y_even			= 1,
108411c0d8fdSPaul Kocialkowski 
108511c0d8fdSPaul Kocialkowski 		/* Binning */
108611c0d8fdSPaul Kocialkowski 		.binning_y			= true,
108711c0d8fdSPaul Kocialkowski 		.sync_hbin			= true,
108811c0d8fdSPaul Kocialkowski 
108911c0d8fdSPaul Kocialkowski 		/* VFIFO */
109011c0d8fdSPaul Kocialkowski 		.vfifo_read_start		= 116,
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	= 4,
110011c0d8fdSPaul Kocialkowski 		.blc_top_black_line_num		= 4,
110111c0d8fdSPaul Kocialkowski 
110211c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_start	= 2,
110311c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_num	= 2,
110411c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_start	= 8,
110511c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_num	= 2,
110611c0d8fdSPaul Kocialkowski 
110711c0d8fdSPaul Kocialkowski 		.blc_anchor_left_start		= 288,
110811c0d8fdSPaul Kocialkowski 		.blc_anchor_left_end		= 415,
110911c0d8fdSPaul Kocialkowski 		.blc_anchor_right_start		= 992,
111011c0d8fdSPaul Kocialkowski 		.blc_anchor_right_end		= 1119,
111111c0d8fdSPaul Kocialkowski 
111211c0d8fdSPaul Kocialkowski 		/* PLL */
111373dcffebSDaniel Scally 		.pll2_binning			= true,
111411c0d8fdSPaul Kocialkowski 
111511c0d8fdSPaul Kocialkowski 		/* Registers */
111611c0d8fdSPaul Kocialkowski 		.register_values	= ov8865_register_values_binning,
111711c0d8fdSPaul Kocialkowski 		.register_values_count	=
111811c0d8fdSPaul Kocialkowski 			ARRAY_SIZE(ov8865_register_values_binning),
111911c0d8fdSPaul Kocialkowski 	},
112011c0d8fdSPaul Kocialkowski 	/* 800x600 (SVGA) */
112111c0d8fdSPaul Kocialkowski 	{
112211c0d8fdSPaul Kocialkowski 		/* Horizontal */
112311c0d8fdSPaul Kocialkowski 		.output_size_x			= 800,
112411c0d8fdSPaul Kocialkowski 		.hts				= 1250,
112511c0d8fdSPaul Kocialkowski 
112611c0d8fdSPaul Kocialkowski 		/* Vertical */
112711c0d8fdSPaul Kocialkowski 		.output_size_y			= 600,
112811c0d8fdSPaul Kocialkowski 		.vts				= 640,
112911c0d8fdSPaul Kocialkowski 
113011c0d8fdSPaul Kocialkowski 		.size_auto			= true,
113111c0d8fdSPaul Kocialkowski 		.size_auto_boundary_x		= 8,
113211c0d8fdSPaul Kocialkowski 		.size_auto_boundary_y		= 8,
113311c0d8fdSPaul Kocialkowski 
113411c0d8fdSPaul Kocialkowski 		/* Subsample increase */
113511c0d8fdSPaul Kocialkowski 		.inc_x_odd			= 3,
113611c0d8fdSPaul Kocialkowski 		.inc_x_even			= 1,
113711c0d8fdSPaul Kocialkowski 		.inc_y_odd			= 5,
113811c0d8fdSPaul Kocialkowski 		.inc_y_even			= 3,
113911c0d8fdSPaul Kocialkowski 
114011c0d8fdSPaul Kocialkowski 		/* Binning */
114111c0d8fdSPaul Kocialkowski 		.binning_y			= true,
114211c0d8fdSPaul Kocialkowski 		.variopixel			= true,
114311c0d8fdSPaul Kocialkowski 		.variopixel_hsub_coef		= 2,
114411c0d8fdSPaul Kocialkowski 		.variopixel_vsub_coef		= 1,
114511c0d8fdSPaul Kocialkowski 		.sync_hbin			= true,
114611c0d8fdSPaul Kocialkowski 		.horz_var2			= true,
114711c0d8fdSPaul Kocialkowski 
114811c0d8fdSPaul Kocialkowski 		/* VFIFO */
114911c0d8fdSPaul Kocialkowski 		.vfifo_read_start		= 80,
115011c0d8fdSPaul Kocialkowski 
115111c0d8fdSPaul Kocialkowski 		.ablc_num			= 8,
115211c0d8fdSPaul Kocialkowski 		.zline_num			= 2,
115311c0d8fdSPaul Kocialkowski 
115411c0d8fdSPaul Kocialkowski 		/* Black Level */
115511c0d8fdSPaul Kocialkowski 
115611c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_start	= 0,
115711c0d8fdSPaul Kocialkowski 		.blc_top_zero_line_num		= 2,
115811c0d8fdSPaul Kocialkowski 		.blc_top_black_line_start	= 2,
115911c0d8fdSPaul Kocialkowski 		.blc_top_black_line_num		= 2,
116011c0d8fdSPaul Kocialkowski 
116111c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_start	= 0,
116211c0d8fdSPaul Kocialkowski 		.blc_bottom_zero_line_num	= 0,
116311c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_start	= 4,
116411c0d8fdSPaul Kocialkowski 		.blc_bottom_black_line_num	= 2,
116511c0d8fdSPaul Kocialkowski 
116611c0d8fdSPaul Kocialkowski 		.blc_col_shift_mask	= OV8865_BLC_CTRL1_COL_SHIFT_128,
116711c0d8fdSPaul Kocialkowski 
116811c0d8fdSPaul Kocialkowski 		.blc_anchor_left_start		= 288,
116911c0d8fdSPaul Kocialkowski 		.blc_anchor_left_end		= 415,
117011c0d8fdSPaul Kocialkowski 		.blc_anchor_right_start		= 992,
117111c0d8fdSPaul Kocialkowski 		.blc_anchor_right_end		= 1119,
117211c0d8fdSPaul Kocialkowski 
117311c0d8fdSPaul Kocialkowski 		/* PLL */
117473dcffebSDaniel Scally 		.pll2_binning			= true,
117511c0d8fdSPaul Kocialkowski 
117611c0d8fdSPaul Kocialkowski 		/* Registers */
117711c0d8fdSPaul Kocialkowski 		.register_values	= ov8865_register_values_binning,
117811c0d8fdSPaul Kocialkowski 		.register_values_count	=
117911c0d8fdSPaul Kocialkowski 			ARRAY_SIZE(ov8865_register_values_binning),
118011c0d8fdSPaul Kocialkowski 	},
118111c0d8fdSPaul Kocialkowski };
118211c0d8fdSPaul Kocialkowski 
118311c0d8fdSPaul Kocialkowski static const u32 ov8865_mbus_codes[] = {
118411c0d8fdSPaul Kocialkowski 	MEDIA_BUS_FMT_SBGGR10_1X10,
118511c0d8fdSPaul Kocialkowski };
118611c0d8fdSPaul Kocialkowski 
118711c0d8fdSPaul Kocialkowski static const struct ov8865_register_value ov8865_init_sequence[] = {
118811c0d8fdSPaul Kocialkowski 	/* Analog */
118911c0d8fdSPaul Kocialkowski 
119011c0d8fdSPaul Kocialkowski 	{ 0x3604, 0x04 },
119111c0d8fdSPaul Kocialkowski 	{ 0x3602, 0x30 },
119211c0d8fdSPaul Kocialkowski 	{ 0x3605, 0x00 },
119311c0d8fdSPaul Kocialkowski 	{ 0x3607, 0x20 },
119411c0d8fdSPaul Kocialkowski 	{ 0x3608, 0x11 },
119511c0d8fdSPaul Kocialkowski 	{ 0x3609, 0x68 },
119611c0d8fdSPaul Kocialkowski 	{ 0x360a, 0x40 },
119711c0d8fdSPaul Kocialkowski 	{ 0x360c, 0xdd },
119811c0d8fdSPaul Kocialkowski 	{ 0x360e, 0x0c },
119911c0d8fdSPaul Kocialkowski 	{ 0x3610, 0x07 },
120011c0d8fdSPaul Kocialkowski 	{ 0x3612, 0x86 },
120111c0d8fdSPaul Kocialkowski 	{ 0x3613, 0x58 },
120211c0d8fdSPaul Kocialkowski 	{ 0x3614, 0x28 },
120311c0d8fdSPaul Kocialkowski 	{ 0x3617, 0x40 },
120411c0d8fdSPaul Kocialkowski 	{ 0x3618, 0x5a },
120511c0d8fdSPaul Kocialkowski 	{ 0x3619, 0x9b },
120611c0d8fdSPaul Kocialkowski 	{ 0x361c, 0x00 },
120711c0d8fdSPaul Kocialkowski 	{ 0x361d, 0x60 },
120811c0d8fdSPaul Kocialkowski 	{ 0x3631, 0x60 },
120911c0d8fdSPaul Kocialkowski 	{ 0x3633, 0x10 },
121011c0d8fdSPaul Kocialkowski 	{ 0x3634, 0x10 },
121111c0d8fdSPaul Kocialkowski 	{ 0x3635, 0x10 },
121211c0d8fdSPaul Kocialkowski 	{ 0x3636, 0x10 },
121311c0d8fdSPaul Kocialkowski 	{ 0x3638, 0xff },
121411c0d8fdSPaul Kocialkowski 	{ 0x3641, 0x55 },
121511c0d8fdSPaul Kocialkowski 	{ 0x3646, 0x86 },
121611c0d8fdSPaul Kocialkowski 	{ 0x3647, 0x27 },
121711c0d8fdSPaul Kocialkowski 	{ 0x364a, 0x1b },
121811c0d8fdSPaul Kocialkowski 
121911c0d8fdSPaul Kocialkowski 	/* Sensor */
122011c0d8fdSPaul Kocialkowski 
122111c0d8fdSPaul Kocialkowski 	{ 0x3700, 0x24 },
122211c0d8fdSPaul Kocialkowski 	{ 0x3701, 0x0c },
122311c0d8fdSPaul Kocialkowski 	{ 0x3702, 0x28 },
122411c0d8fdSPaul Kocialkowski 	{ 0x3703, 0x19 },
122511c0d8fdSPaul Kocialkowski 	{ 0x3704, 0x14 },
122611c0d8fdSPaul Kocialkowski 	{ 0x3705, 0x00 },
122711c0d8fdSPaul Kocialkowski 	{ 0x3706, 0x38 },
122811c0d8fdSPaul Kocialkowski 	{ 0x3707, 0x04 },
122911c0d8fdSPaul Kocialkowski 	{ 0x3708, 0x24 },
123011c0d8fdSPaul Kocialkowski 	{ 0x3709, 0x40 },
123111c0d8fdSPaul Kocialkowski 	{ 0x370a, 0x00 },
123211c0d8fdSPaul Kocialkowski 	{ 0x370b, 0xb8 },
123311c0d8fdSPaul Kocialkowski 	{ 0x370c, 0x04 },
123411c0d8fdSPaul Kocialkowski 	{ 0x3718, 0x12 },
123511c0d8fdSPaul Kocialkowski 	{ 0x3719, 0x31 },
123611c0d8fdSPaul Kocialkowski 	{ 0x3712, 0x42 },
123711c0d8fdSPaul Kocialkowski 	{ 0x3714, 0x12 },
123811c0d8fdSPaul Kocialkowski 	{ 0x371e, 0x19 },
123911c0d8fdSPaul Kocialkowski 	{ 0x371f, 0x40 },
124011c0d8fdSPaul Kocialkowski 	{ 0x3720, 0x05 },
124111c0d8fdSPaul Kocialkowski 	{ 0x3721, 0x05 },
124211c0d8fdSPaul Kocialkowski 	{ 0x3724, 0x02 },
124311c0d8fdSPaul Kocialkowski 	{ 0x3725, 0x02 },
124411c0d8fdSPaul Kocialkowski 	{ 0x3726, 0x06 },
124511c0d8fdSPaul Kocialkowski 	{ 0x3728, 0x05 },
124611c0d8fdSPaul Kocialkowski 	{ 0x3729, 0x02 },
124711c0d8fdSPaul Kocialkowski 	{ 0x372a, 0x03 },
124811c0d8fdSPaul Kocialkowski 	{ 0x372b, 0x53 },
124911c0d8fdSPaul Kocialkowski 	{ 0x372c, 0xa3 },
125011c0d8fdSPaul Kocialkowski 	{ 0x372d, 0x53 },
125111c0d8fdSPaul Kocialkowski 	{ 0x372e, 0x06 },
125211c0d8fdSPaul Kocialkowski 	{ 0x372f, 0x10 },
125311c0d8fdSPaul Kocialkowski 	{ 0x3730, 0x01 },
125411c0d8fdSPaul Kocialkowski 	{ 0x3731, 0x06 },
125511c0d8fdSPaul Kocialkowski 	{ 0x3732, 0x14 },
125611c0d8fdSPaul Kocialkowski 	{ 0x3733, 0x10 },
125711c0d8fdSPaul Kocialkowski 	{ 0x3734, 0x40 },
125811c0d8fdSPaul Kocialkowski 	{ 0x3736, 0x20 },
125911c0d8fdSPaul Kocialkowski 	{ 0x373a, 0x02 },
126011c0d8fdSPaul Kocialkowski 	{ 0x373b, 0x0c },
126111c0d8fdSPaul Kocialkowski 	{ 0x373c, 0x0a },
126211c0d8fdSPaul Kocialkowski 	{ 0x373e, 0x03 },
126311c0d8fdSPaul Kocialkowski 	{ 0x3755, 0x40 },
126411c0d8fdSPaul Kocialkowski 	{ 0x3758, 0x00 },
126511c0d8fdSPaul Kocialkowski 	{ 0x3759, 0x4c },
126611c0d8fdSPaul Kocialkowski 	{ 0x375a, 0x06 },
126711c0d8fdSPaul Kocialkowski 	{ 0x375b, 0x13 },
126811c0d8fdSPaul Kocialkowski 	{ 0x375c, 0x40 },
126911c0d8fdSPaul Kocialkowski 	{ 0x375d, 0x02 },
127011c0d8fdSPaul Kocialkowski 	{ 0x375e, 0x00 },
127111c0d8fdSPaul Kocialkowski 	{ 0x375f, 0x14 },
127211c0d8fdSPaul Kocialkowski 	{ 0x3767, 0x1c },
127311c0d8fdSPaul Kocialkowski 	{ 0x3768, 0x04 },
127411c0d8fdSPaul Kocialkowski 	{ 0x3769, 0x20 },
127511c0d8fdSPaul Kocialkowski 	{ 0x376c, 0xc0 },
127611c0d8fdSPaul Kocialkowski 	{ 0x376d, 0xc0 },
127711c0d8fdSPaul Kocialkowski 	{ 0x376a, 0x08 },
127811c0d8fdSPaul Kocialkowski 	{ 0x3761, 0x00 },
127911c0d8fdSPaul Kocialkowski 	{ 0x3762, 0x00 },
128011c0d8fdSPaul Kocialkowski 	{ 0x3763, 0x00 },
128111c0d8fdSPaul Kocialkowski 	{ 0x3766, 0xff },
128211c0d8fdSPaul Kocialkowski 	{ 0x376b, 0x42 },
128311c0d8fdSPaul Kocialkowski 	{ 0x3772, 0x23 },
128411c0d8fdSPaul Kocialkowski 	{ 0x3773, 0x02 },
128511c0d8fdSPaul Kocialkowski 	{ 0x3774, 0x16 },
128611c0d8fdSPaul Kocialkowski 	{ 0x3775, 0x12 },
128711c0d8fdSPaul Kocialkowski 	{ 0x3776, 0x08 },
128811c0d8fdSPaul Kocialkowski 	{ 0x37a0, 0x44 },
128911c0d8fdSPaul Kocialkowski 	{ 0x37a1, 0x3d },
129011c0d8fdSPaul Kocialkowski 	{ 0x37a2, 0x3d },
129111c0d8fdSPaul Kocialkowski 	{ 0x37a3, 0x01 },
129211c0d8fdSPaul Kocialkowski 	{ 0x37a4, 0x00 },
129311c0d8fdSPaul Kocialkowski 	{ 0x37a5, 0x08 },
129411c0d8fdSPaul Kocialkowski 	{ 0x37a6, 0x00 },
129511c0d8fdSPaul Kocialkowski 	{ 0x37a7, 0x44 },
129611c0d8fdSPaul Kocialkowski 	{ 0x37a8, 0x58 },
129711c0d8fdSPaul Kocialkowski 	{ 0x37a9, 0x58 },
129811c0d8fdSPaul Kocialkowski 	{ 0x3760, 0x00 },
129911c0d8fdSPaul Kocialkowski 	{ 0x376f, 0x01 },
130011c0d8fdSPaul Kocialkowski 	{ 0x37aa, 0x44 },
130111c0d8fdSPaul Kocialkowski 	{ 0x37ab, 0x2e },
130211c0d8fdSPaul Kocialkowski 	{ 0x37ac, 0x2e },
130311c0d8fdSPaul Kocialkowski 	{ 0x37ad, 0x33 },
130411c0d8fdSPaul Kocialkowski 	{ 0x37ae, 0x0d },
130511c0d8fdSPaul Kocialkowski 	{ 0x37af, 0x0d },
130611c0d8fdSPaul Kocialkowski 	{ 0x37b0, 0x00 },
130711c0d8fdSPaul Kocialkowski 	{ 0x37b1, 0x00 },
130811c0d8fdSPaul Kocialkowski 	{ 0x37b2, 0x00 },
130911c0d8fdSPaul Kocialkowski 	{ 0x37b3, 0x42 },
131011c0d8fdSPaul Kocialkowski 	{ 0x37b4, 0x42 },
131111c0d8fdSPaul Kocialkowski 	{ 0x37b5, 0x33 },
131211c0d8fdSPaul Kocialkowski 	{ 0x37b6, 0x00 },
131311c0d8fdSPaul Kocialkowski 	{ 0x37b7, 0x00 },
131411c0d8fdSPaul Kocialkowski 	{ 0x37b8, 0x00 },
131511c0d8fdSPaul Kocialkowski 	{ 0x37b9, 0xff },
131611c0d8fdSPaul Kocialkowski 
131711c0d8fdSPaul Kocialkowski 	/* ADC Sync */
131811c0d8fdSPaul Kocialkowski 
131911c0d8fdSPaul Kocialkowski 	{ 0x4503, 0x10 },
132011c0d8fdSPaul Kocialkowski };
132111c0d8fdSPaul Kocialkowski 
132211c0d8fdSPaul Kocialkowski static const s64 ov8865_link_freq_menu[] = {
132311c0d8fdSPaul Kocialkowski 	360000000,
132411c0d8fdSPaul Kocialkowski };
132511c0d8fdSPaul Kocialkowski 
132611c0d8fdSPaul Kocialkowski static const char *const ov8865_test_pattern_menu[] = {
132711c0d8fdSPaul Kocialkowski 	"Disabled",
132811c0d8fdSPaul Kocialkowski 	"Random data",
132911c0d8fdSPaul Kocialkowski 	"Color bars",
133011c0d8fdSPaul Kocialkowski 	"Color bars with rolling bar",
133111c0d8fdSPaul Kocialkowski 	"Color squares",
133211c0d8fdSPaul Kocialkowski 	"Color squares with rolling bar"
133311c0d8fdSPaul Kocialkowski };
133411c0d8fdSPaul Kocialkowski 
133511c0d8fdSPaul Kocialkowski static const u8 ov8865_test_pattern_bits[] = {
133611c0d8fdSPaul Kocialkowski 	0,
133711c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA,
133811c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_BARS,
133911c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN |
134011c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_COLOR_BARS,
134111c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES,
134211c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN |
134311c0d8fdSPaul Kocialkowski 	OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES,
134411c0d8fdSPaul Kocialkowski };
134511c0d8fdSPaul Kocialkowski 
134611c0d8fdSPaul Kocialkowski /* Input/Output */
134711c0d8fdSPaul Kocialkowski 
ov8865_read(struct ov8865_sensor * sensor,u16 address,u8 * value)134811c0d8fdSPaul Kocialkowski static int ov8865_read(struct ov8865_sensor *sensor, u16 address, u8 *value)
134911c0d8fdSPaul Kocialkowski {
135011c0d8fdSPaul Kocialkowski 	unsigned char data[2] = { address >> 8, address & 0xff };
135111c0d8fdSPaul Kocialkowski 	struct i2c_client *client = sensor->i2c_client;
135211c0d8fdSPaul Kocialkowski 	int ret;
135311c0d8fdSPaul Kocialkowski 
135411c0d8fdSPaul Kocialkowski 	ret = i2c_master_send(client, data, sizeof(data));
135511c0d8fdSPaul Kocialkowski 	if (ret < 0) {
135611c0d8fdSPaul Kocialkowski 		dev_dbg(&client->dev, "i2c send error at address %#04x\n",
135711c0d8fdSPaul Kocialkowski 			address);
135811c0d8fdSPaul Kocialkowski 		return ret;
135911c0d8fdSPaul Kocialkowski 	}
136011c0d8fdSPaul Kocialkowski 
136111c0d8fdSPaul Kocialkowski 	ret = i2c_master_recv(client, value, 1);
136211c0d8fdSPaul Kocialkowski 	if (ret < 0) {
136311c0d8fdSPaul Kocialkowski 		dev_dbg(&client->dev, "i2c recv error at address %#04x\n",
136411c0d8fdSPaul Kocialkowski 			address);
136511c0d8fdSPaul Kocialkowski 		return ret;
136611c0d8fdSPaul Kocialkowski 	}
136711c0d8fdSPaul Kocialkowski 
136811c0d8fdSPaul Kocialkowski 	return 0;
136911c0d8fdSPaul Kocialkowski }
137011c0d8fdSPaul Kocialkowski 
ov8865_write(struct ov8865_sensor * sensor,u16 address,u8 value)137111c0d8fdSPaul Kocialkowski static int ov8865_write(struct ov8865_sensor *sensor, u16 address, u8 value)
137211c0d8fdSPaul Kocialkowski {
137311c0d8fdSPaul Kocialkowski 	unsigned char data[3] = { address >> 8, address & 0xff, value };
137411c0d8fdSPaul Kocialkowski 	struct i2c_client *client = sensor->i2c_client;
137511c0d8fdSPaul Kocialkowski 	int ret;
137611c0d8fdSPaul Kocialkowski 
137711c0d8fdSPaul Kocialkowski 	ret = i2c_master_send(client, data, sizeof(data));
137811c0d8fdSPaul Kocialkowski 	if (ret < 0) {
137911c0d8fdSPaul Kocialkowski 		dev_dbg(&client->dev, "i2c send error at address %#04x\n",
138011c0d8fdSPaul Kocialkowski 			address);
138111c0d8fdSPaul Kocialkowski 		return ret;
138211c0d8fdSPaul Kocialkowski 	}
138311c0d8fdSPaul Kocialkowski 
138411c0d8fdSPaul Kocialkowski 	return 0;
138511c0d8fdSPaul Kocialkowski }
138611c0d8fdSPaul Kocialkowski 
ov8865_write_sequence(struct ov8865_sensor * sensor,const struct ov8865_register_value * sequence,unsigned int sequence_count)138711c0d8fdSPaul Kocialkowski static int ov8865_write_sequence(struct ov8865_sensor *sensor,
138811c0d8fdSPaul Kocialkowski 				 const struct ov8865_register_value *sequence,
138911c0d8fdSPaul Kocialkowski 				 unsigned int sequence_count)
139011c0d8fdSPaul Kocialkowski {
139111c0d8fdSPaul Kocialkowski 	unsigned int i;
139211c0d8fdSPaul Kocialkowski 	int ret = 0;
139311c0d8fdSPaul Kocialkowski 
139411c0d8fdSPaul Kocialkowski 	for (i = 0; i < sequence_count; i++) {
139511c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, sequence[i].address,
139611c0d8fdSPaul Kocialkowski 				   sequence[i].value);
139711c0d8fdSPaul Kocialkowski 		if (ret)
139811c0d8fdSPaul Kocialkowski 			break;
139911c0d8fdSPaul Kocialkowski 
140011c0d8fdSPaul Kocialkowski 		if (sequence[i].delay_ms)
140111c0d8fdSPaul Kocialkowski 			msleep(sequence[i].delay_ms);
140211c0d8fdSPaul Kocialkowski 	}
140311c0d8fdSPaul Kocialkowski 
140411c0d8fdSPaul Kocialkowski 	return ret;
140511c0d8fdSPaul Kocialkowski }
140611c0d8fdSPaul Kocialkowski 
ov8865_update_bits(struct ov8865_sensor * sensor,u16 address,u8 mask,u8 bits)140711c0d8fdSPaul Kocialkowski static int ov8865_update_bits(struct ov8865_sensor *sensor, u16 address,
140811c0d8fdSPaul Kocialkowski 			      u8 mask, u8 bits)
140911c0d8fdSPaul Kocialkowski {
141011c0d8fdSPaul Kocialkowski 	u8 value = 0;
141111c0d8fdSPaul Kocialkowski 	int ret;
141211c0d8fdSPaul Kocialkowski 
141311c0d8fdSPaul Kocialkowski 	ret = ov8865_read(sensor, address, &value);
141411c0d8fdSPaul Kocialkowski 	if (ret)
141511c0d8fdSPaul Kocialkowski 		return ret;
141611c0d8fdSPaul Kocialkowski 
141711c0d8fdSPaul Kocialkowski 	value &= ~mask;
141811c0d8fdSPaul Kocialkowski 	value |= bits;
141911c0d8fdSPaul Kocialkowski 
142011c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, address, value);
142111c0d8fdSPaul Kocialkowski }
142211c0d8fdSPaul Kocialkowski 
142311c0d8fdSPaul Kocialkowski /* Sensor */
142411c0d8fdSPaul Kocialkowski 
ov8865_sw_reset(struct ov8865_sensor * sensor)142511c0d8fdSPaul Kocialkowski static int ov8865_sw_reset(struct ov8865_sensor *sensor)
142611c0d8fdSPaul Kocialkowski {
142711c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_SW_RESET_REG, OV8865_SW_RESET_RESET);
142811c0d8fdSPaul Kocialkowski }
142911c0d8fdSPaul Kocialkowski 
ov8865_sw_standby(struct ov8865_sensor * sensor,int standby)143011c0d8fdSPaul Kocialkowski static int ov8865_sw_standby(struct ov8865_sensor *sensor, int standby)
143111c0d8fdSPaul Kocialkowski {
143211c0d8fdSPaul Kocialkowski 	u8 value = 0;
143311c0d8fdSPaul Kocialkowski 
143411c0d8fdSPaul Kocialkowski 	if (!standby)
143511c0d8fdSPaul Kocialkowski 		value = OV8865_SW_STANDBY_STREAM_ON;
143611c0d8fdSPaul Kocialkowski 
143711c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_SW_STANDBY_REG, value);
143811c0d8fdSPaul Kocialkowski }
143911c0d8fdSPaul Kocialkowski 
ov8865_chip_id_check(struct ov8865_sensor * sensor)144011c0d8fdSPaul Kocialkowski static int ov8865_chip_id_check(struct ov8865_sensor *sensor)
144111c0d8fdSPaul Kocialkowski {
144211c0d8fdSPaul Kocialkowski 	u16 regs[] = { OV8865_CHIP_ID_HH_REG, OV8865_CHIP_ID_H_REG,
144311c0d8fdSPaul Kocialkowski 		       OV8865_CHIP_ID_L_REG };
144411c0d8fdSPaul Kocialkowski 	u8 values[] = { OV8865_CHIP_ID_HH_VALUE, OV8865_CHIP_ID_H_VALUE,
144511c0d8fdSPaul Kocialkowski 			OV8865_CHIP_ID_L_VALUE };
144611c0d8fdSPaul Kocialkowski 	unsigned int i;
144711c0d8fdSPaul Kocialkowski 	u8 value;
144811c0d8fdSPaul Kocialkowski 	int ret;
144911c0d8fdSPaul Kocialkowski 
145011c0d8fdSPaul Kocialkowski 	for (i = 0; i < ARRAY_SIZE(regs); i++) {
145111c0d8fdSPaul Kocialkowski 		ret = ov8865_read(sensor, regs[i], &value);
145211c0d8fdSPaul Kocialkowski 		if (ret < 0)
145311c0d8fdSPaul Kocialkowski 			return ret;
145411c0d8fdSPaul Kocialkowski 
145511c0d8fdSPaul Kocialkowski 		if (value != values[i]) {
145611c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev,
145711c0d8fdSPaul Kocialkowski 				"chip id value mismatch: %#x instead of %#x\n",
145811c0d8fdSPaul Kocialkowski 				value, values[i]);
145911c0d8fdSPaul Kocialkowski 			return -EINVAL;
146011c0d8fdSPaul Kocialkowski 		}
146111c0d8fdSPaul Kocialkowski 	}
146211c0d8fdSPaul Kocialkowski 
146311c0d8fdSPaul Kocialkowski 	return 0;
146411c0d8fdSPaul Kocialkowski }
146511c0d8fdSPaul Kocialkowski 
ov8865_charge_pump_configure(struct ov8865_sensor * sensor)146611c0d8fdSPaul Kocialkowski static int ov8865_charge_pump_configure(struct ov8865_sensor *sensor)
146711c0d8fdSPaul Kocialkowski {
146811c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_PUMP_CLK_DIV_REG,
146911c0d8fdSPaul Kocialkowski 			    OV8865_PUMP_CLK_DIV_PUMP_P(1));
147011c0d8fdSPaul Kocialkowski }
147111c0d8fdSPaul Kocialkowski 
ov8865_mipi_configure(struct ov8865_sensor * sensor)147211c0d8fdSPaul Kocialkowski static int ov8865_mipi_configure(struct ov8865_sensor *sensor)
147311c0d8fdSPaul Kocialkowski {
147494d964e5SLaurent Pinchart 	struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 =
147511c0d8fdSPaul Kocialkowski 		&sensor->endpoint.bus.mipi_csi2;
147611c0d8fdSPaul Kocialkowski 	unsigned int lanes_count = bus_mipi_csi2->num_data_lanes;
147711c0d8fdSPaul Kocialkowski 	int ret;
147811c0d8fdSPaul Kocialkowski 
147911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL0_REG,
148011c0d8fdSPaul Kocialkowski 			   OV8865_MIPI_SC_CTRL0_LANES(lanes_count) |
148111c0d8fdSPaul Kocialkowski 			   OV8865_MIPI_SC_CTRL0_MIPI_EN |
148211c0d8fdSPaul Kocialkowski 			   OV8865_MIPI_SC_CTRL0_UNKNOWN);
148311c0d8fdSPaul Kocialkowski 	if (ret)
148411c0d8fdSPaul Kocialkowski 		return ret;
148511c0d8fdSPaul Kocialkowski 
148611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_MIPI_SC_CTRL2_REG,
148711c0d8fdSPaul Kocialkowski 			   OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC);
148811c0d8fdSPaul Kocialkowski 	if (ret)
148911c0d8fdSPaul Kocialkowski 		return ret;
149011c0d8fdSPaul Kocialkowski 
149111c0d8fdSPaul Kocialkowski 	if (lanes_count >= 2) {
149211c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL01_REG,
149311c0d8fdSPaul Kocialkowski 				   OV8865_MIPI_LANE_SEL01_LANE0(0) |
149411c0d8fdSPaul Kocialkowski 				   OV8865_MIPI_LANE_SEL01_LANE1(1));
149511c0d8fdSPaul Kocialkowski 		if (ret)
149611c0d8fdSPaul Kocialkowski 			return ret;
149711c0d8fdSPaul Kocialkowski 	}
149811c0d8fdSPaul Kocialkowski 
149911c0d8fdSPaul Kocialkowski 	if (lanes_count >= 4) {
150011c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_MIPI_LANE_SEL23_REG,
150111c0d8fdSPaul Kocialkowski 				   OV8865_MIPI_LANE_SEL23_LANE2(2) |
150211c0d8fdSPaul Kocialkowski 				   OV8865_MIPI_LANE_SEL23_LANE3(3));
150311c0d8fdSPaul Kocialkowski 		if (ret)
150411c0d8fdSPaul Kocialkowski 			return ret;
150511c0d8fdSPaul Kocialkowski 	}
150611c0d8fdSPaul Kocialkowski 
150711c0d8fdSPaul Kocialkowski 	ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG,
150811c0d8fdSPaul Kocialkowski 				 OV8865_CLK_SEL1_MIPI_EOF,
150911c0d8fdSPaul Kocialkowski 				 OV8865_CLK_SEL1_MIPI_EOF);
151011c0d8fdSPaul Kocialkowski 	if (ret)
151111c0d8fdSPaul Kocialkowski 		return ret;
151211c0d8fdSPaul Kocialkowski 
151311c0d8fdSPaul Kocialkowski 	/*
151411c0d8fdSPaul Kocialkowski 	 * This value might need to change depending on PCLK rate,
151511c0d8fdSPaul Kocialkowski 	 * but it's unclear how. This value seems to generally work
151611c0d8fdSPaul Kocialkowski 	 * while the default value was found to cause transmission errors.
151711c0d8fdSPaul Kocialkowski 	 */
151811c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_MIPI_PCLK_PERIOD_REG, 0x16);
151911c0d8fdSPaul Kocialkowski }
152011c0d8fdSPaul Kocialkowski 
ov8865_black_level_configure(struct ov8865_sensor * sensor)152111c0d8fdSPaul Kocialkowski static int ov8865_black_level_configure(struct ov8865_sensor *sensor)
152211c0d8fdSPaul Kocialkowski {
152311c0d8fdSPaul Kocialkowski 	int ret;
152411c0d8fdSPaul Kocialkowski 
152511c0d8fdSPaul Kocialkowski 	/* Trigger BLC on relevant events and enable filter. */
152611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_CTRL0_REG,
152711c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_TRIG_RANGE_EN |
152811c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_TRIG_FORMAT_EN |
152911c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_TRIG_GAIN_EN |
153011c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN |
153111c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL0_FILTER_EN);
153211c0d8fdSPaul Kocialkowski 	if (ret)
153311c0d8fdSPaul Kocialkowski 		return ret;
153411c0d8fdSPaul Kocialkowski 
153511c0d8fdSPaul Kocialkowski 	/* Lower BLC offset trigger threshold. */
153611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_CTRLD_REG,
153711c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRLD_OFFSET_TRIGGER(16));
153811c0d8fdSPaul Kocialkowski 	if (ret)
153911c0d8fdSPaul Kocialkowski 		return ret;
154011c0d8fdSPaul Kocialkowski 
154111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_CTRL1F_REG, 0);
154211c0d8fdSPaul Kocialkowski 	if (ret)
154311c0d8fdSPaul Kocialkowski 		return ret;
154411c0d8fdSPaul Kocialkowski 
154511c0d8fdSPaul Kocialkowski 	/* Increase BLC offset maximum limit. */
154611c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_BLC_OFFSET_LIMIT_REG,
154711c0d8fdSPaul Kocialkowski 			    OV8865_BLC_OFFSET_LIMIT(63));
154811c0d8fdSPaul Kocialkowski }
154911c0d8fdSPaul Kocialkowski 
ov8865_isp_configure(struct ov8865_sensor * sensor)155011c0d8fdSPaul Kocialkowski static int ov8865_isp_configure(struct ov8865_sensor *sensor)
155111c0d8fdSPaul Kocialkowski {
155211c0d8fdSPaul Kocialkowski 	int ret;
155311c0d8fdSPaul Kocialkowski 
155411c0d8fdSPaul Kocialkowski 	/* Disable lens correction. */
155511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ISP_CTRL0_REG,
155611c0d8fdSPaul Kocialkowski 			   OV8865_ISP_CTRL0_WHITE_BALANCE_EN |
155711c0d8fdSPaul Kocialkowski 			   OV8865_ISP_CTRL0_DPC_BLACK_EN |
155811c0d8fdSPaul Kocialkowski 			   OV8865_ISP_CTRL0_DPC_WHITE_EN);
155911c0d8fdSPaul Kocialkowski 	if (ret)
156011c0d8fdSPaul Kocialkowski 		return ret;
156111c0d8fdSPaul Kocialkowski 
156211c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_ISP_CTRL1_REG,
156311c0d8fdSPaul Kocialkowski 			    OV8865_ISP_CTRL1_BLC_EN);
156411c0d8fdSPaul Kocialkowski }
156511c0d8fdSPaul Kocialkowski 
ov8865_mode_pll1_rate(struct ov8865_sensor * sensor,const struct ov8865_mode * mode)156611c0d8fdSPaul Kocialkowski static unsigned long ov8865_mode_pll1_rate(struct ov8865_sensor *sensor,
156711c0d8fdSPaul Kocialkowski 					   const struct ov8865_mode *mode)
156811c0d8fdSPaul Kocialkowski {
156973dcffebSDaniel Scally 	const struct ov8865_pll1_config *config;
157011c0d8fdSPaul Kocialkowski 	unsigned long pll1_rate;
157111c0d8fdSPaul Kocialkowski 
157273dcffebSDaniel Scally 	config = sensor->pll_configs->pll1_config;
157373dcffebSDaniel Scally 	pll1_rate = sensor->extclk_rate * config->pll_mul / config->pll_pre_div_half;
157411c0d8fdSPaul Kocialkowski 
157511c0d8fdSPaul Kocialkowski 	switch (config->pll_pre_div) {
157611c0d8fdSPaul Kocialkowski 	case 0:
157711c0d8fdSPaul Kocialkowski 		break;
157811c0d8fdSPaul Kocialkowski 	case 1:
157911c0d8fdSPaul Kocialkowski 		pll1_rate *= 3;
158011c0d8fdSPaul Kocialkowski 		pll1_rate /= 2;
158111c0d8fdSPaul Kocialkowski 		break;
158211c0d8fdSPaul Kocialkowski 	case 3:
158311c0d8fdSPaul Kocialkowski 		pll1_rate *= 5;
158411c0d8fdSPaul Kocialkowski 		pll1_rate /= 2;
158511c0d8fdSPaul Kocialkowski 		break;
158611c0d8fdSPaul Kocialkowski 	case 4:
158711c0d8fdSPaul Kocialkowski 		pll1_rate /= 3;
158811c0d8fdSPaul Kocialkowski 		break;
158911c0d8fdSPaul Kocialkowski 	case 5:
159011c0d8fdSPaul Kocialkowski 		pll1_rate /= 4;
159111c0d8fdSPaul Kocialkowski 		break;
159211c0d8fdSPaul Kocialkowski 	case 7:
159311c0d8fdSPaul Kocialkowski 		pll1_rate /= 8;
159411c0d8fdSPaul Kocialkowski 		break;
159511c0d8fdSPaul Kocialkowski 	default:
159611c0d8fdSPaul Kocialkowski 		pll1_rate /= config->pll_pre_div;
159711c0d8fdSPaul Kocialkowski 		break;
159811c0d8fdSPaul Kocialkowski 	}
159911c0d8fdSPaul Kocialkowski 
160011c0d8fdSPaul Kocialkowski 	return pll1_rate;
160111c0d8fdSPaul Kocialkowski }
160211c0d8fdSPaul Kocialkowski 
ov8865_mode_pll1_configure(struct ov8865_sensor * sensor,const struct ov8865_mode * mode,u32 mbus_code)160311c0d8fdSPaul Kocialkowski static int ov8865_mode_pll1_configure(struct ov8865_sensor *sensor,
160411c0d8fdSPaul Kocialkowski 				      const struct ov8865_mode *mode,
160511c0d8fdSPaul Kocialkowski 				      u32 mbus_code)
160611c0d8fdSPaul Kocialkowski {
160773dcffebSDaniel Scally 	const struct ov8865_pll1_config *config;
160811c0d8fdSPaul Kocialkowski 	u8 value;
160911c0d8fdSPaul Kocialkowski 	int ret;
161011c0d8fdSPaul Kocialkowski 
161173dcffebSDaniel Scally 	config = sensor->pll_configs->pll1_config;
161273dcffebSDaniel Scally 
161311c0d8fdSPaul Kocialkowski 	switch (mbus_code) {
161411c0d8fdSPaul Kocialkowski 	case MEDIA_BUS_FMT_SBGGR10_1X10:
161511c0d8fdSPaul Kocialkowski 		value = OV8865_MIPI_BIT_SEL(10);
161611c0d8fdSPaul Kocialkowski 		break;
161711c0d8fdSPaul Kocialkowski 	default:
161811c0d8fdSPaul Kocialkowski 		return -EINVAL;
161911c0d8fdSPaul Kocialkowski 	}
162011c0d8fdSPaul Kocialkowski 
162111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_MIPI_BIT_SEL_REG, value);
162211c0d8fdSPaul Kocialkowski 	if (ret)
162311c0d8fdSPaul Kocialkowski 		return ret;
162411c0d8fdSPaul Kocialkowski 
162511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLA_REG,
162611c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLA_PRE_DIV_HALF(config->pll_pre_div_half));
162711c0d8fdSPaul Kocialkowski 	if (ret)
162811c0d8fdSPaul Kocialkowski 		return ret;
162911c0d8fdSPaul Kocialkowski 
163011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL0_REG,
163111c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL0_PRE_DIV(config->pll_pre_div));
163211c0d8fdSPaul Kocialkowski 	if (ret)
163311c0d8fdSPaul Kocialkowski 		return ret;
163411c0d8fdSPaul Kocialkowski 
163511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL1_REG,
163611c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL1_MUL_H(config->pll_mul));
163711c0d8fdSPaul Kocialkowski 	if (ret)
163811c0d8fdSPaul Kocialkowski 		return ret;
163911c0d8fdSPaul Kocialkowski 
164011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL2_REG,
164111c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL2_MUL_L(config->pll_mul));
164211c0d8fdSPaul Kocialkowski 	if (ret)
164311c0d8fdSPaul Kocialkowski 		return ret;
164411c0d8fdSPaul Kocialkowski 
164511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL3_REG,
164611c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL3_M_DIV(config->m_div));
164711c0d8fdSPaul Kocialkowski 	if (ret)
164811c0d8fdSPaul Kocialkowski 		return ret;
164911c0d8fdSPaul Kocialkowski 
165011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL4_REG,
165111c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL4_MIPI_DIV(config->mipi_div));
165211c0d8fdSPaul Kocialkowski 	if (ret)
165311c0d8fdSPaul Kocialkowski 		return ret;
165411c0d8fdSPaul Kocialkowski 
165511c0d8fdSPaul Kocialkowski 	ret = ov8865_update_bits(sensor, OV8865_PCLK_SEL_REG,
165611c0d8fdSPaul Kocialkowski 				 OV8865_PCLK_SEL_PCLK_DIV_MASK,
165711c0d8fdSPaul Kocialkowski 				 OV8865_PCLK_SEL_PCLK_DIV(config->pclk_div));
165811c0d8fdSPaul Kocialkowski 	if (ret)
165911c0d8fdSPaul Kocialkowski 		return ret;
166011c0d8fdSPaul Kocialkowski 
166111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL5_REG,
166211c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL5_SYS_PRE_DIV(config->sys_pre_div));
166311c0d8fdSPaul Kocialkowski 	if (ret)
166411c0d8fdSPaul Kocialkowski 		return ret;
166511c0d8fdSPaul Kocialkowski 
166611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL6_REG,
166711c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL6_SYS_DIV(config->sys_div));
166811c0d8fdSPaul Kocialkowski 	if (ret)
166911c0d8fdSPaul Kocialkowski 		return ret;
167011c0d8fdSPaul Kocialkowski 
167111c0d8fdSPaul Kocialkowski 	return ov8865_update_bits(sensor, OV8865_PLL_CTRL1E_REG,
167211c0d8fdSPaul Kocialkowski 				  OV8865_PLL_CTRL1E_PLL1_NO_LAT,
167311c0d8fdSPaul Kocialkowski 				  OV8865_PLL_CTRL1E_PLL1_NO_LAT);
167411c0d8fdSPaul Kocialkowski }
167511c0d8fdSPaul Kocialkowski 
ov8865_mode_pll2_configure(struct ov8865_sensor * sensor,const struct ov8865_mode * mode)167611c0d8fdSPaul Kocialkowski static int ov8865_mode_pll2_configure(struct ov8865_sensor *sensor,
167711c0d8fdSPaul Kocialkowski 				      const struct ov8865_mode *mode)
167811c0d8fdSPaul Kocialkowski {
167973dcffebSDaniel Scally 	const struct ov8865_pll2_config *config;
168011c0d8fdSPaul Kocialkowski 	int ret;
168111c0d8fdSPaul Kocialkowski 
168273dcffebSDaniel Scally 	config = mode->pll2_binning ? sensor->pll_configs->pll2_config_binning :
168373dcffebSDaniel Scally 				      sensor->pll_configs->pll2_config_native;
168473dcffebSDaniel Scally 
168511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRL12_REG,
168611c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL12_PRE_DIV_HALF(config->pll_pre_div_half) |
168711c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRL12_DAC_DIV(config->dac_div));
168811c0d8fdSPaul Kocialkowski 	if (ret)
168911c0d8fdSPaul Kocialkowski 		return ret;
169011c0d8fdSPaul Kocialkowski 
169111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLB_REG,
169211c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLB_PRE_DIV(config->pll_pre_div));
169311c0d8fdSPaul Kocialkowski 	if (ret)
169411c0d8fdSPaul Kocialkowski 		return ret;
169511c0d8fdSPaul Kocialkowski 
169611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLC_REG,
169711c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLC_MUL_H(config->pll_mul));
169811c0d8fdSPaul Kocialkowski 	if (ret)
169911c0d8fdSPaul Kocialkowski 		return ret;
170011c0d8fdSPaul Kocialkowski 
170111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLD_REG,
170211c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLD_MUL_L(config->pll_mul));
170311c0d8fdSPaul Kocialkowski 	if (ret)
170411c0d8fdSPaul Kocialkowski 		return ret;
170511c0d8fdSPaul Kocialkowski 
170611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_PLL_CTRLF_REG,
170711c0d8fdSPaul Kocialkowski 			   OV8865_PLL_CTRLF_SYS_PRE_DIV(config->sys_pre_div));
170811c0d8fdSPaul Kocialkowski 	if (ret)
170911c0d8fdSPaul Kocialkowski 		return ret;
171011c0d8fdSPaul Kocialkowski 
171111c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_PLL_CTRLE_REG,
171211c0d8fdSPaul Kocialkowski 			    OV8865_PLL_CTRLE_SYS_DIV(config->sys_div));
171311c0d8fdSPaul Kocialkowski }
171411c0d8fdSPaul Kocialkowski 
ov8865_mode_sclk_configure(struct ov8865_sensor * sensor,const struct ov8865_mode * mode)171511c0d8fdSPaul Kocialkowski static int ov8865_mode_sclk_configure(struct ov8865_sensor *sensor,
171611c0d8fdSPaul Kocialkowski 				      const struct ov8865_mode *mode)
171711c0d8fdSPaul Kocialkowski {
171873dcffebSDaniel Scally 	const struct ov8865_sclk_config *config = &ov8865_sclk_config_native;
171911c0d8fdSPaul Kocialkowski 	int ret;
172011c0d8fdSPaul Kocialkowski 
172111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_CLK_SEL0_REG,
172211c0d8fdSPaul Kocialkowski 			   OV8865_CLK_SEL0_PLL1_SYS_SEL(config->sys_sel));
172311c0d8fdSPaul Kocialkowski 	if (ret)
172411c0d8fdSPaul Kocialkowski 		return ret;
172511c0d8fdSPaul Kocialkowski 
172611c0d8fdSPaul Kocialkowski 	ret = ov8865_update_bits(sensor, OV8865_CLK_SEL1_REG,
172711c0d8fdSPaul Kocialkowski 				 OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK,
172811c0d8fdSPaul Kocialkowski 				 OV8865_CLK_SEL1_PLL_SCLK_SEL(config->sclk_sel));
172911c0d8fdSPaul Kocialkowski 	if (ret)
173011c0d8fdSPaul Kocialkowski 		return ret;
173111c0d8fdSPaul Kocialkowski 
173211c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_SCLK_CTRL_REG,
173311c0d8fdSPaul Kocialkowski 			    OV8865_SCLK_CTRL_UNKNOWN |
173411c0d8fdSPaul Kocialkowski 			    OV8865_SCLK_CTRL_SCLK_DIV(config->sclk_div) |
173511c0d8fdSPaul Kocialkowski 			    OV8865_SCLK_CTRL_SCLK_PRE_DIV(config->sclk_pre_div));
173611c0d8fdSPaul Kocialkowski }
173711c0d8fdSPaul Kocialkowski 
ov8865_mode_binning_configure(struct ov8865_sensor * sensor,const struct ov8865_mode * mode)173811c0d8fdSPaul Kocialkowski static int ov8865_mode_binning_configure(struct ov8865_sensor *sensor,
173911c0d8fdSPaul Kocialkowski 					 const struct ov8865_mode *mode)
174011c0d8fdSPaul Kocialkowski {
174111c0d8fdSPaul Kocialkowski 	unsigned int variopixel_hsub_coef, variopixel_vsub_coef;
174211c0d8fdSPaul Kocialkowski 	u8 value;
174311c0d8fdSPaul Kocialkowski 	int ret;
174411c0d8fdSPaul Kocialkowski 
174511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_FORMAT1_REG, 0);
174611c0d8fdSPaul Kocialkowski 	if (ret)
174711c0d8fdSPaul Kocialkowski 		return ret;
174811c0d8fdSPaul Kocialkowski 
174911c0d8fdSPaul Kocialkowski 	value = OV8865_FORMAT2_HSYNC_EN;
175011c0d8fdSPaul Kocialkowski 
175111c0d8fdSPaul Kocialkowski 	if (mode->binning_x)
175211c0d8fdSPaul Kocialkowski 		value |= OV8865_FORMAT2_FST_HBIN_EN;
175311c0d8fdSPaul Kocialkowski 
175411c0d8fdSPaul Kocialkowski 	if (mode->binning_y)
175511c0d8fdSPaul Kocialkowski 		value |= OV8865_FORMAT2_FST_VBIN_EN;
175611c0d8fdSPaul Kocialkowski 
175711c0d8fdSPaul Kocialkowski 	if (mode->sync_hbin)
175811c0d8fdSPaul Kocialkowski 		value |= OV8865_FORMAT2_SYNC_HBIN_EN;
175911c0d8fdSPaul Kocialkowski 
176011c0d8fdSPaul Kocialkowski 	if (mode->horz_var2)
176111c0d8fdSPaul Kocialkowski 		value |= OV8865_FORMAT2_ISP_HORZ_VAR2_EN;
176211c0d8fdSPaul Kocialkowski 
176311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_FORMAT2_REG, value);
176411c0d8fdSPaul Kocialkowski 	if (ret)
176511c0d8fdSPaul Kocialkowski 		return ret;
176611c0d8fdSPaul Kocialkowski 
176711c0d8fdSPaul Kocialkowski 	ret = ov8865_update_bits(sensor, OV8865_ISP_CTRL2_REG,
176811c0d8fdSPaul Kocialkowski 				 OV8865_ISP_CTRL2_VARIOPIXEL_EN,
176911c0d8fdSPaul Kocialkowski 				 mode->variopixel ?
177011c0d8fdSPaul Kocialkowski 				 OV8865_ISP_CTRL2_VARIOPIXEL_EN : 0);
177111c0d8fdSPaul Kocialkowski 	if (ret)
177211c0d8fdSPaul Kocialkowski 		return ret;
177311c0d8fdSPaul Kocialkowski 
177411c0d8fdSPaul Kocialkowski 	if (mode->variopixel) {
177511c0d8fdSPaul Kocialkowski 		/* VarioPixel coefs needs to be > 1. */
177611c0d8fdSPaul Kocialkowski 		variopixel_hsub_coef = mode->variopixel_hsub_coef;
177711c0d8fdSPaul Kocialkowski 		variopixel_vsub_coef = mode->variopixel_vsub_coef;
177811c0d8fdSPaul Kocialkowski 	} else {
177911c0d8fdSPaul Kocialkowski 		variopixel_hsub_coef = 1;
178011c0d8fdSPaul Kocialkowski 		variopixel_vsub_coef = 1;
178111c0d8fdSPaul Kocialkowski 	}
178211c0d8fdSPaul Kocialkowski 
178311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VAP_CTRL1_REG,
178411c0d8fdSPaul Kocialkowski 			   OV8865_VAP_CTRL1_HSUB_COEF(variopixel_hsub_coef) |
178511c0d8fdSPaul Kocialkowski 			   OV8865_VAP_CTRL1_VSUB_COEF(variopixel_vsub_coef));
178611c0d8fdSPaul Kocialkowski 	if (ret)
178711c0d8fdSPaul Kocialkowski 		return ret;
178811c0d8fdSPaul Kocialkowski 
178911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_INC_X_ODD_REG,
179011c0d8fdSPaul Kocialkowski 			   OV8865_INC_X_ODD(mode->inc_x_odd));
179111c0d8fdSPaul Kocialkowski 	if (ret)
179211c0d8fdSPaul Kocialkowski 		return ret;
179311c0d8fdSPaul Kocialkowski 
179411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_INC_X_EVEN_REG,
179511c0d8fdSPaul Kocialkowski 			   OV8865_INC_X_EVEN(mode->inc_x_even));
179611c0d8fdSPaul Kocialkowski 	if (ret)
179711c0d8fdSPaul Kocialkowski 		return ret;
179811c0d8fdSPaul Kocialkowski 
179911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_INC_Y_ODD_REG,
180011c0d8fdSPaul Kocialkowski 			   OV8865_INC_Y_ODD(mode->inc_y_odd));
180111c0d8fdSPaul Kocialkowski 	if (ret)
180211c0d8fdSPaul Kocialkowski 		return ret;
180311c0d8fdSPaul Kocialkowski 
180411c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_INC_Y_EVEN_REG,
180511c0d8fdSPaul Kocialkowski 			    OV8865_INC_Y_EVEN(mode->inc_y_even));
180611c0d8fdSPaul Kocialkowski }
180711c0d8fdSPaul Kocialkowski 
ov8865_mode_black_level_configure(struct ov8865_sensor * sensor,const struct ov8865_mode * mode)180811c0d8fdSPaul Kocialkowski static int ov8865_mode_black_level_configure(struct ov8865_sensor *sensor,
180911c0d8fdSPaul Kocialkowski 					     const struct ov8865_mode *mode)
181011c0d8fdSPaul Kocialkowski {
181111c0d8fdSPaul Kocialkowski 	int ret;
181211c0d8fdSPaul Kocialkowski 
181311c0d8fdSPaul Kocialkowski 	/* Note that a zero value for blc_col_shift_mask is the default 256. */
181411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_CTRL1_REG,
181511c0d8fdSPaul Kocialkowski 			   mode->blc_col_shift_mask |
181611c0d8fdSPaul Kocialkowski 			   OV8865_BLC_CTRL1_OFFSET_LIMIT_EN);
181711c0d8fdSPaul Kocialkowski 	if (ret)
181811c0d8fdSPaul Kocialkowski 		return ret;
181911c0d8fdSPaul Kocialkowski 
182011c0d8fdSPaul Kocialkowski 	/* BLC top zero line */
182111c0d8fdSPaul Kocialkowski 
182211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_START_REG,
182311c0d8fdSPaul Kocialkowski 			   OV8865_BLC_TOP_ZLINE_START(mode->blc_top_zero_line_start));
182411c0d8fdSPaul Kocialkowski 	if (ret)
182511c0d8fdSPaul Kocialkowski 		return ret;
182611c0d8fdSPaul Kocialkowski 
182711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_TOP_ZLINE_NUM_REG,
182811c0d8fdSPaul Kocialkowski 			   OV8865_BLC_TOP_ZLINE_NUM(mode->blc_top_zero_line_num));
182911c0d8fdSPaul Kocialkowski 	if (ret)
183011c0d8fdSPaul Kocialkowski 		return ret;
183111c0d8fdSPaul Kocialkowski 
183211c0d8fdSPaul Kocialkowski 	/* BLC top black line */
183311c0d8fdSPaul Kocialkowski 
183411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_START_REG,
183511c0d8fdSPaul Kocialkowski 			   OV8865_BLC_TOP_BLKLINE_START(mode->blc_top_black_line_start));
183611c0d8fdSPaul Kocialkowski 	if (ret)
183711c0d8fdSPaul Kocialkowski 		return ret;
183811c0d8fdSPaul Kocialkowski 
183911c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_TOP_BLKLINE_NUM_REG,
184011c0d8fdSPaul Kocialkowski 			   OV8865_BLC_TOP_BLKLINE_NUM(mode->blc_top_black_line_num));
184111c0d8fdSPaul Kocialkowski 	if (ret)
184211c0d8fdSPaul Kocialkowski 		return ret;
184311c0d8fdSPaul Kocialkowski 
184411c0d8fdSPaul Kocialkowski 	/* BLC bottom zero line */
184511c0d8fdSPaul Kocialkowski 
184611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_START_REG,
184711c0d8fdSPaul Kocialkowski 			   OV8865_BLC_BOT_ZLINE_START(mode->blc_bottom_zero_line_start));
184811c0d8fdSPaul Kocialkowski 	if (ret)
184911c0d8fdSPaul Kocialkowski 		return ret;
185011c0d8fdSPaul Kocialkowski 
185111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_BOT_ZLINE_NUM_REG,
185211c0d8fdSPaul Kocialkowski 			   OV8865_BLC_BOT_ZLINE_NUM(mode->blc_bottom_zero_line_num));
185311c0d8fdSPaul Kocialkowski 	if (ret)
185411c0d8fdSPaul Kocialkowski 		return ret;
185511c0d8fdSPaul Kocialkowski 
185611c0d8fdSPaul Kocialkowski 	/* BLC bottom black line */
185711c0d8fdSPaul Kocialkowski 
185811c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_START_REG,
185911c0d8fdSPaul Kocialkowski 			   OV8865_BLC_BOT_BLKLINE_START(mode->blc_bottom_black_line_start));
186011c0d8fdSPaul Kocialkowski 	if (ret)
186111c0d8fdSPaul Kocialkowski 		return ret;
186211c0d8fdSPaul Kocialkowski 
186311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_BOT_BLKLINE_NUM_REG,
186411c0d8fdSPaul Kocialkowski 			   OV8865_BLC_BOT_BLKLINE_NUM(mode->blc_bottom_black_line_num));
186511c0d8fdSPaul Kocialkowski 	if (ret)
186611c0d8fdSPaul Kocialkowski 		return ret;
186711c0d8fdSPaul Kocialkowski 
186811c0d8fdSPaul Kocialkowski 	/* BLC anchor */
186911c0d8fdSPaul Kocialkowski 
187011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_H_REG,
187111c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_LEFT_START_H(mode->blc_anchor_left_start));
187211c0d8fdSPaul Kocialkowski 	if (ret)
187311c0d8fdSPaul Kocialkowski 		return ret;
187411c0d8fdSPaul Kocialkowski 
187511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_START_L_REG,
187611c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_LEFT_START_L(mode->blc_anchor_left_start));
187711c0d8fdSPaul Kocialkowski 	if (ret)
187811c0d8fdSPaul Kocialkowski 		return ret;
187911c0d8fdSPaul Kocialkowski 
188011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_H_REG,
188111c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_LEFT_END_H(mode->blc_anchor_left_end));
188211c0d8fdSPaul Kocialkowski 	if (ret)
188311c0d8fdSPaul Kocialkowski 		return ret;
188411c0d8fdSPaul Kocialkowski 
188511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_LEFT_END_L_REG,
188611c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_LEFT_END_L(mode->blc_anchor_left_end));
188711c0d8fdSPaul Kocialkowski 	if (ret)
188811c0d8fdSPaul Kocialkowski 		return ret;
188911c0d8fdSPaul Kocialkowski 
189011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_H_REG,
189111c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_RIGHT_START_H(mode->blc_anchor_right_start));
189211c0d8fdSPaul Kocialkowski 	if (ret)
189311c0d8fdSPaul Kocialkowski 		return ret;
189411c0d8fdSPaul Kocialkowski 
189511c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_START_L_REG,
189611c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_RIGHT_START_L(mode->blc_anchor_right_start));
189711c0d8fdSPaul Kocialkowski 	if (ret)
189811c0d8fdSPaul Kocialkowski 		return ret;
189911c0d8fdSPaul Kocialkowski 
190011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_H_REG,
190111c0d8fdSPaul Kocialkowski 			   OV8865_BLC_ANCHOR_RIGHT_END_H(mode->blc_anchor_right_end));
190211c0d8fdSPaul Kocialkowski 	if (ret)
190311c0d8fdSPaul Kocialkowski 		return ret;
190411c0d8fdSPaul Kocialkowski 
190511c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_BLC_ANCHOR_RIGHT_END_L_REG,
190611c0d8fdSPaul Kocialkowski 			    OV8865_BLC_ANCHOR_RIGHT_END_L(mode->blc_anchor_right_end));
190711c0d8fdSPaul Kocialkowski }
190811c0d8fdSPaul Kocialkowski 
ov8865_mode_configure(struct ov8865_sensor * sensor,const struct ov8865_mode * mode,u32 mbus_code)190911c0d8fdSPaul Kocialkowski static int ov8865_mode_configure(struct ov8865_sensor *sensor,
191011c0d8fdSPaul Kocialkowski 				 const struct ov8865_mode *mode, u32 mbus_code)
191111c0d8fdSPaul Kocialkowski {
191211c0d8fdSPaul Kocialkowski 	int ret;
191311c0d8fdSPaul Kocialkowski 
191411c0d8fdSPaul Kocialkowski 	/* Output Size X */
191511c0d8fdSPaul Kocialkowski 
191611c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_H_REG,
191711c0d8fdSPaul Kocialkowski 			   OV8865_OUTPUT_SIZE_X_H(mode->output_size_x));
191811c0d8fdSPaul Kocialkowski 	if (ret)
191911c0d8fdSPaul Kocialkowski 		return ret;
192011c0d8fdSPaul Kocialkowski 
192111c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_X_L_REG,
192211c0d8fdSPaul Kocialkowski 			   OV8865_OUTPUT_SIZE_X_L(mode->output_size_x));
192311c0d8fdSPaul Kocialkowski 	if (ret)
192411c0d8fdSPaul Kocialkowski 		return ret;
192511c0d8fdSPaul Kocialkowski 
192611c0d8fdSPaul Kocialkowski 	/* Horizontal Total Size */
192711c0d8fdSPaul Kocialkowski 
192811c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_HTS_H_REG, OV8865_HTS_H(mode->hts));
192911c0d8fdSPaul Kocialkowski 	if (ret)
193011c0d8fdSPaul Kocialkowski 		return ret;
193111c0d8fdSPaul Kocialkowski 
193211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_HTS_L_REG, OV8865_HTS_L(mode->hts));
193311c0d8fdSPaul Kocialkowski 	if (ret)
193411c0d8fdSPaul Kocialkowski 		return ret;
193511c0d8fdSPaul Kocialkowski 
193611c0d8fdSPaul Kocialkowski 	/* Output Size Y */
193711c0d8fdSPaul Kocialkowski 
193811c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_H_REG,
193911c0d8fdSPaul Kocialkowski 			   OV8865_OUTPUT_SIZE_Y_H(mode->output_size_y));
194011c0d8fdSPaul Kocialkowski 	if (ret)
194111c0d8fdSPaul Kocialkowski 		return ret;
194211c0d8fdSPaul Kocialkowski 
194311c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_OUTPUT_SIZE_Y_L_REG,
194411c0d8fdSPaul Kocialkowski 			   OV8865_OUTPUT_SIZE_Y_L(mode->output_size_y));
194511c0d8fdSPaul Kocialkowski 	if (ret)
194611c0d8fdSPaul Kocialkowski 		return ret;
194711c0d8fdSPaul Kocialkowski 
194811c0d8fdSPaul Kocialkowski 	/* Vertical Total Size */
194911c0d8fdSPaul Kocialkowski 
195011c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(mode->vts));
195111c0d8fdSPaul Kocialkowski 	if (ret)
195211c0d8fdSPaul Kocialkowski 		return ret;
195311c0d8fdSPaul Kocialkowski 
195411c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(mode->vts));
195511c0d8fdSPaul Kocialkowski 	if (ret)
195611c0d8fdSPaul Kocialkowski 		return ret;
195711c0d8fdSPaul Kocialkowski 
195811c0d8fdSPaul Kocialkowski 	if (mode->size_auto) {
195911c0d8fdSPaul Kocialkowski 		/* Auto Size */
196011c0d8fdSPaul Kocialkowski 
196111c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_AUTO_SIZE_CTRL_REG,
196211c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG |
196311c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG |
196411c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG |
196511c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG |
196611c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG |
196711c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG);
196811c0d8fdSPaul Kocialkowski 		if (ret)
196911c0d8fdSPaul Kocialkowski 			return ret;
197011c0d8fdSPaul Kocialkowski 
197111c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_AUTO_SIZE_BOUNDARIES_REG,
197211c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_BOUNDARIES_Y(mode->size_auto_boundary_y) |
197311c0d8fdSPaul Kocialkowski 				   OV8865_AUTO_SIZE_BOUNDARIES_X(mode->size_auto_boundary_x));
197411c0d8fdSPaul Kocialkowski 		if (ret)
197511c0d8fdSPaul Kocialkowski 			return ret;
197611c0d8fdSPaul Kocialkowski 	} else {
197711c0d8fdSPaul Kocialkowski 		/* Crop Start X */
197811c0d8fdSPaul Kocialkowski 
197911c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_START_X_H_REG,
198011c0d8fdSPaul Kocialkowski 				   OV8865_CROP_START_X_H(mode->crop_start_x));
198111c0d8fdSPaul Kocialkowski 		if (ret)
198211c0d8fdSPaul Kocialkowski 			return ret;
198311c0d8fdSPaul Kocialkowski 
198411c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_START_X_L_REG,
198511c0d8fdSPaul Kocialkowski 				   OV8865_CROP_START_X_L(mode->crop_start_x));
198611c0d8fdSPaul Kocialkowski 		if (ret)
198711c0d8fdSPaul Kocialkowski 			return ret;
198811c0d8fdSPaul Kocialkowski 
198911c0d8fdSPaul Kocialkowski 		/* Offset X */
199011c0d8fdSPaul Kocialkowski 
199111c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_OFFSET_X_H_REG,
199211c0d8fdSPaul Kocialkowski 				   OV8865_OFFSET_X_H(mode->offset_x));
199311c0d8fdSPaul Kocialkowski 		if (ret)
199411c0d8fdSPaul Kocialkowski 			return ret;
199511c0d8fdSPaul Kocialkowski 
199611c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_OFFSET_X_L_REG,
199711c0d8fdSPaul Kocialkowski 				   OV8865_OFFSET_X_L(mode->offset_x));
199811c0d8fdSPaul Kocialkowski 		if (ret)
199911c0d8fdSPaul Kocialkowski 			return ret;
200011c0d8fdSPaul Kocialkowski 
200111c0d8fdSPaul Kocialkowski 		/* Crop End X */
200211c0d8fdSPaul Kocialkowski 
200311c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_END_X_H_REG,
200411c0d8fdSPaul Kocialkowski 				   OV8865_CROP_END_X_H(mode->crop_end_x));
200511c0d8fdSPaul Kocialkowski 		if (ret)
200611c0d8fdSPaul Kocialkowski 			return ret;
200711c0d8fdSPaul Kocialkowski 
200811c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_END_X_L_REG,
200911c0d8fdSPaul Kocialkowski 				   OV8865_CROP_END_X_L(mode->crop_end_x));
201011c0d8fdSPaul Kocialkowski 		if (ret)
201111c0d8fdSPaul Kocialkowski 			return ret;
201211c0d8fdSPaul Kocialkowski 
201311c0d8fdSPaul Kocialkowski 		/* Crop Start Y */
201411c0d8fdSPaul Kocialkowski 
201511c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_START_Y_H_REG,
201611c0d8fdSPaul Kocialkowski 				   OV8865_CROP_START_Y_H(mode->crop_start_y));
201711c0d8fdSPaul Kocialkowski 		if (ret)
201811c0d8fdSPaul Kocialkowski 			return ret;
201911c0d8fdSPaul Kocialkowski 
202011c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_START_Y_L_REG,
202111c0d8fdSPaul Kocialkowski 				   OV8865_CROP_START_Y_L(mode->crop_start_y));
202211c0d8fdSPaul Kocialkowski 		if (ret)
202311c0d8fdSPaul Kocialkowski 			return ret;
202411c0d8fdSPaul Kocialkowski 
202511c0d8fdSPaul Kocialkowski 		/* Offset Y */
202611c0d8fdSPaul Kocialkowski 
202711c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_OFFSET_Y_H_REG,
202811c0d8fdSPaul Kocialkowski 				   OV8865_OFFSET_Y_H(mode->offset_y));
202911c0d8fdSPaul Kocialkowski 		if (ret)
203011c0d8fdSPaul Kocialkowski 			return ret;
203111c0d8fdSPaul Kocialkowski 
203211c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_OFFSET_Y_L_REG,
203311c0d8fdSPaul Kocialkowski 				   OV8865_OFFSET_Y_L(mode->offset_y));
203411c0d8fdSPaul Kocialkowski 		if (ret)
203511c0d8fdSPaul Kocialkowski 			return ret;
203611c0d8fdSPaul Kocialkowski 
203711c0d8fdSPaul Kocialkowski 		/* Crop End Y */
203811c0d8fdSPaul Kocialkowski 
203911c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_END_Y_H_REG,
204011c0d8fdSPaul Kocialkowski 				   OV8865_CROP_END_Y_H(mode->crop_end_y));
204111c0d8fdSPaul Kocialkowski 		if (ret)
204211c0d8fdSPaul Kocialkowski 			return ret;
204311c0d8fdSPaul Kocialkowski 
204411c0d8fdSPaul Kocialkowski 		ret = ov8865_write(sensor, OV8865_CROP_END_Y_L_REG,
204511c0d8fdSPaul Kocialkowski 				   OV8865_CROP_END_Y_L(mode->crop_end_y));
204611c0d8fdSPaul Kocialkowski 		if (ret)
204711c0d8fdSPaul Kocialkowski 			return ret;
204811c0d8fdSPaul Kocialkowski 	}
204911c0d8fdSPaul Kocialkowski 
205011c0d8fdSPaul Kocialkowski 	/* VFIFO */
205111c0d8fdSPaul Kocialkowski 
205211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_H_REG,
205311c0d8fdSPaul Kocialkowski 			   OV8865_VFIFO_READ_START_H(mode->vfifo_read_start));
205411c0d8fdSPaul Kocialkowski 	if (ret)
205511c0d8fdSPaul Kocialkowski 		return ret;
205611c0d8fdSPaul Kocialkowski 
205711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_VFIFO_READ_START_L_REG,
205811c0d8fdSPaul Kocialkowski 			   OV8865_VFIFO_READ_START_L(mode->vfifo_read_start));
205911c0d8fdSPaul Kocialkowski 	if (ret)
206011c0d8fdSPaul Kocialkowski 		return ret;
206111c0d8fdSPaul Kocialkowski 
206211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ABLC_NUM_REG,
206311c0d8fdSPaul Kocialkowski 			   OV8865_ABLC_NUM(mode->ablc_num));
206411c0d8fdSPaul Kocialkowski 	if (ret)
206511c0d8fdSPaul Kocialkowski 		return ret;
206611c0d8fdSPaul Kocialkowski 
206711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ZLINE_NUM_REG,
206811c0d8fdSPaul Kocialkowski 			   OV8865_ZLINE_NUM(mode->zline_num));
206911c0d8fdSPaul Kocialkowski 	if (ret)
207011c0d8fdSPaul Kocialkowski 		return ret;
207111c0d8fdSPaul Kocialkowski 
207211c0d8fdSPaul Kocialkowski 	/* Binning */
207311c0d8fdSPaul Kocialkowski 
207411c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_binning_configure(sensor, mode);
207511c0d8fdSPaul Kocialkowski 	if (ret)
207611c0d8fdSPaul Kocialkowski 		return ret;
207711c0d8fdSPaul Kocialkowski 
207811c0d8fdSPaul Kocialkowski 	/* Black Level */
207911c0d8fdSPaul Kocialkowski 
208011c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_black_level_configure(sensor, mode);
208111c0d8fdSPaul Kocialkowski 	if (ret)
208211c0d8fdSPaul Kocialkowski 		return ret;
208311c0d8fdSPaul Kocialkowski 
208411c0d8fdSPaul Kocialkowski 	/* PLLs */
208511c0d8fdSPaul Kocialkowski 
208611c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_pll1_configure(sensor, mode, mbus_code);
208711c0d8fdSPaul Kocialkowski 	if (ret)
208811c0d8fdSPaul Kocialkowski 		return ret;
208911c0d8fdSPaul Kocialkowski 
209011c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_pll2_configure(sensor, mode);
209111c0d8fdSPaul Kocialkowski 	if (ret)
209211c0d8fdSPaul Kocialkowski 		return ret;
209311c0d8fdSPaul Kocialkowski 
209411c0d8fdSPaul Kocialkowski 	ret = ov8865_mode_sclk_configure(sensor, mode);
209511c0d8fdSPaul Kocialkowski 	if (ret)
209611c0d8fdSPaul Kocialkowski 		return ret;
209711c0d8fdSPaul Kocialkowski 
209811c0d8fdSPaul Kocialkowski 	/* Extra registers */
209911c0d8fdSPaul Kocialkowski 
210011c0d8fdSPaul Kocialkowski 	if (mode->register_values) {
210111c0d8fdSPaul Kocialkowski 		ret = ov8865_write_sequence(sensor, mode->register_values,
210211c0d8fdSPaul Kocialkowski 					    mode->register_values_count);
210311c0d8fdSPaul Kocialkowski 		if (ret)
210411c0d8fdSPaul Kocialkowski 			return ret;
210511c0d8fdSPaul Kocialkowski 	}
210611c0d8fdSPaul Kocialkowski 
210711c0d8fdSPaul Kocialkowski 	return 0;
210811c0d8fdSPaul Kocialkowski }
210911c0d8fdSPaul Kocialkowski 
ov8865_mode_mipi_clk_rate(struct ov8865_sensor * sensor,const struct ov8865_mode * mode)211011c0d8fdSPaul Kocialkowski static unsigned long ov8865_mode_mipi_clk_rate(struct ov8865_sensor *sensor,
211111c0d8fdSPaul Kocialkowski 					       const struct ov8865_mode *mode)
211211c0d8fdSPaul Kocialkowski {
211373dcffebSDaniel Scally 	const struct ov8865_pll1_config *config;
211411c0d8fdSPaul Kocialkowski 	unsigned long pll1_rate;
211511c0d8fdSPaul Kocialkowski 
211673dcffebSDaniel Scally 	config = sensor->pll_configs->pll1_config;
211773dcffebSDaniel Scally 
211811c0d8fdSPaul Kocialkowski 	pll1_rate = ov8865_mode_pll1_rate(sensor, mode);
211911c0d8fdSPaul Kocialkowski 
212011c0d8fdSPaul Kocialkowski 	return pll1_rate / config->m_div / 2;
212111c0d8fdSPaul Kocialkowski }
212211c0d8fdSPaul Kocialkowski 
212311c0d8fdSPaul Kocialkowski /* Exposure */
212411c0d8fdSPaul Kocialkowski 
ov8865_exposure_configure(struct ov8865_sensor * sensor,u32 exposure)212511c0d8fdSPaul Kocialkowski static int ov8865_exposure_configure(struct ov8865_sensor *sensor, u32 exposure)
212611c0d8fdSPaul Kocialkowski {
212711c0d8fdSPaul Kocialkowski 	int ret;
212811c0d8fdSPaul Kocialkowski 
2129e15ddc96SDaniel Scally 	/* The sensor stores exposure in units of 1/16th of a line */
2130e15ddc96SDaniel Scally 	exposure *= 16;
2131e15ddc96SDaniel Scally 
213211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_HH_REG,
213311c0d8fdSPaul Kocialkowski 			   OV8865_EXPOSURE_CTRL_HH(exposure));
213411c0d8fdSPaul Kocialkowski 	if (ret)
213511c0d8fdSPaul Kocialkowski 		return ret;
213611c0d8fdSPaul Kocialkowski 
213711c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_EXPOSURE_CTRL_H_REG,
213811c0d8fdSPaul Kocialkowski 			   OV8865_EXPOSURE_CTRL_H(exposure));
213911c0d8fdSPaul Kocialkowski 	if (ret)
214011c0d8fdSPaul Kocialkowski 		return ret;
214111c0d8fdSPaul Kocialkowski 
214211c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_EXPOSURE_CTRL_L_REG,
214311c0d8fdSPaul Kocialkowski 			    OV8865_EXPOSURE_CTRL_L(exposure));
214411c0d8fdSPaul Kocialkowski }
214511c0d8fdSPaul Kocialkowski 
214611c0d8fdSPaul Kocialkowski /* Gain */
214711c0d8fdSPaul Kocialkowski 
ov8865_analog_gain_configure(struct ov8865_sensor * sensor,u32 gain)2148d938b2f2SDaniel Scally static int ov8865_analog_gain_configure(struct ov8865_sensor *sensor, u32 gain)
214911c0d8fdSPaul Kocialkowski {
215011c0d8fdSPaul Kocialkowski 	int ret;
215111c0d8fdSPaul Kocialkowski 
215211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_GAIN_CTRL_H_REG,
215311c0d8fdSPaul Kocialkowski 			   OV8865_GAIN_CTRL_H(gain));
215411c0d8fdSPaul Kocialkowski 	if (ret)
215511c0d8fdSPaul Kocialkowski 		return ret;
215611c0d8fdSPaul Kocialkowski 
215711c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_GAIN_CTRL_L_REG,
215811c0d8fdSPaul Kocialkowski 			    OV8865_GAIN_CTRL_L(gain));
215911c0d8fdSPaul Kocialkowski }
216011c0d8fdSPaul Kocialkowski 
216111c0d8fdSPaul Kocialkowski /* White Balance */
216211c0d8fdSPaul Kocialkowski 
ov8865_red_balance_configure(struct ov8865_sensor * sensor,u32 red_balance)216311c0d8fdSPaul Kocialkowski static int ov8865_red_balance_configure(struct ov8865_sensor *sensor,
216411c0d8fdSPaul Kocialkowski 					u32 red_balance)
216511c0d8fdSPaul Kocialkowski {
216611c0d8fdSPaul Kocialkowski 	int ret;
216711c0d8fdSPaul Kocialkowski 
216811c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ISP_GAIN_RED_H_REG,
216911c0d8fdSPaul Kocialkowski 			   OV8865_ISP_GAIN_RED_H(red_balance));
217011c0d8fdSPaul Kocialkowski 	if (ret)
217111c0d8fdSPaul Kocialkowski 		return ret;
217211c0d8fdSPaul Kocialkowski 
217311c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_ISP_GAIN_RED_L_REG,
217411c0d8fdSPaul Kocialkowski 			    OV8865_ISP_GAIN_RED_L(red_balance));
217511c0d8fdSPaul Kocialkowski }
217611c0d8fdSPaul Kocialkowski 
ov8865_blue_balance_configure(struct ov8865_sensor * sensor,u32 blue_balance)217711c0d8fdSPaul Kocialkowski static int ov8865_blue_balance_configure(struct ov8865_sensor *sensor,
217811c0d8fdSPaul Kocialkowski 					 u32 blue_balance)
217911c0d8fdSPaul Kocialkowski {
218011c0d8fdSPaul Kocialkowski 	int ret;
218111c0d8fdSPaul Kocialkowski 
218211c0d8fdSPaul Kocialkowski 	ret = ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_H_REG,
218311c0d8fdSPaul Kocialkowski 			   OV8865_ISP_GAIN_BLUE_H(blue_balance));
218411c0d8fdSPaul Kocialkowski 	if (ret)
218511c0d8fdSPaul Kocialkowski 		return ret;
218611c0d8fdSPaul Kocialkowski 
218711c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_ISP_GAIN_BLUE_L_REG,
218811c0d8fdSPaul Kocialkowski 			    OV8865_ISP_GAIN_BLUE_L(blue_balance));
218911c0d8fdSPaul Kocialkowski }
219011c0d8fdSPaul Kocialkowski 
219111c0d8fdSPaul Kocialkowski /* Flip */
219211c0d8fdSPaul Kocialkowski 
ov8865_flip_vert_configure(struct ov8865_sensor * sensor,bool enable)219311c0d8fdSPaul Kocialkowski static int ov8865_flip_vert_configure(struct ov8865_sensor *sensor, bool enable)
219411c0d8fdSPaul Kocialkowski {
219511c0d8fdSPaul Kocialkowski 	u8 bits = OV8865_FORMAT1_FLIP_VERT_ISP_EN |
219611c0d8fdSPaul Kocialkowski 		  OV8865_FORMAT1_FLIP_VERT_SENSOR_EN;
219711c0d8fdSPaul Kocialkowski 
219811c0d8fdSPaul Kocialkowski 	return ov8865_update_bits(sensor, OV8865_FORMAT1_REG, bits,
219911c0d8fdSPaul Kocialkowski 				  enable ? bits : 0);
220011c0d8fdSPaul Kocialkowski }
220111c0d8fdSPaul Kocialkowski 
ov8865_flip_horz_configure(struct ov8865_sensor * sensor,bool enable)220211c0d8fdSPaul Kocialkowski static int ov8865_flip_horz_configure(struct ov8865_sensor *sensor, bool enable)
220311c0d8fdSPaul Kocialkowski {
220411c0d8fdSPaul Kocialkowski 	u8 bits = OV8865_FORMAT2_FLIP_HORZ_ISP_EN |
220511c0d8fdSPaul Kocialkowski 		  OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN;
220611c0d8fdSPaul Kocialkowski 
220711c0d8fdSPaul Kocialkowski 	return ov8865_update_bits(sensor, OV8865_FORMAT2_REG, bits,
220811c0d8fdSPaul Kocialkowski 				  enable ? bits : 0);
220911c0d8fdSPaul Kocialkowski }
221011c0d8fdSPaul Kocialkowski 
221111c0d8fdSPaul Kocialkowski /* Test Pattern */
221211c0d8fdSPaul Kocialkowski 
ov8865_test_pattern_configure(struct ov8865_sensor * sensor,unsigned int index)221311c0d8fdSPaul Kocialkowski static int ov8865_test_pattern_configure(struct ov8865_sensor *sensor,
221411c0d8fdSPaul Kocialkowski 					 unsigned int index)
221511c0d8fdSPaul Kocialkowski {
221611c0d8fdSPaul Kocialkowski 	if (index >= ARRAY_SIZE(ov8865_test_pattern_bits))
221711c0d8fdSPaul Kocialkowski 		return -EINVAL;
221811c0d8fdSPaul Kocialkowski 
221911c0d8fdSPaul Kocialkowski 	return ov8865_write(sensor, OV8865_PRE_CTRL0_REG,
222011c0d8fdSPaul Kocialkowski 			    ov8865_test_pattern_bits[index]);
222111c0d8fdSPaul Kocialkowski }
222211c0d8fdSPaul Kocialkowski 
22239293aafeSDaniel Scally /* Blanking */
22249293aafeSDaniel Scally 
ov8865_vts_configure(struct ov8865_sensor * sensor,u32 vblank)22259293aafeSDaniel Scally static int ov8865_vts_configure(struct ov8865_sensor *sensor, u32 vblank)
22269293aafeSDaniel Scally {
22279293aafeSDaniel Scally 	u16 vts = sensor->state.mode->output_size_y + vblank;
22289293aafeSDaniel Scally 	int ret;
22299293aafeSDaniel Scally 
22309293aafeSDaniel Scally 	ret = ov8865_write(sensor, OV8865_VTS_H_REG, OV8865_VTS_H(vts));
22319293aafeSDaniel Scally 	if (ret)
22329293aafeSDaniel Scally 		return ret;
22339293aafeSDaniel Scally 
22349293aafeSDaniel Scally 	return ov8865_write(sensor, OV8865_VTS_L_REG, OV8865_VTS_L(vts));
22359293aafeSDaniel Scally }
22369293aafeSDaniel Scally 
223711c0d8fdSPaul Kocialkowski /* State */
223811c0d8fdSPaul Kocialkowski 
ov8865_state_mipi_configure(struct ov8865_sensor * sensor,const struct ov8865_mode * mode,u32 mbus_code)223911c0d8fdSPaul Kocialkowski static int ov8865_state_mipi_configure(struct ov8865_sensor *sensor,
224011c0d8fdSPaul Kocialkowski 				       const struct ov8865_mode *mode,
224111c0d8fdSPaul Kocialkowski 				       u32 mbus_code)
224211c0d8fdSPaul Kocialkowski {
224311c0d8fdSPaul Kocialkowski 	struct ov8865_ctrls *ctrls = &sensor->ctrls;
224494d964e5SLaurent Pinchart 	struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 =
224511c0d8fdSPaul Kocialkowski 		&sensor->endpoint.bus.mipi_csi2;
224611c0d8fdSPaul Kocialkowski 	unsigned long mipi_clk_rate;
224711c0d8fdSPaul Kocialkowski 	unsigned int bits_per_sample;
224811c0d8fdSPaul Kocialkowski 	unsigned int lanes_count;
224911c0d8fdSPaul Kocialkowski 	unsigned int i, j;
225011c0d8fdSPaul Kocialkowski 	s64 mipi_pixel_rate;
225111c0d8fdSPaul Kocialkowski 
225211c0d8fdSPaul Kocialkowski 	mipi_clk_rate = ov8865_mode_mipi_clk_rate(sensor, mode);
225311c0d8fdSPaul Kocialkowski 	if (!mipi_clk_rate)
225411c0d8fdSPaul Kocialkowski 		return -EINVAL;
225511c0d8fdSPaul Kocialkowski 
225611c0d8fdSPaul Kocialkowski 	for (i = 0; i < ARRAY_SIZE(ov8865_link_freq_menu); i++) {
225711c0d8fdSPaul Kocialkowski 		s64 freq = ov8865_link_freq_menu[i];
225811c0d8fdSPaul Kocialkowski 
225911c0d8fdSPaul Kocialkowski 		if (freq == mipi_clk_rate)
226011c0d8fdSPaul Kocialkowski 			break;
226111c0d8fdSPaul Kocialkowski 	}
226211c0d8fdSPaul Kocialkowski 
226311c0d8fdSPaul Kocialkowski 	for (j = 0; j < sensor->endpoint.nr_of_link_frequencies; j++) {
226411c0d8fdSPaul Kocialkowski 		u64 freq = sensor->endpoint.link_frequencies[j];
226511c0d8fdSPaul Kocialkowski 
226611c0d8fdSPaul Kocialkowski 		if (freq == mipi_clk_rate)
226711c0d8fdSPaul Kocialkowski 			break;
226811c0d8fdSPaul Kocialkowski 	}
226911c0d8fdSPaul Kocialkowski 
227011c0d8fdSPaul Kocialkowski 	if (i == ARRAY_SIZE(ov8865_link_freq_menu)) {
227111c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev,
227211c0d8fdSPaul Kocialkowski 			"failed to find %lu clk rate in link freq\n",
227311c0d8fdSPaul Kocialkowski 			mipi_clk_rate);
227411c0d8fdSPaul Kocialkowski 	} else if (j == sensor->endpoint.nr_of_link_frequencies) {
227511c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev,
227611c0d8fdSPaul Kocialkowski 			"failed to find %lu clk rate in endpoint link-frequencies\n",
227711c0d8fdSPaul Kocialkowski 			mipi_clk_rate);
227811c0d8fdSPaul Kocialkowski 	} else {
227911c0d8fdSPaul Kocialkowski 		__v4l2_ctrl_s_ctrl(ctrls->link_freq, i);
228011c0d8fdSPaul Kocialkowski 	}
228111c0d8fdSPaul Kocialkowski 
228211c0d8fdSPaul Kocialkowski 	switch (mbus_code) {
228311c0d8fdSPaul Kocialkowski 	case MEDIA_BUS_FMT_SBGGR10_1X10:
228411c0d8fdSPaul Kocialkowski 		bits_per_sample = 10;
228511c0d8fdSPaul Kocialkowski 		break;
228611c0d8fdSPaul Kocialkowski 	default:
228711c0d8fdSPaul Kocialkowski 		return -EINVAL;
228811c0d8fdSPaul Kocialkowski 	}
228911c0d8fdSPaul Kocialkowski 
229011c0d8fdSPaul Kocialkowski 	lanes_count = bus_mipi_csi2->num_data_lanes;
229111c0d8fdSPaul Kocialkowski 	mipi_pixel_rate = mipi_clk_rate * 2 * lanes_count / bits_per_sample;
229211c0d8fdSPaul Kocialkowski 
229311c0d8fdSPaul Kocialkowski 	__v4l2_ctrl_s_ctrl_int64(ctrls->pixel_rate, mipi_pixel_rate);
229411c0d8fdSPaul Kocialkowski 
229511c0d8fdSPaul Kocialkowski 	return 0;
229611c0d8fdSPaul Kocialkowski }
229711c0d8fdSPaul Kocialkowski 
ov8865_state_configure(struct ov8865_sensor * sensor,const struct ov8865_mode * mode,u32 mbus_code)229811c0d8fdSPaul Kocialkowski static int ov8865_state_configure(struct ov8865_sensor *sensor,
229911c0d8fdSPaul Kocialkowski 				  const struct ov8865_mode *mode,
230011c0d8fdSPaul Kocialkowski 				  u32 mbus_code)
230111c0d8fdSPaul Kocialkowski {
230211c0d8fdSPaul Kocialkowski 	int ret;
230311c0d8fdSPaul Kocialkowski 
230411c0d8fdSPaul Kocialkowski 	if (sensor->state.streaming)
230511c0d8fdSPaul Kocialkowski 		return -EBUSY;
230611c0d8fdSPaul Kocialkowski 
230711c0d8fdSPaul Kocialkowski 	/* State will be configured at first power on otherwise. */
230811c0d8fdSPaul Kocialkowski 	if (pm_runtime_enabled(sensor->dev) &&
230911c0d8fdSPaul Kocialkowski 	    !pm_runtime_suspended(sensor->dev)) {
231011c0d8fdSPaul Kocialkowski 		ret = ov8865_mode_configure(sensor, mode, mbus_code);
231111c0d8fdSPaul Kocialkowski 		if (ret)
231211c0d8fdSPaul Kocialkowski 			return ret;
231311c0d8fdSPaul Kocialkowski 	}
231411c0d8fdSPaul Kocialkowski 
231511c0d8fdSPaul Kocialkowski 	ret = ov8865_state_mipi_configure(sensor, mode, mbus_code);
231611c0d8fdSPaul Kocialkowski 	if (ret)
231711c0d8fdSPaul Kocialkowski 		return ret;
231811c0d8fdSPaul Kocialkowski 
231911c0d8fdSPaul Kocialkowski 	sensor->state.mode = mode;
232011c0d8fdSPaul Kocialkowski 	sensor->state.mbus_code = mbus_code;
232111c0d8fdSPaul Kocialkowski 
232211c0d8fdSPaul Kocialkowski 	return 0;
232311c0d8fdSPaul Kocialkowski }
232411c0d8fdSPaul Kocialkowski 
ov8865_state_init(struct ov8865_sensor * sensor)232511c0d8fdSPaul Kocialkowski static int ov8865_state_init(struct ov8865_sensor *sensor)
232611c0d8fdSPaul Kocialkowski {
232711c0d8fdSPaul Kocialkowski 	return ov8865_state_configure(sensor, &ov8865_modes[0],
232811c0d8fdSPaul Kocialkowski 				      ov8865_mbus_codes[0]);
232911c0d8fdSPaul Kocialkowski }
233011c0d8fdSPaul Kocialkowski 
233111c0d8fdSPaul Kocialkowski /* Sensor Base */
233211c0d8fdSPaul Kocialkowski 
ov8865_sensor_init(struct ov8865_sensor * sensor)233311c0d8fdSPaul Kocialkowski static int ov8865_sensor_init(struct ov8865_sensor *sensor)
233411c0d8fdSPaul Kocialkowski {
233511c0d8fdSPaul Kocialkowski 	int ret;
233611c0d8fdSPaul Kocialkowski 
233711c0d8fdSPaul Kocialkowski 	ret = ov8865_sw_reset(sensor);
233811c0d8fdSPaul Kocialkowski 	if (ret) {
233911c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to perform sw reset\n");
234011c0d8fdSPaul Kocialkowski 		return ret;
234111c0d8fdSPaul Kocialkowski 	}
234211c0d8fdSPaul Kocialkowski 
234311c0d8fdSPaul Kocialkowski 	ret = ov8865_sw_standby(sensor, 1);
234411c0d8fdSPaul Kocialkowski 	if (ret) {
234511c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to set sensor standby\n");
234611c0d8fdSPaul Kocialkowski 		return ret;
234711c0d8fdSPaul Kocialkowski 	}
234811c0d8fdSPaul Kocialkowski 
234911c0d8fdSPaul Kocialkowski 	ret = ov8865_chip_id_check(sensor);
235011c0d8fdSPaul Kocialkowski 	if (ret) {
235111c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to check sensor chip id\n");
235211c0d8fdSPaul Kocialkowski 		return ret;
235311c0d8fdSPaul Kocialkowski 	}
235411c0d8fdSPaul Kocialkowski 
235511c0d8fdSPaul Kocialkowski 	ret = ov8865_write_sequence(sensor, ov8865_init_sequence,
235611c0d8fdSPaul Kocialkowski 				    ARRAY_SIZE(ov8865_init_sequence));
235711c0d8fdSPaul Kocialkowski 	if (ret) {
235811c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to write init sequence\n");
235911c0d8fdSPaul Kocialkowski 		return ret;
236011c0d8fdSPaul Kocialkowski 	}
236111c0d8fdSPaul Kocialkowski 
236211c0d8fdSPaul Kocialkowski 	ret = ov8865_charge_pump_configure(sensor);
236311c0d8fdSPaul Kocialkowski 	if (ret) {
236411c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure pad\n");
236511c0d8fdSPaul Kocialkowski 		return ret;
236611c0d8fdSPaul Kocialkowski 	}
236711c0d8fdSPaul Kocialkowski 
236811c0d8fdSPaul Kocialkowski 	ret = ov8865_mipi_configure(sensor);
236911c0d8fdSPaul Kocialkowski 	if (ret) {
237011c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure MIPI\n");
237111c0d8fdSPaul Kocialkowski 		return ret;
237211c0d8fdSPaul Kocialkowski 	}
237311c0d8fdSPaul Kocialkowski 
237411c0d8fdSPaul Kocialkowski 	ret = ov8865_isp_configure(sensor);
237511c0d8fdSPaul Kocialkowski 	if (ret) {
237611c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure ISP\n");
237711c0d8fdSPaul Kocialkowski 		return ret;
237811c0d8fdSPaul Kocialkowski 	}
237911c0d8fdSPaul Kocialkowski 
238011c0d8fdSPaul Kocialkowski 	ret = ov8865_black_level_configure(sensor);
238111c0d8fdSPaul Kocialkowski 	if (ret) {
238211c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure black level\n");
238311c0d8fdSPaul Kocialkowski 		return ret;
238411c0d8fdSPaul Kocialkowski 	}
238511c0d8fdSPaul Kocialkowski 
238611c0d8fdSPaul Kocialkowski 	/* Configure current mode. */
238711c0d8fdSPaul Kocialkowski 	ret = ov8865_state_configure(sensor, sensor->state.mode,
238811c0d8fdSPaul Kocialkowski 				     sensor->state.mbus_code);
238911c0d8fdSPaul Kocialkowski 	if (ret) {
239011c0d8fdSPaul Kocialkowski 		dev_err(sensor->dev, "failed to configure state\n");
239111c0d8fdSPaul Kocialkowski 		return ret;
239211c0d8fdSPaul Kocialkowski 	}
239311c0d8fdSPaul Kocialkowski 
239411c0d8fdSPaul Kocialkowski 	return 0;
239511c0d8fdSPaul Kocialkowski }
239611c0d8fdSPaul Kocialkowski 
ov8865_sensor_power(struct ov8865_sensor * sensor,bool on)239711c0d8fdSPaul Kocialkowski static int ov8865_sensor_power(struct ov8865_sensor *sensor, bool on)
239811c0d8fdSPaul Kocialkowski {
239911c0d8fdSPaul Kocialkowski 	/* Keep initialized to zero for disable label. */
240011c0d8fdSPaul Kocialkowski 	int ret = 0;
240111c0d8fdSPaul Kocialkowski 
240211c0d8fdSPaul Kocialkowski 	if (on) {
240311c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->reset, 1);
240411c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->powerdown, 1);
240511c0d8fdSPaul Kocialkowski 
240611c0d8fdSPaul Kocialkowski 		ret = regulator_enable(sensor->dovdd);
240711c0d8fdSPaul Kocialkowski 		if (ret) {
240811c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev,
240911c0d8fdSPaul Kocialkowski 				"failed to enable DOVDD regulator\n");
2410cbe0b3afSSakari Ailus 			return ret;
241111c0d8fdSPaul Kocialkowski 		}
241211c0d8fdSPaul Kocialkowski 
241311c0d8fdSPaul Kocialkowski 		ret = regulator_enable(sensor->avdd);
241411c0d8fdSPaul Kocialkowski 		if (ret) {
241511c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev,
241611c0d8fdSPaul Kocialkowski 				"failed to enable AVDD regulator\n");
2417cbe0b3afSSakari Ailus 			goto disable_dovdd;
241811c0d8fdSPaul Kocialkowski 		}
241911c0d8fdSPaul Kocialkowski 
242011c0d8fdSPaul Kocialkowski 		ret = regulator_enable(sensor->dvdd);
242111c0d8fdSPaul Kocialkowski 		if (ret) {
242211c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev,
242311c0d8fdSPaul Kocialkowski 				"failed to enable DVDD regulator\n");
2424cbe0b3afSSakari Ailus 			goto disable_avdd;
242511c0d8fdSPaul Kocialkowski 		}
242611c0d8fdSPaul Kocialkowski 
242711c0d8fdSPaul Kocialkowski 		ret = clk_prepare_enable(sensor->extclk);
242811c0d8fdSPaul Kocialkowski 		if (ret) {
242911c0d8fdSPaul Kocialkowski 			dev_err(sensor->dev, "failed to enable EXTCLK clock\n");
2430cbe0b3afSSakari Ailus 			goto disable_dvdd;
243111c0d8fdSPaul Kocialkowski 		}
243211c0d8fdSPaul Kocialkowski 
243311c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->reset, 0);
243411c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->powerdown, 0);
243511c0d8fdSPaul Kocialkowski 
243611c0d8fdSPaul Kocialkowski 		/* Time to enter streaming mode according to power timings. */
243711c0d8fdSPaul Kocialkowski 		usleep_range(10000, 12000);
243811c0d8fdSPaul Kocialkowski 	} else {
243911c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->powerdown, 1);
244011c0d8fdSPaul Kocialkowski 		gpiod_set_value_cansleep(sensor->reset, 1);
244111c0d8fdSPaul Kocialkowski 
244211c0d8fdSPaul Kocialkowski 		clk_disable_unprepare(sensor->extclk);
244311c0d8fdSPaul Kocialkowski 
2444cbe0b3afSSakari Ailus disable_dvdd:
244511c0d8fdSPaul Kocialkowski 		regulator_disable(sensor->dvdd);
2446cbe0b3afSSakari Ailus disable_avdd:
244711c0d8fdSPaul Kocialkowski 		regulator_disable(sensor->avdd);
2448cbe0b3afSSakari Ailus disable_dovdd:
244911c0d8fdSPaul Kocialkowski 		regulator_disable(sensor->dovdd);
245011c0d8fdSPaul Kocialkowski 	}
245111c0d8fdSPaul Kocialkowski 
245211c0d8fdSPaul Kocialkowski 	return ret;
245311c0d8fdSPaul Kocialkowski }
245411c0d8fdSPaul Kocialkowski 
245511c0d8fdSPaul Kocialkowski /* Controls */
245611c0d8fdSPaul Kocialkowski 
ov8865_s_ctrl(struct v4l2_ctrl * ctrl)245711c0d8fdSPaul Kocialkowski static int ov8865_s_ctrl(struct v4l2_ctrl *ctrl)
245811c0d8fdSPaul Kocialkowski {
245911c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev = ov8865_ctrl_subdev(ctrl);
246011c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
246111c0d8fdSPaul Kocialkowski 	unsigned int index;
246211c0d8fdSPaul Kocialkowski 	int ret;
246311c0d8fdSPaul Kocialkowski 
2464ca28690eSDaniel Scally 	/* If VBLANK is altered we need to update exposure to compensate */
2465ca28690eSDaniel Scally 	if (ctrl->id == V4L2_CID_VBLANK) {
2466ca28690eSDaniel Scally 		int exposure_max;
2467ca28690eSDaniel Scally 
2468ca28690eSDaniel Scally 		exposure_max = sensor->state.mode->output_size_y + ctrl->val -
2469ca28690eSDaniel Scally 			       OV8865_INTEGRATION_TIME_MARGIN;
2470ca28690eSDaniel Scally 		__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
2471ca28690eSDaniel Scally 					 sensor->ctrls.exposure->minimum,
2472ca28690eSDaniel Scally 					 exposure_max,
2473ca28690eSDaniel Scally 					 sensor->ctrls.exposure->step,
2474ca28690eSDaniel Scally 					 min(sensor->ctrls.exposure->val,
2475ca28690eSDaniel Scally 					     exposure_max));
2476ca28690eSDaniel Scally 	}
2477ca28690eSDaniel Scally 
247811c0d8fdSPaul Kocialkowski 	/* Wait for the sensor to be on before setting controls. */
247911c0d8fdSPaul Kocialkowski 	if (pm_runtime_suspended(sensor->dev))
248011c0d8fdSPaul Kocialkowski 		return 0;
248111c0d8fdSPaul Kocialkowski 
248211c0d8fdSPaul Kocialkowski 	switch (ctrl->id) {
248311c0d8fdSPaul Kocialkowski 	case V4L2_CID_EXPOSURE:
248411c0d8fdSPaul Kocialkowski 		ret = ov8865_exposure_configure(sensor, ctrl->val);
248511c0d8fdSPaul Kocialkowski 		if (ret)
248611c0d8fdSPaul Kocialkowski 			return ret;
248711c0d8fdSPaul Kocialkowski 		break;
2488d938b2f2SDaniel Scally 	case V4L2_CID_ANALOGUE_GAIN:
2489d938b2f2SDaniel Scally 		ret = ov8865_analog_gain_configure(sensor, ctrl->val);
249011c0d8fdSPaul Kocialkowski 		if (ret)
249111c0d8fdSPaul Kocialkowski 			return ret;
249211c0d8fdSPaul Kocialkowski 		break;
249311c0d8fdSPaul Kocialkowski 	case V4L2_CID_RED_BALANCE:
249411c0d8fdSPaul Kocialkowski 		return ov8865_red_balance_configure(sensor, ctrl->val);
249511c0d8fdSPaul Kocialkowski 	case V4L2_CID_BLUE_BALANCE:
249611c0d8fdSPaul Kocialkowski 		return ov8865_blue_balance_configure(sensor, ctrl->val);
249711c0d8fdSPaul Kocialkowski 	case V4L2_CID_HFLIP:
249811c0d8fdSPaul Kocialkowski 		return ov8865_flip_horz_configure(sensor, !!ctrl->val);
249911c0d8fdSPaul Kocialkowski 	case V4L2_CID_VFLIP:
250011c0d8fdSPaul Kocialkowski 		return ov8865_flip_vert_configure(sensor, !!ctrl->val);
250111c0d8fdSPaul Kocialkowski 	case V4L2_CID_TEST_PATTERN:
250211c0d8fdSPaul Kocialkowski 		index = (unsigned int)ctrl->val;
250311c0d8fdSPaul Kocialkowski 		return ov8865_test_pattern_configure(sensor, index);
25049293aafeSDaniel Scally 	case V4L2_CID_VBLANK:
25059293aafeSDaniel Scally 		return ov8865_vts_configure(sensor, ctrl->val);
250611c0d8fdSPaul Kocialkowski 	default:
250711c0d8fdSPaul Kocialkowski 		return -EINVAL;
250811c0d8fdSPaul Kocialkowski 	}
250911c0d8fdSPaul Kocialkowski 
251011c0d8fdSPaul Kocialkowski 	return 0;
251111c0d8fdSPaul Kocialkowski }
251211c0d8fdSPaul Kocialkowski 
251311c0d8fdSPaul Kocialkowski static const struct v4l2_ctrl_ops ov8865_ctrl_ops = {
251411c0d8fdSPaul Kocialkowski 	.s_ctrl			= ov8865_s_ctrl,
251511c0d8fdSPaul Kocialkowski };
251611c0d8fdSPaul Kocialkowski 
ov8865_ctrls_init(struct ov8865_sensor * sensor)251711c0d8fdSPaul Kocialkowski static int ov8865_ctrls_init(struct ov8865_sensor *sensor)
251811c0d8fdSPaul Kocialkowski {
251911c0d8fdSPaul Kocialkowski 	struct ov8865_ctrls *ctrls = &sensor->ctrls;
252011c0d8fdSPaul Kocialkowski 	struct v4l2_ctrl_handler *handler = &ctrls->handler;
252111c0d8fdSPaul Kocialkowski 	const struct v4l2_ctrl_ops *ops = &ov8865_ctrl_ops;
25229293aafeSDaniel Scally 	const struct ov8865_mode *mode = &ov8865_modes[0];
25236eecfb34SDaniel Scally 	struct v4l2_fwnode_device_properties props;
25249293aafeSDaniel Scally 	unsigned int vblank_max, vblank_def;
2525d84d4ceeSDaniel Scally 	unsigned int hblank;
252611c0d8fdSPaul Kocialkowski 	int ret;
252711c0d8fdSPaul Kocialkowski 
252811c0d8fdSPaul Kocialkowski 	v4l2_ctrl_handler_init(handler, 32);
252911c0d8fdSPaul Kocialkowski 
253011c0d8fdSPaul Kocialkowski 	/* Use our mutex for ctrl locking. */
253111c0d8fdSPaul Kocialkowski 	handler->lock = &sensor->mutex;
253211c0d8fdSPaul Kocialkowski 
253311c0d8fdSPaul Kocialkowski 	/* Exposure */
253411c0d8fdSPaul Kocialkowski 
2535e15ddc96SDaniel Scally 	ctrls->exposure = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 2,
2536e15ddc96SDaniel Scally 					    65535, 1, 32);
253711c0d8fdSPaul Kocialkowski 
253811c0d8fdSPaul Kocialkowski 	/* Gain */
253911c0d8fdSPaul Kocialkowski 
25403fdd94e2SDaniel Scally 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_ANALOGUE_GAIN, 128, 2048, 128,
2541d938b2f2SDaniel Scally 			  128);
254211c0d8fdSPaul Kocialkowski 
254311c0d8fdSPaul Kocialkowski 	/* White Balance */
254411c0d8fdSPaul Kocialkowski 
254511c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_RED_BALANCE, 1, 32767, 1,
254611c0d8fdSPaul Kocialkowski 			  1024);
254711c0d8fdSPaul Kocialkowski 
254811c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_BLUE_BALANCE, 1, 32767, 1,
254911c0d8fdSPaul Kocialkowski 			  1024);
255011c0d8fdSPaul Kocialkowski 
255111c0d8fdSPaul Kocialkowski 	/* Flip */
255211c0d8fdSPaul Kocialkowski 
255311c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
255411c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std(handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
255511c0d8fdSPaul Kocialkowski 
255611c0d8fdSPaul Kocialkowski 	/* Test Pattern */
255711c0d8fdSPaul Kocialkowski 
255811c0d8fdSPaul Kocialkowski 	v4l2_ctrl_new_std_menu_items(handler, ops, V4L2_CID_TEST_PATTERN,
255911c0d8fdSPaul Kocialkowski 				     ARRAY_SIZE(ov8865_test_pattern_menu) - 1,
256011c0d8fdSPaul Kocialkowski 				     0, 0, ov8865_test_pattern_menu);
256111c0d8fdSPaul Kocialkowski 
25629293aafeSDaniel Scally 	/* Blanking */
2563d84d4ceeSDaniel Scally 	hblank = mode->hts - mode->output_size_x;
2564d84d4ceeSDaniel Scally 	ctrls->hblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HBLANK, hblank,
2565d84d4ceeSDaniel Scally 					  hblank, 1, hblank);
2566d84d4ceeSDaniel Scally 
2567d84d4ceeSDaniel Scally 	if (ctrls->hblank)
2568d84d4ceeSDaniel Scally 		ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
2569d84d4ceeSDaniel Scally 
25709293aafeSDaniel Scally 	vblank_max = OV8865_TIMING_MAX_VTS - mode->output_size_y;
25719293aafeSDaniel Scally 	vblank_def = mode->vts - mode->output_size_y;
25729293aafeSDaniel Scally 	ctrls->vblank = v4l2_ctrl_new_std(handler, ops, V4L2_CID_VBLANK,
25739293aafeSDaniel Scally 					  OV8865_TIMING_MIN_VTS, vblank_max, 1,
25749293aafeSDaniel Scally 					  vblank_def);
25759293aafeSDaniel Scally 
257611c0d8fdSPaul Kocialkowski 	/* MIPI CSI-2 */
257711c0d8fdSPaul Kocialkowski 
257811c0d8fdSPaul Kocialkowski 	ctrls->link_freq =
257911c0d8fdSPaul Kocialkowski 		v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
258011c0d8fdSPaul Kocialkowski 				       ARRAY_SIZE(ov8865_link_freq_menu) - 1,
258111c0d8fdSPaul Kocialkowski 				       0, ov8865_link_freq_menu);
258211c0d8fdSPaul Kocialkowski 
258311c0d8fdSPaul Kocialkowski 	ctrls->pixel_rate =
258411c0d8fdSPaul Kocialkowski 		v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 1,
258511c0d8fdSPaul Kocialkowski 				  INT_MAX, 1, 1);
258611c0d8fdSPaul Kocialkowski 
25876eecfb34SDaniel Scally 	/* set properties from fwnode (e.g. rotation, orientation) */
25886eecfb34SDaniel Scally 	ret = v4l2_fwnode_device_parse(sensor->dev, &props);
25896eecfb34SDaniel Scally 	if (ret)
25906eecfb34SDaniel Scally 		goto error_ctrls;
25916eecfb34SDaniel Scally 
25926eecfb34SDaniel Scally 	ret = v4l2_ctrl_new_fwnode_properties(handler, ops, &props);
25936eecfb34SDaniel Scally 	if (ret)
25946eecfb34SDaniel Scally 		goto error_ctrls;
25956eecfb34SDaniel Scally 
259611c0d8fdSPaul Kocialkowski 	if (handler->error) {
259711c0d8fdSPaul Kocialkowski 		ret = handler->error;
259811c0d8fdSPaul Kocialkowski 		goto error_ctrls;
259911c0d8fdSPaul Kocialkowski 	}
260011c0d8fdSPaul Kocialkowski 
260111c0d8fdSPaul Kocialkowski 	ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
260211c0d8fdSPaul Kocialkowski 	ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
260311c0d8fdSPaul Kocialkowski 
260411c0d8fdSPaul Kocialkowski 	sensor->subdev.ctrl_handler = handler;
260511c0d8fdSPaul Kocialkowski 
260611c0d8fdSPaul Kocialkowski 	return 0;
260711c0d8fdSPaul Kocialkowski 
260811c0d8fdSPaul Kocialkowski error_ctrls:
260911c0d8fdSPaul Kocialkowski 	v4l2_ctrl_handler_free(handler);
261011c0d8fdSPaul Kocialkowski 
261111c0d8fdSPaul Kocialkowski 	return ret;
261211c0d8fdSPaul Kocialkowski }
261311c0d8fdSPaul Kocialkowski 
261411c0d8fdSPaul Kocialkowski /* Subdev Video Operations */
261511c0d8fdSPaul Kocialkowski 
ov8865_s_stream(struct v4l2_subdev * subdev,int enable)261611c0d8fdSPaul Kocialkowski static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable)
261711c0d8fdSPaul Kocialkowski {
261811c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
261911c0d8fdSPaul Kocialkowski 	struct ov8865_state *state = &sensor->state;
262011c0d8fdSPaul Kocialkowski 	int ret;
262111c0d8fdSPaul Kocialkowski 
262211c0d8fdSPaul Kocialkowski 	if (enable) {
2623586ee057SMauro Carvalho Chehab 		ret = pm_runtime_resume_and_get(sensor->dev);
2624586ee057SMauro Carvalho Chehab 		if (ret < 0)
262511c0d8fdSPaul Kocialkowski 			return ret;
262611c0d8fdSPaul Kocialkowski 	}
262711c0d8fdSPaul Kocialkowski 
262811c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
262911c0d8fdSPaul Kocialkowski 	ret = ov8865_sw_standby(sensor, !enable);
263011c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
263111c0d8fdSPaul Kocialkowski 
263211c0d8fdSPaul Kocialkowski 	if (ret)
263311c0d8fdSPaul Kocialkowski 		return ret;
263411c0d8fdSPaul Kocialkowski 
263511c0d8fdSPaul Kocialkowski 	state->streaming = !!enable;
263611c0d8fdSPaul Kocialkowski 
263711c0d8fdSPaul Kocialkowski 	if (!enable)
263811c0d8fdSPaul Kocialkowski 		pm_runtime_put(sensor->dev);
263911c0d8fdSPaul Kocialkowski 
264011c0d8fdSPaul Kocialkowski 	return 0;
264111c0d8fdSPaul Kocialkowski }
264211c0d8fdSPaul Kocialkowski 
ov8865_g_frame_interval(struct v4l2_subdev * subdev,struct v4l2_subdev_frame_interval * interval)264311c0d8fdSPaul Kocialkowski static int ov8865_g_frame_interval(struct v4l2_subdev *subdev,
264411c0d8fdSPaul Kocialkowski 				   struct v4l2_subdev_frame_interval *interval)
264511c0d8fdSPaul Kocialkowski {
264611c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
264711c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode;
2648295786e5SDaniel Scally 	unsigned int framesize;
2649295786e5SDaniel Scally 	unsigned int fps;
265011c0d8fdSPaul Kocialkowski 
265111c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
265211c0d8fdSPaul Kocialkowski 
265311c0d8fdSPaul Kocialkowski 	mode = sensor->state.mode;
2654295786e5SDaniel Scally 	framesize = mode->hts * (mode->output_size_y +
2655295786e5SDaniel Scally 				 sensor->ctrls.vblank->val);
2656295786e5SDaniel Scally 	fps = DIV_ROUND_CLOSEST(sensor->ctrls.pixel_rate->val, framesize);
2657295786e5SDaniel Scally 
2658295786e5SDaniel Scally 	interval->interval.numerator = 1;
2659295786e5SDaniel Scally 	interval->interval.denominator = fps;
266011c0d8fdSPaul Kocialkowski 
266111c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
266211c0d8fdSPaul Kocialkowski 
2663a1946cafSYang Li 	return 0;
266411c0d8fdSPaul Kocialkowski }
266511c0d8fdSPaul Kocialkowski 
266611c0d8fdSPaul Kocialkowski static const struct v4l2_subdev_video_ops ov8865_subdev_video_ops = {
266711c0d8fdSPaul Kocialkowski 	.s_stream		= ov8865_s_stream,
266811c0d8fdSPaul Kocialkowski 	.g_frame_interval	= ov8865_g_frame_interval,
266911c0d8fdSPaul Kocialkowski 	.s_frame_interval	= ov8865_g_frame_interval,
267011c0d8fdSPaul Kocialkowski };
267111c0d8fdSPaul Kocialkowski 
267211c0d8fdSPaul Kocialkowski /* Subdev Pad Operations */
267311c0d8fdSPaul Kocialkowski 
ov8865_enum_mbus_code(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code_enum)267411c0d8fdSPaul Kocialkowski static int ov8865_enum_mbus_code(struct v4l2_subdev *subdev,
26750d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
267611c0d8fdSPaul Kocialkowski 				 struct v4l2_subdev_mbus_code_enum *code_enum)
267711c0d8fdSPaul Kocialkowski {
267811c0d8fdSPaul Kocialkowski 	if (code_enum->index >= ARRAY_SIZE(ov8865_mbus_codes))
267911c0d8fdSPaul Kocialkowski 		return -EINVAL;
268011c0d8fdSPaul Kocialkowski 
268111c0d8fdSPaul Kocialkowski 	code_enum->code = ov8865_mbus_codes[code_enum->index];
268211c0d8fdSPaul Kocialkowski 
268311c0d8fdSPaul Kocialkowski 	return 0;
268411c0d8fdSPaul Kocialkowski }
268511c0d8fdSPaul Kocialkowski 
ov8865_mbus_format_fill(struct v4l2_mbus_framefmt * mbus_format,u32 mbus_code,const struct ov8865_mode * mode)268611c0d8fdSPaul Kocialkowski static void ov8865_mbus_format_fill(struct v4l2_mbus_framefmt *mbus_format,
268711c0d8fdSPaul Kocialkowski 				    u32 mbus_code,
268811c0d8fdSPaul Kocialkowski 				    const struct ov8865_mode *mode)
268911c0d8fdSPaul Kocialkowski {
269011c0d8fdSPaul Kocialkowski 	mbus_format->width = mode->output_size_x;
269111c0d8fdSPaul Kocialkowski 	mbus_format->height = mode->output_size_y;
269211c0d8fdSPaul Kocialkowski 	mbus_format->code = mbus_code;
269311c0d8fdSPaul Kocialkowski 
269411c0d8fdSPaul Kocialkowski 	mbus_format->field = V4L2_FIELD_NONE;
269511c0d8fdSPaul Kocialkowski 	mbus_format->colorspace = V4L2_COLORSPACE_RAW;
269611c0d8fdSPaul Kocialkowski 	mbus_format->ycbcr_enc =
269711c0d8fdSPaul Kocialkowski 		V4L2_MAP_YCBCR_ENC_DEFAULT(mbus_format->colorspace);
269811c0d8fdSPaul Kocialkowski 	mbus_format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
269911c0d8fdSPaul Kocialkowski 	mbus_format->xfer_func =
270011c0d8fdSPaul Kocialkowski 		V4L2_MAP_XFER_FUNC_DEFAULT(mbus_format->colorspace);
270111c0d8fdSPaul Kocialkowski }
270211c0d8fdSPaul Kocialkowski 
ov8865_get_fmt(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)270311c0d8fdSPaul Kocialkowski static int ov8865_get_fmt(struct v4l2_subdev *subdev,
27040d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
270511c0d8fdSPaul Kocialkowski 			  struct v4l2_subdev_format *format)
270611c0d8fdSPaul Kocialkowski {
270711c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
270811c0d8fdSPaul Kocialkowski 	struct v4l2_mbus_framefmt *mbus_format = &format->format;
270911c0d8fdSPaul Kocialkowski 
271011c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
271111c0d8fdSPaul Kocialkowski 
271211c0d8fdSPaul Kocialkowski 	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
27130d346d2aSTomi Valkeinen 		*mbus_format = *v4l2_subdev_get_try_format(subdev, sd_state,
271411c0d8fdSPaul Kocialkowski 							   format->pad);
271511c0d8fdSPaul Kocialkowski 	else
271611c0d8fdSPaul Kocialkowski 		ov8865_mbus_format_fill(mbus_format, sensor->state.mbus_code,
271711c0d8fdSPaul Kocialkowski 					sensor->state.mode);
271811c0d8fdSPaul Kocialkowski 
271911c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
272011c0d8fdSPaul Kocialkowski 
272111c0d8fdSPaul Kocialkowski 	return 0;
272211c0d8fdSPaul Kocialkowski }
272311c0d8fdSPaul Kocialkowski 
ov8865_set_fmt(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)272411c0d8fdSPaul Kocialkowski static int ov8865_set_fmt(struct v4l2_subdev *subdev,
27250d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
272611c0d8fdSPaul Kocialkowski 			  struct v4l2_subdev_format *format)
272711c0d8fdSPaul Kocialkowski {
272811c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
272911c0d8fdSPaul Kocialkowski 	struct v4l2_mbus_framefmt *mbus_format = &format->format;
273011c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode;
273111c0d8fdSPaul Kocialkowski 	u32 mbus_code = 0;
2732d84d4ceeSDaniel Scally 	unsigned int hblank;
273311c0d8fdSPaul Kocialkowski 	unsigned int index;
2734ca28690eSDaniel Scally 	int exposure_max;
273511c0d8fdSPaul Kocialkowski 	int ret = 0;
273611c0d8fdSPaul Kocialkowski 
273711c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
273811c0d8fdSPaul Kocialkowski 
273911c0d8fdSPaul Kocialkowski 	if (sensor->state.streaming) {
274011c0d8fdSPaul Kocialkowski 		ret = -EBUSY;
274111c0d8fdSPaul Kocialkowski 		goto complete;
274211c0d8fdSPaul Kocialkowski 	}
274311c0d8fdSPaul Kocialkowski 
274411c0d8fdSPaul Kocialkowski 	/* Try to find requested mbus code. */
274511c0d8fdSPaul Kocialkowski 	for (index = 0; index < ARRAY_SIZE(ov8865_mbus_codes); index++) {
274611c0d8fdSPaul Kocialkowski 		if (ov8865_mbus_codes[index] == mbus_format->code) {
274711c0d8fdSPaul Kocialkowski 			mbus_code = mbus_format->code;
274811c0d8fdSPaul Kocialkowski 			break;
274911c0d8fdSPaul Kocialkowski 		}
275011c0d8fdSPaul Kocialkowski 	}
275111c0d8fdSPaul Kocialkowski 
275211c0d8fdSPaul Kocialkowski 	/* Fallback to default. */
275311c0d8fdSPaul Kocialkowski 	if (!mbus_code)
275411c0d8fdSPaul Kocialkowski 		mbus_code = ov8865_mbus_codes[0];
275511c0d8fdSPaul Kocialkowski 
275611c0d8fdSPaul Kocialkowski 	/* Find the mode with nearest dimensions. */
275711c0d8fdSPaul Kocialkowski 	mode = v4l2_find_nearest_size(ov8865_modes, ARRAY_SIZE(ov8865_modes),
275811c0d8fdSPaul Kocialkowski 				      output_size_x, output_size_y,
275911c0d8fdSPaul Kocialkowski 				      mbus_format->width, mbus_format->height);
276011c0d8fdSPaul Kocialkowski 	if (!mode) {
276111c0d8fdSPaul Kocialkowski 		ret = -EINVAL;
276211c0d8fdSPaul Kocialkowski 		goto complete;
276311c0d8fdSPaul Kocialkowski 	}
276411c0d8fdSPaul Kocialkowski 
276511c0d8fdSPaul Kocialkowski 	ov8865_mbus_format_fill(mbus_format, mbus_code, mode);
276611c0d8fdSPaul Kocialkowski 
276711c0d8fdSPaul Kocialkowski 	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
27680d346d2aSTomi Valkeinen 		*v4l2_subdev_get_try_format(subdev, sd_state, format->pad) =
276911c0d8fdSPaul Kocialkowski 			*mbus_format;
277011c0d8fdSPaul Kocialkowski 	else if (sensor->state.mode != mode ||
277111c0d8fdSPaul Kocialkowski 		 sensor->state.mbus_code != mbus_code)
277211c0d8fdSPaul Kocialkowski 		ret = ov8865_state_configure(sensor, mode, mbus_code);
277311c0d8fdSPaul Kocialkowski 
27749293aafeSDaniel Scally 	__v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV8865_TIMING_MIN_VTS,
27759293aafeSDaniel Scally 				 OV8865_TIMING_MAX_VTS - mode->output_size_y,
27769293aafeSDaniel Scally 				 1, mode->vts - mode->output_size_y);
27779293aafeSDaniel Scally 
2778d84d4ceeSDaniel Scally 	hblank = mode->hts - mode->output_size_x;
2779d84d4ceeSDaniel Scally 	__v4l2_ctrl_modify_range(sensor->ctrls.hblank, hblank, hblank, 1,
2780d84d4ceeSDaniel Scally 				 hblank);
2781d84d4ceeSDaniel Scally 
2782ca28690eSDaniel Scally 	exposure_max = mode->vts - OV8865_INTEGRATION_TIME_MARGIN;
2783ca28690eSDaniel Scally 	__v4l2_ctrl_modify_range(sensor->ctrls.exposure,
2784ca28690eSDaniel Scally 				 sensor->ctrls.exposure->minimum, exposure_max,
2785ca28690eSDaniel Scally 				 sensor->ctrls.exposure->step,
2786ca28690eSDaniel Scally 				 min(sensor->ctrls.exposure->val,
2787ca28690eSDaniel Scally 				     exposure_max));
2788ca28690eSDaniel Scally 
278911c0d8fdSPaul Kocialkowski complete:
279011c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
279111c0d8fdSPaul Kocialkowski 
279211c0d8fdSPaul Kocialkowski 	return ret;
279311c0d8fdSPaul Kocialkowski }
279411c0d8fdSPaul Kocialkowski 
ov8865_enum_frame_size(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * size_enum)279511c0d8fdSPaul Kocialkowski static int ov8865_enum_frame_size(struct v4l2_subdev *subdev,
27960d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
279711c0d8fdSPaul Kocialkowski 				  struct v4l2_subdev_frame_size_enum *size_enum)
279811c0d8fdSPaul Kocialkowski {
279911c0d8fdSPaul Kocialkowski 	const struct ov8865_mode *mode;
280011c0d8fdSPaul Kocialkowski 
280111c0d8fdSPaul Kocialkowski 	if (size_enum->index >= ARRAY_SIZE(ov8865_modes))
280211c0d8fdSPaul Kocialkowski 		return -EINVAL;
280311c0d8fdSPaul Kocialkowski 
280411c0d8fdSPaul Kocialkowski 	mode = &ov8865_modes[size_enum->index];
280511c0d8fdSPaul Kocialkowski 
280611c0d8fdSPaul Kocialkowski 	size_enum->min_width = size_enum->max_width = mode->output_size_x;
280711c0d8fdSPaul Kocialkowski 	size_enum->min_height = size_enum->max_height = mode->output_size_y;
280811c0d8fdSPaul Kocialkowski 
280911c0d8fdSPaul Kocialkowski 	return 0;
281011c0d8fdSPaul Kocialkowski }
281111c0d8fdSPaul Kocialkowski 
2812acd25e22SDaniel Scally static void
__ov8865_get_pad_crop(struct ov8865_sensor * sensor,struct v4l2_subdev_state * state,unsigned int pad,enum v4l2_subdev_format_whence which,struct v4l2_rect * r)2813acd25e22SDaniel Scally __ov8865_get_pad_crop(struct ov8865_sensor *sensor,
2814acd25e22SDaniel Scally 		      struct v4l2_subdev_state *state, unsigned int pad,
2815acd25e22SDaniel Scally 		      enum v4l2_subdev_format_whence which, struct v4l2_rect *r)
2816acd25e22SDaniel Scally {
2817acd25e22SDaniel Scally 	const struct ov8865_mode *mode = sensor->state.mode;
2818acd25e22SDaniel Scally 
2819acd25e22SDaniel Scally 	switch (which) {
2820acd25e22SDaniel Scally 	case V4L2_SUBDEV_FORMAT_TRY:
2821acd25e22SDaniel Scally 		*r = *v4l2_subdev_get_try_crop(&sensor->subdev, state, pad);
2822acd25e22SDaniel Scally 		break;
2823acd25e22SDaniel Scally 	case V4L2_SUBDEV_FORMAT_ACTIVE:
2824acd25e22SDaniel Scally 		r->height = mode->output_size_y;
2825acd25e22SDaniel Scally 		r->width = mode->output_size_x;
2826acd25e22SDaniel Scally 		r->top = (OV8865_NATIVE_HEIGHT - mode->output_size_y) / 2;
2827acd25e22SDaniel Scally 		r->left = (OV8865_NATIVE_WIDTH - mode->output_size_x) / 2;
2828acd25e22SDaniel Scally 		break;
2829acd25e22SDaniel Scally 	}
2830acd25e22SDaniel Scally }
2831acd25e22SDaniel Scally 
ov8865_get_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_state * state,struct v4l2_subdev_selection * sel)2832acd25e22SDaniel Scally static int ov8865_get_selection(struct v4l2_subdev *subdev,
2833acd25e22SDaniel Scally 				struct v4l2_subdev_state *state,
2834acd25e22SDaniel Scally 				struct v4l2_subdev_selection *sel)
2835acd25e22SDaniel Scally {
2836acd25e22SDaniel Scally 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
2837acd25e22SDaniel Scally 
2838acd25e22SDaniel Scally 	switch (sel->target) {
2839acd25e22SDaniel Scally 	case V4L2_SEL_TGT_CROP:
2840acd25e22SDaniel Scally 		mutex_lock(&sensor->mutex);
2841acd25e22SDaniel Scally 		__ov8865_get_pad_crop(sensor, state, sel->pad,
2842acd25e22SDaniel Scally 				      sel->which, &sel->r);
2843acd25e22SDaniel Scally 		mutex_unlock(&sensor->mutex);
2844acd25e22SDaniel Scally 		break;
2845acd25e22SDaniel Scally 	case V4L2_SEL_TGT_NATIVE_SIZE:
2846acd25e22SDaniel Scally 		sel->r.top = 0;
2847acd25e22SDaniel Scally 		sel->r.left = 0;
2848acd25e22SDaniel Scally 		sel->r.width = OV8865_NATIVE_WIDTH;
2849acd25e22SDaniel Scally 		sel->r.height = OV8865_NATIVE_HEIGHT;
2850acd25e22SDaniel Scally 		break;
2851acd25e22SDaniel Scally 	case V4L2_SEL_TGT_CROP_BOUNDS:
2852acd25e22SDaniel Scally 	case V4L2_SEL_TGT_CROP_DEFAULT:
2853acd25e22SDaniel Scally 		sel->r.top = OV8865_ACTIVE_START_TOP;
2854acd25e22SDaniel Scally 		sel->r.left = OV8865_ACTIVE_START_LEFT;
2855acd25e22SDaniel Scally 		sel->r.width = OV8865_ACTIVE_WIDTH;
2856acd25e22SDaniel Scally 		sel->r.height = OV8865_ACTIVE_HEIGHT;
2857acd25e22SDaniel Scally 		break;
2858acd25e22SDaniel Scally 	default:
2859acd25e22SDaniel Scally 		return -EINVAL;
2860acd25e22SDaniel Scally 	}
2861acd25e22SDaniel Scally 
2862acd25e22SDaniel Scally 	return 0;
2863acd25e22SDaniel Scally }
2864acd25e22SDaniel Scally 
286511c0d8fdSPaul Kocialkowski static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = {
286611c0d8fdSPaul Kocialkowski 	.enum_mbus_code		= ov8865_enum_mbus_code,
286711c0d8fdSPaul Kocialkowski 	.get_fmt		= ov8865_get_fmt,
286811c0d8fdSPaul Kocialkowski 	.set_fmt		= ov8865_set_fmt,
286911c0d8fdSPaul Kocialkowski 	.enum_frame_size	= ov8865_enum_frame_size,
2870acd25e22SDaniel Scally 	.get_selection		= ov8865_get_selection,
2871acd25e22SDaniel Scally 	.set_selection		= ov8865_get_selection,
287211c0d8fdSPaul Kocialkowski };
287311c0d8fdSPaul Kocialkowski 
287411c0d8fdSPaul Kocialkowski static const struct v4l2_subdev_ops ov8865_subdev_ops = {
287511c0d8fdSPaul Kocialkowski 	.video		= &ov8865_subdev_video_ops,
287611c0d8fdSPaul Kocialkowski 	.pad		= &ov8865_subdev_pad_ops,
287711c0d8fdSPaul Kocialkowski };
287811c0d8fdSPaul Kocialkowski 
ov8865_suspend(struct device * dev)287911c0d8fdSPaul Kocialkowski static int ov8865_suspend(struct device *dev)
288011c0d8fdSPaul Kocialkowski {
288111c0d8fdSPaul Kocialkowski 	struct i2c_client *client = to_i2c_client(dev);
288211c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
288311c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
288411c0d8fdSPaul Kocialkowski 	struct ov8865_state *state = &sensor->state;
288511c0d8fdSPaul Kocialkowski 	int ret = 0;
288611c0d8fdSPaul Kocialkowski 
288711c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
288811c0d8fdSPaul Kocialkowski 
288911c0d8fdSPaul Kocialkowski 	if (state->streaming) {
289011c0d8fdSPaul Kocialkowski 		ret = ov8865_sw_standby(sensor, true);
289111c0d8fdSPaul Kocialkowski 		if (ret)
289211c0d8fdSPaul Kocialkowski 			goto complete;
289311c0d8fdSPaul Kocialkowski 	}
289411c0d8fdSPaul Kocialkowski 
289511c0d8fdSPaul Kocialkowski 	ret = ov8865_sensor_power(sensor, false);
289611c0d8fdSPaul Kocialkowski 	if (ret)
289711c0d8fdSPaul Kocialkowski 		ov8865_sw_standby(sensor, false);
289811c0d8fdSPaul Kocialkowski 
289911c0d8fdSPaul Kocialkowski complete:
290011c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
290111c0d8fdSPaul Kocialkowski 
290211c0d8fdSPaul Kocialkowski 	return ret;
290311c0d8fdSPaul Kocialkowski }
290411c0d8fdSPaul Kocialkowski 
ov8865_resume(struct device * dev)290511c0d8fdSPaul Kocialkowski static int ov8865_resume(struct device *dev)
290611c0d8fdSPaul Kocialkowski {
290711c0d8fdSPaul Kocialkowski 	struct i2c_client *client = to_i2c_client(dev);
290811c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
290911c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
291011c0d8fdSPaul Kocialkowski 	struct ov8865_state *state = &sensor->state;
291111c0d8fdSPaul Kocialkowski 	int ret = 0;
291211c0d8fdSPaul Kocialkowski 
291311c0d8fdSPaul Kocialkowski 	mutex_lock(&sensor->mutex);
291411c0d8fdSPaul Kocialkowski 
291511c0d8fdSPaul Kocialkowski 	ret = ov8865_sensor_power(sensor, true);
291611c0d8fdSPaul Kocialkowski 	if (ret)
291711c0d8fdSPaul Kocialkowski 		goto complete;
291811c0d8fdSPaul Kocialkowski 
291911c0d8fdSPaul Kocialkowski 	ret = ov8865_sensor_init(sensor);
292011c0d8fdSPaul Kocialkowski 	if (ret)
292111c0d8fdSPaul Kocialkowski 		goto error_power;
292211c0d8fdSPaul Kocialkowski 
292311c0d8fdSPaul Kocialkowski 	ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
292411c0d8fdSPaul Kocialkowski 	if (ret)
292511c0d8fdSPaul Kocialkowski 		goto error_power;
292611c0d8fdSPaul Kocialkowski 
292711c0d8fdSPaul Kocialkowski 	if (state->streaming) {
292811c0d8fdSPaul Kocialkowski 		ret = ov8865_sw_standby(sensor, false);
292911c0d8fdSPaul Kocialkowski 		if (ret)
293011c0d8fdSPaul Kocialkowski 			goto error_power;
293111c0d8fdSPaul Kocialkowski 	}
293211c0d8fdSPaul Kocialkowski 
293311c0d8fdSPaul Kocialkowski 	goto complete;
293411c0d8fdSPaul Kocialkowski 
293511c0d8fdSPaul Kocialkowski error_power:
293611c0d8fdSPaul Kocialkowski 	ov8865_sensor_power(sensor, false);
293711c0d8fdSPaul Kocialkowski 
293811c0d8fdSPaul Kocialkowski complete:
293911c0d8fdSPaul Kocialkowski 	mutex_unlock(&sensor->mutex);
294011c0d8fdSPaul Kocialkowski 
294111c0d8fdSPaul Kocialkowski 	return ret;
294211c0d8fdSPaul Kocialkowski }
294311c0d8fdSPaul Kocialkowski 
ov8865_probe(struct i2c_client * client)294411c0d8fdSPaul Kocialkowski static int ov8865_probe(struct i2c_client *client)
294511c0d8fdSPaul Kocialkowski {
294611c0d8fdSPaul Kocialkowski 	struct device *dev = &client->dev;
294711c0d8fdSPaul Kocialkowski 	struct fwnode_handle *handle;
294811c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor;
294911c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev;
295011c0d8fdSPaul Kocialkowski 	struct media_pad *pad;
295173dcffebSDaniel Scally 	unsigned int rate = 0;
295273dcffebSDaniel Scally 	unsigned int i;
295311c0d8fdSPaul Kocialkowski 	int ret;
295411c0d8fdSPaul Kocialkowski 
295511c0d8fdSPaul Kocialkowski 	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
295611c0d8fdSPaul Kocialkowski 	if (!sensor)
295711c0d8fdSPaul Kocialkowski 		return -ENOMEM;
295811c0d8fdSPaul Kocialkowski 
295911c0d8fdSPaul Kocialkowski 	sensor->dev = dev;
296011c0d8fdSPaul Kocialkowski 	sensor->i2c_client = client;
296111c0d8fdSPaul Kocialkowski 
296291f08141SDaniel Scally 	/* Regulators */
296391f08141SDaniel Scally 
296491f08141SDaniel Scally 	/* DVDD: digital core */
296591f08141SDaniel Scally 	sensor->dvdd = devm_regulator_get(dev, "dvdd");
296691f08141SDaniel Scally 	if (IS_ERR(sensor->dvdd))
296791f08141SDaniel Scally 		return dev_err_probe(dev, PTR_ERR(sensor->dvdd),
296891f08141SDaniel Scally 				     "cannot get DVDD regulator\n");
296991f08141SDaniel Scally 
297091f08141SDaniel Scally 	/* DOVDD: digital I/O */
297191f08141SDaniel Scally 	sensor->dovdd = devm_regulator_get(dev, "dovdd");
297291f08141SDaniel Scally 	if (IS_ERR(sensor->dovdd))
297391f08141SDaniel Scally 		return dev_err_probe(dev, PTR_ERR(sensor->dovdd),
297491f08141SDaniel Scally 				     "cannot get DOVDD regulator\n");
297591f08141SDaniel Scally 
297691f08141SDaniel Scally 	/* AVDD: analog */
297791f08141SDaniel Scally 	sensor->avdd = devm_regulator_get(dev, "avdd");
297891f08141SDaniel Scally 	if (IS_ERR(sensor->avdd))
297991f08141SDaniel Scally 		return dev_err_probe(dev, PTR_ERR(sensor->avdd),
298091f08141SDaniel Scally 				     "cannot get AVDD (analog) regulator\n");
298191f08141SDaniel Scally 
298211c0d8fdSPaul Kocialkowski 	/* Graph Endpoint */
298311c0d8fdSPaul Kocialkowski 
298411c0d8fdSPaul Kocialkowski 	handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
2985ba0c8045SDaniel Scally 	if (!handle)
2986ba0c8045SDaniel Scally 		return -EPROBE_DEFER;
298711c0d8fdSPaul Kocialkowski 
298811c0d8fdSPaul Kocialkowski 	sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY;
298911c0d8fdSPaul Kocialkowski 
299011c0d8fdSPaul Kocialkowski 	ret = v4l2_fwnode_endpoint_alloc_parse(handle, &sensor->endpoint);
299111c0d8fdSPaul Kocialkowski 	fwnode_handle_put(handle);
299211c0d8fdSPaul Kocialkowski 	if (ret) {
299311c0d8fdSPaul Kocialkowski 		dev_err(dev, "failed to parse endpoint node\n");
299411c0d8fdSPaul Kocialkowski 		return ret;
299511c0d8fdSPaul Kocialkowski 	}
299611c0d8fdSPaul Kocialkowski 
299711c0d8fdSPaul Kocialkowski 	/* GPIOs */
299811c0d8fdSPaul Kocialkowski 
299911c0d8fdSPaul Kocialkowski 	sensor->powerdown = devm_gpiod_get_optional(dev, "powerdown",
300011c0d8fdSPaul Kocialkowski 						    GPIOD_OUT_HIGH);
300111c0d8fdSPaul Kocialkowski 	if (IS_ERR(sensor->powerdown)) {
300211c0d8fdSPaul Kocialkowski 		ret = PTR_ERR(sensor->powerdown);
300311c0d8fdSPaul Kocialkowski 		goto error_endpoint;
300411c0d8fdSPaul Kocialkowski 	}
300511c0d8fdSPaul Kocialkowski 
300611c0d8fdSPaul Kocialkowski 	sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
300711c0d8fdSPaul Kocialkowski 	if (IS_ERR(sensor->reset)) {
300811c0d8fdSPaul Kocialkowski 		ret = PTR_ERR(sensor->reset);
300911c0d8fdSPaul Kocialkowski 		goto error_endpoint;
301011c0d8fdSPaul Kocialkowski 	}
301111c0d8fdSPaul Kocialkowski 
301211c0d8fdSPaul Kocialkowski 	/* External Clock */
301311c0d8fdSPaul Kocialkowski 
301411c0d8fdSPaul Kocialkowski 	sensor->extclk = devm_clk_get(dev, NULL);
301573dcffebSDaniel Scally 	if (PTR_ERR(sensor->extclk) == -ENOENT) {
301673dcffebSDaniel Scally 		dev_info(dev, "no external clock found, continuing...\n");
301773dcffebSDaniel Scally 		sensor->extclk = NULL;
301873dcffebSDaniel Scally 	} else if (IS_ERR(sensor->extclk)) {
301911c0d8fdSPaul Kocialkowski 		dev_err(dev, "failed to get external clock\n");
302011c0d8fdSPaul Kocialkowski 		ret = PTR_ERR(sensor->extclk);
302111c0d8fdSPaul Kocialkowski 		goto error_endpoint;
302211c0d8fdSPaul Kocialkowski 	}
302311c0d8fdSPaul Kocialkowski 
302473dcffebSDaniel Scally 	/*
302573dcffebSDaniel Scally 	 * We could have either a 24MHz or 19.2MHz clock rate from either dt or
302673dcffebSDaniel Scally 	 * ACPI...but we also need to support the weird IPU3 case which will
302773dcffebSDaniel Scally 	 * have an external clock AND a clock-frequency property. Check for the
302873dcffebSDaniel Scally 	 * clock-frequency property and if found, set that rate if we managed
302973dcffebSDaniel Scally 	 * to acquire a clock. This should cover the ACPI case. If the system
303073dcffebSDaniel Scally 	 * uses devicetree then the configured rate should already be set, so
303173dcffebSDaniel Scally 	 * we can just read it.
303273dcffebSDaniel Scally 	 */
303373dcffebSDaniel Scally 	ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
303473dcffebSDaniel Scally 				       &rate);
303573dcffebSDaniel Scally 	if (!ret && sensor->extclk) {
303673dcffebSDaniel Scally 		ret = clk_set_rate(sensor->extclk, rate);
3037080e0b74SChristophe JAILLET 		if (ret) {
3038080e0b74SChristophe JAILLET 			dev_err_probe(dev, ret, "failed to set clock rate\n");
3039080e0b74SChristophe JAILLET 			goto error_endpoint;
3040080e0b74SChristophe JAILLET 		}
304173dcffebSDaniel Scally 	} else if (ret && !sensor->extclk) {
3042080e0b74SChristophe JAILLET 		dev_err_probe(dev, ret, "invalid clock config\n");
3043080e0b74SChristophe JAILLET 		goto error_endpoint;
304473dcffebSDaniel Scally 	}
304573dcffebSDaniel Scally 
304673dcffebSDaniel Scally 	sensor->extclk_rate = rate ? rate : clk_get_rate(sensor->extclk);
304773dcffebSDaniel Scally 
304873dcffebSDaniel Scally 	for (i = 0; i < ARRAY_SIZE(supported_extclk_rates); i++) {
304973dcffebSDaniel Scally 		if (sensor->extclk_rate == supported_extclk_rates[i])
305073dcffebSDaniel Scally 			break;
305173dcffebSDaniel Scally 	}
305273dcffebSDaniel Scally 
305373dcffebSDaniel Scally 	if (i == ARRAY_SIZE(supported_extclk_rates)) {
305473dcffebSDaniel Scally 		dev_err(dev, "clock rate %lu Hz is unsupported\n",
305573dcffebSDaniel Scally 			sensor->extclk_rate);
305611c0d8fdSPaul Kocialkowski 		ret = -EINVAL;
305711c0d8fdSPaul Kocialkowski 		goto error_endpoint;
305811c0d8fdSPaul Kocialkowski 	}
305911c0d8fdSPaul Kocialkowski 
306073dcffebSDaniel Scally 	sensor->pll_configs = ov8865_pll_configs[i];
306173dcffebSDaniel Scally 
306211c0d8fdSPaul Kocialkowski 	/* Subdev, entity and pad */
306311c0d8fdSPaul Kocialkowski 
306411c0d8fdSPaul Kocialkowski 	subdev = &sensor->subdev;
306511c0d8fdSPaul Kocialkowski 	v4l2_i2c_subdev_init(subdev, client, &ov8865_subdev_ops);
306611c0d8fdSPaul Kocialkowski 
306711c0d8fdSPaul Kocialkowski 	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
306811c0d8fdSPaul Kocialkowski 	subdev->entity.function = MEDIA_ENT_F_CAM_SENSOR;
306911c0d8fdSPaul Kocialkowski 
307011c0d8fdSPaul Kocialkowski 	pad = &sensor->pad;
307111c0d8fdSPaul Kocialkowski 	pad->flags = MEDIA_PAD_FL_SOURCE;
307211c0d8fdSPaul Kocialkowski 
307311c0d8fdSPaul Kocialkowski 	ret = media_entity_pads_init(&subdev->entity, 1, pad);
307411c0d8fdSPaul Kocialkowski 	if (ret)
307511c0d8fdSPaul Kocialkowski 		goto error_entity;
307611c0d8fdSPaul Kocialkowski 
307711c0d8fdSPaul Kocialkowski 	/* Mutex */
307811c0d8fdSPaul Kocialkowski 
307911c0d8fdSPaul Kocialkowski 	mutex_init(&sensor->mutex);
308011c0d8fdSPaul Kocialkowski 
308111c0d8fdSPaul Kocialkowski 	/* Sensor */
308211c0d8fdSPaul Kocialkowski 
308311c0d8fdSPaul Kocialkowski 	ret = ov8865_ctrls_init(sensor);
308411c0d8fdSPaul Kocialkowski 	if (ret)
308511c0d8fdSPaul Kocialkowski 		goto error_mutex;
308611c0d8fdSPaul Kocialkowski 
30876e1c9bc9SHans de Goede 	mutex_lock(&sensor->mutex);
308811c0d8fdSPaul Kocialkowski 	ret = ov8865_state_init(sensor);
30896e1c9bc9SHans de Goede 	mutex_unlock(&sensor->mutex);
309011c0d8fdSPaul Kocialkowski 	if (ret)
309111c0d8fdSPaul Kocialkowski 		goto error_ctrls;
309211c0d8fdSPaul Kocialkowski 
309311c0d8fdSPaul Kocialkowski 	/* Runtime PM */
309411c0d8fdSPaul Kocialkowski 
309511c0d8fdSPaul Kocialkowski 	pm_runtime_set_suspended(sensor->dev);
3096d2484fbfSDaniel Scally 	pm_runtime_enable(sensor->dev);
309711c0d8fdSPaul Kocialkowski 
309811c0d8fdSPaul Kocialkowski 	/* V4L2 subdev register */
309911c0d8fdSPaul Kocialkowski 
310015786f7bSSakari Ailus 	ret = v4l2_async_register_subdev_sensor(subdev);
310111c0d8fdSPaul Kocialkowski 	if (ret)
310211c0d8fdSPaul Kocialkowski 		goto error_pm;
310311c0d8fdSPaul Kocialkowski 
310411c0d8fdSPaul Kocialkowski 	return 0;
310511c0d8fdSPaul Kocialkowski 
310611c0d8fdSPaul Kocialkowski error_pm:
310711c0d8fdSPaul Kocialkowski 	pm_runtime_disable(sensor->dev);
310811c0d8fdSPaul Kocialkowski 
310911c0d8fdSPaul Kocialkowski error_ctrls:
311011c0d8fdSPaul Kocialkowski 	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
311111c0d8fdSPaul Kocialkowski 
311211c0d8fdSPaul Kocialkowski error_mutex:
311311c0d8fdSPaul Kocialkowski 	mutex_destroy(&sensor->mutex);
311411c0d8fdSPaul Kocialkowski 
311511c0d8fdSPaul Kocialkowski error_entity:
311611c0d8fdSPaul Kocialkowski 	media_entity_cleanup(&sensor->subdev.entity);
311711c0d8fdSPaul Kocialkowski 
311811c0d8fdSPaul Kocialkowski error_endpoint:
311911c0d8fdSPaul Kocialkowski 	v4l2_fwnode_endpoint_free(&sensor->endpoint);
312011c0d8fdSPaul Kocialkowski 
312111c0d8fdSPaul Kocialkowski 	return ret;
312211c0d8fdSPaul Kocialkowski }
312311c0d8fdSPaul Kocialkowski 
ov8865_remove(struct i2c_client * client)3124ed5c2f5fSUwe Kleine-König static void ov8865_remove(struct i2c_client *client)
312511c0d8fdSPaul Kocialkowski {
312611c0d8fdSPaul Kocialkowski 	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
312711c0d8fdSPaul Kocialkowski 	struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev);
312811c0d8fdSPaul Kocialkowski 
312911c0d8fdSPaul Kocialkowski 	v4l2_async_unregister_subdev(subdev);
313011c0d8fdSPaul Kocialkowski 	pm_runtime_disable(sensor->dev);
313111c0d8fdSPaul Kocialkowski 	v4l2_ctrl_handler_free(&sensor->ctrls.handler);
313211c0d8fdSPaul Kocialkowski 	mutex_destroy(&sensor->mutex);
313311c0d8fdSPaul Kocialkowski 	media_entity_cleanup(&subdev->entity);
313411c0d8fdSPaul Kocialkowski 
313511c0d8fdSPaul Kocialkowski 	v4l2_fwnode_endpoint_free(&sensor->endpoint);
313611c0d8fdSPaul Kocialkowski }
313711c0d8fdSPaul Kocialkowski 
313811c0d8fdSPaul Kocialkowski static const struct dev_pm_ops ov8865_pm_ops = {
313911c0d8fdSPaul Kocialkowski 	SET_RUNTIME_PM_OPS(ov8865_suspend, ov8865_resume, NULL)
314011c0d8fdSPaul Kocialkowski };
314111c0d8fdSPaul Kocialkowski 
3142dc69bc7aSDaniel Scally static const struct acpi_device_id ov8865_acpi_match[] = {
3143dc69bc7aSDaniel Scally 	{"INT347A"},
3144dc69bc7aSDaniel Scally 	{ }
3145dc69bc7aSDaniel Scally };
3146dc69bc7aSDaniel Scally MODULE_DEVICE_TABLE(acpi, ov8865_acpi_match);
3147dc69bc7aSDaniel Scally 
314811c0d8fdSPaul Kocialkowski static const struct of_device_id ov8865_of_match[] = {
314911c0d8fdSPaul Kocialkowski 	{ .compatible = "ovti,ov8865" },
315011c0d8fdSPaul Kocialkowski 	{ }
315111c0d8fdSPaul Kocialkowski };
315211c0d8fdSPaul Kocialkowski MODULE_DEVICE_TABLE(of, ov8865_of_match);
315311c0d8fdSPaul Kocialkowski 
315411c0d8fdSPaul Kocialkowski static struct i2c_driver ov8865_driver = {
315511c0d8fdSPaul Kocialkowski 	.driver = {
315611c0d8fdSPaul Kocialkowski 		.name = "ov8865",
315711c0d8fdSPaul Kocialkowski 		.of_match_table = ov8865_of_match,
3158dc69bc7aSDaniel Scally 		.acpi_match_table = ov8865_acpi_match,
315911c0d8fdSPaul Kocialkowski 		.pm = &ov8865_pm_ops,
316011c0d8fdSPaul Kocialkowski 	},
3161*aaeb31c0SUwe Kleine-König 	.probe = ov8865_probe,
316211c0d8fdSPaul Kocialkowski 	.remove = ov8865_remove,
316311c0d8fdSPaul Kocialkowski };
316411c0d8fdSPaul Kocialkowski 
316511c0d8fdSPaul Kocialkowski module_i2c_driver(ov8865_driver);
316611c0d8fdSPaul Kocialkowski 
316711c0d8fdSPaul Kocialkowski MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>");
316811c0d8fdSPaul Kocialkowski MODULE_DESCRIPTION("V4L2 driver for the OmniVision OV8865 image sensor");
316911c0d8fdSPaul Kocialkowski MODULE_LICENSE("GPL v2");
3170