xref: /linux/drivers/media/i2c/ov4689.c (revision 6fd600d742744dc7ef7fc65ca26daa2b1163158a)
132a437dbSMikhail Rudenko // SPDX-License-Identifier: GPL-2.0
232a437dbSMikhail Rudenko /*
332a437dbSMikhail Rudenko  * ov4689 driver
432a437dbSMikhail Rudenko  *
532a437dbSMikhail Rudenko  * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
6af9874c9SMikhail Rudenko  * Copyright (C) 2022, 2024 Mikhail Rudenko
732a437dbSMikhail Rudenko  */
832a437dbSMikhail Rudenko 
932a437dbSMikhail Rudenko #include <linux/clk.h>
1032a437dbSMikhail Rudenko #include <linux/delay.h>
1132a437dbSMikhail Rudenko #include <linux/gpio/consumer.h>
1232a437dbSMikhail Rudenko #include <linux/i2c.h>
1332a437dbSMikhail Rudenko #include <linux/module.h>
1432a437dbSMikhail Rudenko #include <linux/pm_runtime.h>
1532a437dbSMikhail Rudenko #include <linux/regulator/consumer.h>
1632a437dbSMikhail Rudenko #include <media/media-entity.h>
1732a437dbSMikhail Rudenko #include <media/v4l2-async.h>
188fe37e59SMikhail Rudenko #include <media/v4l2-cci.h>
1932a437dbSMikhail Rudenko #include <media/v4l2-ctrls.h>
2032a437dbSMikhail Rudenko #include <media/v4l2-subdev.h>
2132a437dbSMikhail Rudenko #include <media/v4l2-fwnode.h>
2232a437dbSMikhail Rudenko 
238fe37e59SMikhail Rudenko #define OV4689_REG_CTRL_MODE		CCI_REG8(0x0100)
2432a437dbSMikhail Rudenko #define OV4689_MODE_SW_STANDBY		0x0
2532a437dbSMikhail Rudenko #define OV4689_MODE_STREAMING		BIT(0)
2632a437dbSMikhail Rudenko 
278fe37e59SMikhail Rudenko #define OV4689_REG_CHIP_ID		CCI_REG16(0x300a)
28f3adec37SMikhail Rudenko #define CHIP_ID				0x004688
29f3adec37SMikhail Rudenko 
308fe37e59SMikhail Rudenko #define OV4689_REG_EXPOSURE		CCI_REG24(0x3500)
3132a437dbSMikhail Rudenko #define OV4689_EXPOSURE_MIN		4
3232a437dbSMikhail Rudenko #define OV4689_EXPOSURE_STEP		1
3332a437dbSMikhail Rudenko 
348fe37e59SMikhail Rudenko #define OV4689_REG_GAIN			CCI_REG16(0x3508)
3532a437dbSMikhail Rudenko #define OV4689_GAIN_STEP		1
3632a437dbSMikhail Rudenko #define OV4689_GAIN_DEFAULT		0x80
3732a437dbSMikhail Rudenko 
38398eca19SMikhail Rudenko #define OV4689_REG_DIG_GAIN		CCI_REG16(0x352a)
39398eca19SMikhail Rudenko #define OV4689_DIG_GAIN_MIN		1
40398eca19SMikhail Rudenko #define OV4689_DIG_GAIN_MAX		0x7fff
41398eca19SMikhail Rudenko #define OV4689_DIG_GAIN_STEP		1
42398eca19SMikhail Rudenko #define OV4689_DIG_GAIN_DEFAULT		0x800
43398eca19SMikhail Rudenko 
44*05e8c95eSMikhail Rudenko #define OV4689_REG_H_CROP_START		CCI_REG16(0x3800)
45*05e8c95eSMikhail Rudenko #define OV4689_REG_V_CROP_START		CCI_REG16(0x3802)
46*05e8c95eSMikhail Rudenko #define OV4689_REG_H_CROP_END		CCI_REG16(0x3804)
47*05e8c95eSMikhail Rudenko #define OV4689_REG_V_CROP_END		CCI_REG16(0x3806)
48*05e8c95eSMikhail Rudenko #define OV4689_REG_H_OUTPUT_SIZE	CCI_REG16(0x3808)
49*05e8c95eSMikhail Rudenko #define OV4689_REG_V_OUTPUT_SIZE	CCI_REG16(0x380a)
50*05e8c95eSMikhail Rudenko 
5162911feaSMikhail Rudenko #define OV4689_REG_HTS			CCI_REG16(0x380c)
5262911feaSMikhail Rudenko #define OV4689_HTS_DIVIDER		4
5362911feaSMikhail Rudenko #define OV4689_HTS_MAX			0x7fff
5462911feaSMikhail Rudenko 
558fe37e59SMikhail Rudenko #define OV4689_REG_VTS			CCI_REG16(0x380e)
5662911feaSMikhail Rudenko #define OV4689_VTS_MAX			0x7fff
57f3adec37SMikhail Rudenko 
58*05e8c95eSMikhail Rudenko #define OV4689_REG_H_WIN_OFF		CCI_REG16(0x3810)
59*05e8c95eSMikhail Rudenko #define OV4689_REG_V_WIN_OFF		CCI_REG16(0x3812)
60*05e8c95eSMikhail Rudenko 
61ec43d634SMikhail Rudenko #define OV4689_REG_TIMING_FORMAT1	CCI_REG8(0x3820) /* Vertical */
62ec43d634SMikhail Rudenko #define OV4689_REG_TIMING_FORMAT2	CCI_REG8(0x3821) /* Horizontal */
63ec43d634SMikhail Rudenko #define OV4689_TIMING_FLIP_MASK		GENMASK(2, 1)
64ec43d634SMikhail Rudenko #define OV4689_TIMING_FLIP_ARRAY	BIT(1)
65ec43d634SMikhail Rudenko #define OV4689_TIMING_FLIP_DIGITAL	BIT(2)
66ec43d634SMikhail Rudenko #define OV4689_TIMING_FLIP_BOTH		(OV4689_TIMING_FLIP_ARRAY |\
67ec43d634SMikhail Rudenko 					 OV4689_TIMING_FLIP_DIGITAL)
68ec43d634SMikhail Rudenko 
69*05e8c95eSMikhail Rudenko #define OV4689_REG_ANCHOR_LEFT_START	CCI_REG16(0x4020)
70*05e8c95eSMikhail Rudenko #define OV4689_ANCHOR_LEFT_START_DEF	576
71*05e8c95eSMikhail Rudenko #define OV4689_REG_ANCHOR_LEFT_END	CCI_REG16(0x4022)
72*05e8c95eSMikhail Rudenko #define OV4689_ANCHOR_LEFT_END_DEF	831
73*05e8c95eSMikhail Rudenko #define OV4689_REG_ANCHOR_RIGHT_START	CCI_REG16(0x4024)
74*05e8c95eSMikhail Rudenko #define OV4689_ANCHOR_RIGHT_START_DEF	1984
75*05e8c95eSMikhail Rudenko #define OV4689_REG_ANCHOR_RIGHT_END	CCI_REG16(0x4026)
76*05e8c95eSMikhail Rudenko #define OV4689_ANCHOR_RIGHT_END_DEF	2239
77*05e8c95eSMikhail Rudenko 
78*05e8c95eSMikhail Rudenko #define OV4689_REG_VFIFO_CTRL_01	CCI_REG8(0x4601)
79*05e8c95eSMikhail Rudenko 
806b3ad3bcSMikhail Rudenko #define OV4689_REG_WB_GAIN_RED		CCI_REG16(0x500c)
816b3ad3bcSMikhail Rudenko #define OV4689_REG_WB_GAIN_BLUE		CCI_REG16(0x5010)
826b3ad3bcSMikhail Rudenko #define OV4689_WB_GAIN_MIN		1
836b3ad3bcSMikhail Rudenko #define OV4689_WB_GAIN_MAX		0xfff
846b3ad3bcSMikhail Rudenko #define OV4689_WB_GAIN_STEP		1
856b3ad3bcSMikhail Rudenko #define OV4689_WB_GAIN_DEFAULT		0x400
866b3ad3bcSMikhail Rudenko 
878fe37e59SMikhail Rudenko #define OV4689_REG_TEST_PATTERN		CCI_REG8(0x5040)
8832a437dbSMikhail Rudenko #define OV4689_TEST_PATTERN_ENABLE	0x80
8932a437dbSMikhail Rudenko #define OV4689_TEST_PATTERN_DISABLE	0x0
9032a437dbSMikhail Rudenko 
9132a437dbSMikhail Rudenko #define OV4689_LANES			4
928fe37e59SMikhail Rudenko #define OV4689_XVCLK_FREQ		24000000
9332a437dbSMikhail Rudenko 
94bf475d32SMikhail Rudenko #define OV4689_PIXEL_ARRAY_WIDTH	2720
95bf475d32SMikhail Rudenko #define OV4689_PIXEL_ARRAY_HEIGHT	1536
96bf475d32SMikhail Rudenko #define OV4689_DUMMY_ROWS		8	/* 8 dummy rows on each side */
97bf475d32SMikhail Rudenko #define OV4689_DUMMY_COLUMNS		16	/* 16 dummy columns on each side */
98bf475d32SMikhail Rudenko 
9932a437dbSMikhail Rudenko static const char *const ov4689_supply_names[] = {
10032a437dbSMikhail Rudenko 	"avdd", /* Analog power */
10132a437dbSMikhail Rudenko 	"dovdd", /* Digital I/O power */
10232a437dbSMikhail Rudenko 	"dvdd", /* Digital core power */
10332a437dbSMikhail Rudenko };
10432a437dbSMikhail Rudenko 
10532a437dbSMikhail Rudenko enum ov4689_mode_id {
10632a437dbSMikhail Rudenko 	OV4689_MODE_2688_1520 = 0,
10732a437dbSMikhail Rudenko 	OV4689_NUM_MODES,
10832a437dbSMikhail Rudenko };
10932a437dbSMikhail Rudenko 
11032a437dbSMikhail Rudenko struct ov4689_mode {
11132a437dbSMikhail Rudenko 	enum ov4689_mode_id id;
11232a437dbSMikhail Rudenko 	u32 width;
11332a437dbSMikhail Rudenko 	u32 height;
11432a437dbSMikhail Rudenko 	u32 hts_def;
11562911feaSMikhail Rudenko 	u32 hts_min;
11632a437dbSMikhail Rudenko 	u32 vts_def;
11732a437dbSMikhail Rudenko 	u32 exp_def;
11832a437dbSMikhail Rudenko 	u32 pixel_rate;
1198fe37e59SMikhail Rudenko 	const struct cci_reg_sequence *reg_list;
1208fe37e59SMikhail Rudenko 	unsigned int num_regs;
12132a437dbSMikhail Rudenko };
12232a437dbSMikhail Rudenko 
12332a437dbSMikhail Rudenko struct ov4689 {
124d015aaafSMikhail Rudenko 	struct device *dev;
1258fe37e59SMikhail Rudenko 	struct regmap *regmap;
12632a437dbSMikhail Rudenko 	struct clk *xvclk;
12732a437dbSMikhail Rudenko 	struct gpio_desc *reset_gpio;
12832a437dbSMikhail Rudenko 	struct gpio_desc *pwdn_gpio;
12932a437dbSMikhail Rudenko 	struct regulator_bulk_data supplies[ARRAY_SIZE(ov4689_supply_names)];
13032a437dbSMikhail Rudenko 
13132a437dbSMikhail Rudenko 	struct v4l2_subdev subdev;
13232a437dbSMikhail Rudenko 	struct media_pad pad;
13332a437dbSMikhail Rudenko 
13432a437dbSMikhail Rudenko 	u32 clock_rate;
13532a437dbSMikhail Rudenko 
13632a437dbSMikhail Rudenko 	struct v4l2_ctrl_handler ctrl_handler;
13732a437dbSMikhail Rudenko 	struct v4l2_ctrl *exposure;
13832a437dbSMikhail Rudenko 
13932a437dbSMikhail Rudenko 	const struct ov4689_mode *cur_mode;
14032a437dbSMikhail Rudenko };
14132a437dbSMikhail Rudenko 
14232a437dbSMikhail Rudenko #define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
14332a437dbSMikhail Rudenko 
14432a437dbSMikhail Rudenko struct ov4689_gain_range {
14532a437dbSMikhail Rudenko 	u32 logical_min;
14632a437dbSMikhail Rudenko 	u32 logical_max;
14732a437dbSMikhail Rudenko 	u32 offset;
14832a437dbSMikhail Rudenko 	u32 divider;
14932a437dbSMikhail Rudenko 	u32 physical_min;
15032a437dbSMikhail Rudenko 	u32 physical_max;
15132a437dbSMikhail Rudenko };
15232a437dbSMikhail Rudenko 
15332a437dbSMikhail Rudenko /*
15432a437dbSMikhail Rudenko  * Xclk 24Mhz
15562911feaSMikhail Rudenko  * max_framerate 90fps
15632a437dbSMikhail Rudenko  * mipi_datarate per lane 1008Mbps
15732a437dbSMikhail Rudenko  */
1588fe37e59SMikhail Rudenko static const struct cci_reg_sequence ov4689_2688x1520_regs[] = {
159af9874c9SMikhail Rudenko 	/* System control*/
1608fe37e59SMikhail Rudenko 	{ CCI_REG8(0x0103), 0x01 }, /* SC_CTRL0103 software_reset = 1 */
1618fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3000), 0x20 }, /* SC_CMMN_PAD_OEN0 FSIN_output_enable = 1 */
1628fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3021), 0x03 }, /*
163af9874c9SMikhail Rudenko 				     * SC_CMMN_MISC_CTRL fst_stby_ctr = 0,
164af9874c9SMikhail Rudenko 				     * sleep_no_latch_enable = 0
165af9874c9SMikhail Rudenko 				     */
166af9874c9SMikhail Rudenko 
167af9874c9SMikhail Rudenko 	/* AEC PK */
1688fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3503), 0x04 }, /* AEC_MANUAL gain_input_as_sensor_gain_format = 1 */
169af9874c9SMikhail Rudenko 
170af9874c9SMikhail Rudenko 	/* ADC and analog control*/
1718fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3603), 0x40 },
1728fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3604), 0x02 },
1738fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3609), 0x12 },
1748fe37e59SMikhail Rudenko 	{ CCI_REG8(0x360c), 0x08 },
1758fe37e59SMikhail Rudenko 	{ CCI_REG8(0x360f), 0xe5 },
1768fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3608), 0x8f },
1778fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3611), 0x00 },
1788fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3613), 0xf7 },
1798fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3616), 0x58 },
1808fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3619), 0x99 },
1818fe37e59SMikhail Rudenko 	{ CCI_REG8(0x361b), 0x60 },
1828fe37e59SMikhail Rudenko 	{ CCI_REG8(0x361e), 0x79 },
1838fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3634), 0x10 },
1848fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3635), 0x10 },
1858fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3636), 0x15 },
1868fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3646), 0x86 },
1878fe37e59SMikhail Rudenko 	{ CCI_REG8(0x364a), 0x0b },
188af9874c9SMikhail Rudenko 
189af9874c9SMikhail Rudenko 	/* Sensor control */
1908fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3700), 0x17 },
1918fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3701), 0x22 },
1928fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3703), 0x10 },
1938fe37e59SMikhail Rudenko 	{ CCI_REG8(0x370a), 0x37 },
1948fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3706), 0x63 },
1958fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3709), 0x3c },
1968fe37e59SMikhail Rudenko 	{ CCI_REG8(0x370c), 0x30 },
1978fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3710), 0x24 },
1988fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3720), 0x28 },
1998fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3729), 0x7b },
2008fe37e59SMikhail Rudenko 	{ CCI_REG8(0x372b), 0xbd },
2018fe37e59SMikhail Rudenko 	{ CCI_REG8(0x372c), 0xbc },
2028fe37e59SMikhail Rudenko 	{ CCI_REG8(0x372e), 0x52 },
2038fe37e59SMikhail Rudenko 	{ CCI_REG8(0x373c), 0x0e },
2048fe37e59SMikhail Rudenko 	{ CCI_REG8(0x373e), 0x33 },
2058fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3743), 0x10 },
2068fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3744), 0x88 },
2078fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3745), 0xc0 },
2088fe37e59SMikhail Rudenko 	{ CCI_REG8(0x374c), 0x00 },
2098fe37e59SMikhail Rudenko 	{ CCI_REG8(0x374e), 0x23 },
2108fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3751), 0x7b },
2118fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3753), 0xbd },
2128fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3754), 0xbc },
2138fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3756), 0x52 },
2148fe37e59SMikhail Rudenko 	{ CCI_REG8(0x376b), 0x20 },
2158fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3774), 0x51 },
2168fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3776), 0xbd },
2178fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3777), 0xbd },
2188fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3781), 0x18 },
2198fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3783), 0x25 },
2208fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3798), 0x1b },
221af9874c9SMikhail Rudenko 
222af9874c9SMikhail Rudenko 	/* Timing control */
2238fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3819), 0x01 }, /* VSYNC_END_L vsync_end_point[7:0] = 0x01 */
224af9874c9SMikhail Rudenko 
225af9874c9SMikhail Rudenko 	/* OTP control */
2268fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3d85), 0x36 }, /* OTP_REG85 OTP_power_up_load_setting_enable = 1,
227af9874c9SMikhail Rudenko 				     * OTP_power_up_load_data_enable = 1,
228af9874c9SMikhail Rudenko 				     * OTP_bist_select = 1 (compare with zero)
229af9874c9SMikhail Rudenko 				     */
2308fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3d8c), 0x71 }, /* OTP_SETTING_STT_ADDRESS_H */
2318fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3d8d), 0xcb }, /* OTP_SETTING_STT_ADDRESS_L */
232af9874c9SMikhail Rudenko 
233af9874c9SMikhail Rudenko 	/* BLC registers*/
2348fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4001), 0x40 }, /* DEBUG_MODE */
2358fe37e59SMikhail Rudenko 	{ CCI_REG8(0x401b), 0x00 }, /* DEBUG_MODE */
2368fe37e59SMikhail Rudenko 	{ CCI_REG8(0x401d), 0x00 }, /* DEBUG_MODE */
2378fe37e59SMikhail Rudenko 	{ CCI_REG8(0x401f), 0x00 }, /* DEBUG_MODE */
238af9874c9SMikhail Rudenko 
239af9874c9SMikhail Rudenko 	/* ADC sync control */
2408fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4500), 0x6c }, /* ADC_SYNC_CTRL */
2418fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4503), 0x01 }, /* ADC_SYNC_CTRL */
242af9874c9SMikhail Rudenko 
243af9874c9SMikhail Rudenko 	/* Temperature monitor */
2448fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d00), 0x04 }, /* TPM_CTRL_00 tmp_slope[15:8] = 0x04 */
2458fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d01), 0x42 }, /* TPM_CTRL_01 tmp_slope[7:0] = 0x42 */
2468fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d02), 0xd1 }, /* TPM_CTRL_02 tpm_offset[31:24] = 0xd1 */
2478fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d03), 0x93 }, /* TPM_CTRL_03 tpm_offset[23:16] = 0x93 */
2488fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d04), 0xf5 }, /* TPM_CTRL_04 tpm_offset[15:8]  = 0xf5 */
2498fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d05), 0xc1 }, /* TPM_CTRL_05 tpm_offset[7:0]   = 0xc1 */
250af9874c9SMikhail Rudenko 
251af9874c9SMikhail Rudenko 	/* pre-ISP control */
2528fe37e59SMikhail Rudenko 	{ CCI_REG8(0x5050), 0x0c }, /* DEBUG_MODE */
253af9874c9SMikhail Rudenko 
254af9874c9SMikhail Rudenko 	/* OTP-DPC control */
2558fe37e59SMikhail Rudenko 	{ CCI_REG8(0x5501), 0x10 }, /* OTP_DPC_START_L otp_start_address[7:0] = 0x10 */
2568fe37e59SMikhail Rudenko 	{ CCI_REG8(0x5503), 0x0f }, /* OTP_DPC_END_L otp_end_address[7:0] = 0x0f */
25732a437dbSMikhail Rudenko };
25832a437dbSMikhail Rudenko 
25932a437dbSMikhail Rudenko static const struct ov4689_mode supported_modes[] = {
26032a437dbSMikhail Rudenko 	{
26132a437dbSMikhail Rudenko 		.id = OV4689_MODE_2688_1520,
26232a437dbSMikhail Rudenko 		.width = 2688,
26332a437dbSMikhail Rudenko 		.height = 1520,
26432a437dbSMikhail Rudenko 		.exp_def = 1536,
26562911feaSMikhail Rudenko 		.hts_def = 10296,
26662911feaSMikhail Rudenko 		.hts_min = 3432,
26732a437dbSMikhail Rudenko 		.vts_def = 1554,
26832a437dbSMikhail Rudenko 		.pixel_rate = 480000000,
26932a437dbSMikhail Rudenko 		.reg_list = ov4689_2688x1520_regs,
2708fe37e59SMikhail Rudenko 		.num_regs = ARRAY_SIZE(ov4689_2688x1520_regs),
27132a437dbSMikhail Rudenko 	},
27232a437dbSMikhail Rudenko };
27332a437dbSMikhail Rudenko 
27432a437dbSMikhail Rudenko static const u64 link_freq_menu_items[] = { 504000000 };
27532a437dbSMikhail Rudenko 
27632a437dbSMikhail Rudenko static const char *const ov4689_test_pattern_menu[] = {
27732a437dbSMikhail Rudenko 	"Disabled",
27832a437dbSMikhail Rudenko 	"Vertical Color Bar Type 1",
27932a437dbSMikhail Rudenko 	"Vertical Color Bar Type 2",
28032a437dbSMikhail Rudenko 	"Vertical Color Bar Type 3",
28132a437dbSMikhail Rudenko 	"Vertical Color Bar Type 4"
28232a437dbSMikhail Rudenko };
28332a437dbSMikhail Rudenko 
28432a437dbSMikhail Rudenko /*
28532a437dbSMikhail Rudenko  * These coefficients are based on those used in Rockchip's camera
28632a437dbSMikhail Rudenko  * engine, with minor tweaks for continuity.
28732a437dbSMikhail Rudenko  */
28832a437dbSMikhail Rudenko static const struct ov4689_gain_range ov4689_gain_ranges[] = {
28932a437dbSMikhail Rudenko 	{
29032a437dbSMikhail Rudenko 		.logical_min = 0,
29132a437dbSMikhail Rudenko 		.logical_max = 255,
29232a437dbSMikhail Rudenko 		.offset = 0,
29332a437dbSMikhail Rudenko 		.divider = 1,
29432a437dbSMikhail Rudenko 		.physical_min = 0,
29532a437dbSMikhail Rudenko 		.physical_max = 255,
29632a437dbSMikhail Rudenko 	},
29732a437dbSMikhail Rudenko 	{
29832a437dbSMikhail Rudenko 		.logical_min = 256,
29932a437dbSMikhail Rudenko 		.logical_max = 511,
30032a437dbSMikhail Rudenko 		.offset = 252,
30132a437dbSMikhail Rudenko 		.divider = 2,
30232a437dbSMikhail Rudenko 		.physical_min = 376,
30332a437dbSMikhail Rudenko 		.physical_max = 504,
30432a437dbSMikhail Rudenko 	},
30532a437dbSMikhail Rudenko 	{
30632a437dbSMikhail Rudenko 		.logical_min = 512,
30732a437dbSMikhail Rudenko 		.logical_max = 1023,
30832a437dbSMikhail Rudenko 		.offset = 758,
30932a437dbSMikhail Rudenko 		.divider = 4,
31032a437dbSMikhail Rudenko 		.physical_min = 884,
31132a437dbSMikhail Rudenko 		.physical_max = 1012,
31232a437dbSMikhail Rudenko 	},
31332a437dbSMikhail Rudenko 	{
31432a437dbSMikhail Rudenko 		.logical_min = 1024,
31532a437dbSMikhail Rudenko 		.logical_max = 2047,
31632a437dbSMikhail Rudenko 		.offset = 1788,
31732a437dbSMikhail Rudenko 		.divider = 8,
31832a437dbSMikhail Rudenko 		.physical_min = 1912,
31932a437dbSMikhail Rudenko 		.physical_max = 2047,
32032a437dbSMikhail Rudenko 	},
32132a437dbSMikhail Rudenko };
32232a437dbSMikhail Rudenko 
ov4689_fill_fmt(const struct ov4689_mode * mode,struct v4l2_mbus_framefmt * fmt)32332a437dbSMikhail Rudenko static void ov4689_fill_fmt(const struct ov4689_mode *mode,
32432a437dbSMikhail Rudenko 			    struct v4l2_mbus_framefmt *fmt)
32532a437dbSMikhail Rudenko {
32632a437dbSMikhail Rudenko 	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
32732a437dbSMikhail Rudenko 	fmt->width = mode->width;
32832a437dbSMikhail Rudenko 	fmt->height = mode->height;
32932a437dbSMikhail Rudenko 	fmt->field = V4L2_FIELD_NONE;
33032a437dbSMikhail Rudenko }
33132a437dbSMikhail Rudenko 
ov4689_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)33232a437dbSMikhail Rudenko static int ov4689_set_fmt(struct v4l2_subdev *sd,
33332a437dbSMikhail Rudenko 			  struct v4l2_subdev_state *sd_state,
33432a437dbSMikhail Rudenko 			  struct v4l2_subdev_format *fmt)
33532a437dbSMikhail Rudenko {
33632a437dbSMikhail Rudenko 	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
33732a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
33832a437dbSMikhail Rudenko 
33932a437dbSMikhail Rudenko 	/* only one mode supported for now */
34032a437dbSMikhail Rudenko 	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
34132a437dbSMikhail Rudenko 
34232a437dbSMikhail Rudenko 	return 0;
34332a437dbSMikhail Rudenko }
34432a437dbSMikhail Rudenko 
ov4689_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)34532a437dbSMikhail Rudenko static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
34632a437dbSMikhail Rudenko 				 struct v4l2_subdev_state *sd_state,
34732a437dbSMikhail Rudenko 				 struct v4l2_subdev_mbus_code_enum *code)
34832a437dbSMikhail Rudenko {
34932a437dbSMikhail Rudenko 	if (code->index != 0)
35032a437dbSMikhail Rudenko 		return -EINVAL;
35132a437dbSMikhail Rudenko 	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
35232a437dbSMikhail Rudenko 
35332a437dbSMikhail Rudenko 	return 0;
35432a437dbSMikhail Rudenko }
35532a437dbSMikhail Rudenko 
ov4689_enum_frame_sizes(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)35632a437dbSMikhail Rudenko static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
35732a437dbSMikhail Rudenko 				   struct v4l2_subdev_state *sd_state,
35832a437dbSMikhail Rudenko 				   struct v4l2_subdev_frame_size_enum *fse)
35932a437dbSMikhail Rudenko {
36032a437dbSMikhail Rudenko 	if (fse->index >= ARRAY_SIZE(supported_modes))
36132a437dbSMikhail Rudenko 		return -EINVAL;
36232a437dbSMikhail Rudenko 
36332a437dbSMikhail Rudenko 	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
36432a437dbSMikhail Rudenko 		return -EINVAL;
36532a437dbSMikhail Rudenko 
36632a437dbSMikhail Rudenko 	fse->min_width = supported_modes[fse->index].width;
36732a437dbSMikhail Rudenko 	fse->max_width = supported_modes[fse->index].width;
36832a437dbSMikhail Rudenko 	fse->max_height = supported_modes[fse->index].height;
36932a437dbSMikhail Rudenko 	fse->min_height = supported_modes[fse->index].height;
37032a437dbSMikhail Rudenko 
37132a437dbSMikhail Rudenko 	return 0;
37232a437dbSMikhail Rudenko }
37332a437dbSMikhail Rudenko 
ov4689_enable_test_pattern(struct ov4689 * ov4689,u32 pattern)37432a437dbSMikhail Rudenko static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
37532a437dbSMikhail Rudenko {
37632a437dbSMikhail Rudenko 	u32 val;
37732a437dbSMikhail Rudenko 
37832a437dbSMikhail Rudenko 	if (pattern)
37932a437dbSMikhail Rudenko 		val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
38032a437dbSMikhail Rudenko 	else
38132a437dbSMikhail Rudenko 		val = OV4689_TEST_PATTERN_DISABLE;
38232a437dbSMikhail Rudenko 
3838fe37e59SMikhail Rudenko 	return cci_write(ov4689->regmap, OV4689_REG_TEST_PATTERN,
3848fe37e59SMikhail Rudenko 			 val, NULL);
38532a437dbSMikhail Rudenko }
38632a437dbSMikhail Rudenko 
ov4689_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * state,struct v4l2_subdev_selection * sel)38732a437dbSMikhail Rudenko static int ov4689_get_selection(struct v4l2_subdev *sd,
38832a437dbSMikhail Rudenko 				struct v4l2_subdev_state *state,
38932a437dbSMikhail Rudenko 				struct v4l2_subdev_selection *sel)
39032a437dbSMikhail Rudenko {
39132a437dbSMikhail Rudenko 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
39232a437dbSMikhail Rudenko 		return -EINVAL;
39332a437dbSMikhail Rudenko 
39432a437dbSMikhail Rudenko 	switch (sel->target) {
39532a437dbSMikhail Rudenko 	case V4L2_SEL_TGT_CROP_BOUNDS:
39632a437dbSMikhail Rudenko 		sel->r.top = 0;
39732a437dbSMikhail Rudenko 		sel->r.left = 0;
398bf475d32SMikhail Rudenko 		sel->r.width = OV4689_PIXEL_ARRAY_WIDTH;
399bf475d32SMikhail Rudenko 		sel->r.height = OV4689_PIXEL_ARRAY_HEIGHT;
40032a437dbSMikhail Rudenko 		return 0;
40132a437dbSMikhail Rudenko 	case V4L2_SEL_TGT_CROP:
40232a437dbSMikhail Rudenko 	case V4L2_SEL_TGT_CROP_DEFAULT:
403bf475d32SMikhail Rudenko 		sel->r.top = OV4689_DUMMY_ROWS;
404bf475d32SMikhail Rudenko 		sel->r.left = OV4689_DUMMY_COLUMNS;
405bf475d32SMikhail Rudenko 		sel->r.width =
406bf475d32SMikhail Rudenko 			OV4689_PIXEL_ARRAY_WIDTH - 2 * OV4689_DUMMY_COLUMNS;
407bf475d32SMikhail Rudenko 		sel->r.height =
408bf475d32SMikhail Rudenko 			OV4689_PIXEL_ARRAY_HEIGHT - 2 * OV4689_DUMMY_ROWS;
40932a437dbSMikhail Rudenko 		return 0;
41032a437dbSMikhail Rudenko 	}
41132a437dbSMikhail Rudenko 
41232a437dbSMikhail Rudenko 	return -EINVAL;
41332a437dbSMikhail Rudenko }
41432a437dbSMikhail Rudenko 
ov4689_setup_timings(struct ov4689 * ov4689)415*05e8c95eSMikhail Rudenko static int ov4689_setup_timings(struct ov4689 *ov4689)
416*05e8c95eSMikhail Rudenko {
417*05e8c95eSMikhail Rudenko 	const struct ov4689_mode *mode = ov4689->cur_mode;
418*05e8c95eSMikhail Rudenko 	struct regmap *rm = ov4689->regmap;
419*05e8c95eSMikhail Rudenko 	int ret = 0;
420*05e8c95eSMikhail Rudenko 
421*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_H_CROP_START, 8, &ret);
422*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_V_CROP_START, 8, &ret);
423*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_H_CROP_END, 2711, &ret);
424*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_V_CROP_END, 1531, &ret);
425*05e8c95eSMikhail Rudenko 
426*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_H_OUTPUT_SIZE, mode->width, &ret);
427*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_V_OUTPUT_SIZE, mode->height, &ret);
428*05e8c95eSMikhail Rudenko 
429*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_H_WIN_OFF, 8, &ret);
430*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_V_WIN_OFF, 4, &ret);
431*05e8c95eSMikhail Rudenko 
432*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_VFIFO_CTRL_01, 167, &ret);
433*05e8c95eSMikhail Rudenko 
434*05e8c95eSMikhail Rudenko 	return ret;
435*05e8c95eSMikhail Rudenko }
436*05e8c95eSMikhail Rudenko 
ov4689_setup_blc_anchors(struct ov4689 * ov4689)437*05e8c95eSMikhail Rudenko static int ov4689_setup_blc_anchors(struct ov4689 *ov4689)
438*05e8c95eSMikhail Rudenko {
439*05e8c95eSMikhail Rudenko 	struct regmap *rm = ov4689->regmap;
440*05e8c95eSMikhail Rudenko 	int ret = 0;
441*05e8c95eSMikhail Rudenko 
442*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_ANCHOR_LEFT_START, 16, &ret);
443*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_ANCHOR_LEFT_END, 1999, &ret);
444*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_ANCHOR_RIGHT_START, 2400, &ret);
445*05e8c95eSMikhail Rudenko 	cci_write(rm, OV4689_REG_ANCHOR_RIGHT_END, 2415, &ret);
446*05e8c95eSMikhail Rudenko 
447*05e8c95eSMikhail Rudenko 	return ret;
448*05e8c95eSMikhail Rudenko }
449*05e8c95eSMikhail Rudenko 
ov4689_s_stream(struct v4l2_subdev * sd,int on)45032a437dbSMikhail Rudenko static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
45132a437dbSMikhail Rudenko {
45232a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
4535e2974acSMikhail Rudenko 	struct v4l2_subdev_state *sd_state;
454d015aaafSMikhail Rudenko 	struct device *dev = ov4689->dev;
45532a437dbSMikhail Rudenko 	int ret = 0;
45632a437dbSMikhail Rudenko 
4575e2974acSMikhail Rudenko 	sd_state = v4l2_subdev_lock_and_get_active_state(&ov4689->subdev);
45832a437dbSMikhail Rudenko 
45932a437dbSMikhail Rudenko 	if (on) {
460d015aaafSMikhail Rudenko 		ret = pm_runtime_resume_and_get(dev);
46132a437dbSMikhail Rudenko 		if (ret < 0)
46232a437dbSMikhail Rudenko 			goto unlock_and_return;
46332a437dbSMikhail Rudenko 
4648fe37e59SMikhail Rudenko 		ret = cci_multi_reg_write(ov4689->regmap,
4658fe37e59SMikhail Rudenko 					  ov4689->cur_mode->reg_list,
4668fe37e59SMikhail Rudenko 					  ov4689->cur_mode->num_regs,
4678fe37e59SMikhail Rudenko 					  NULL);
46832a437dbSMikhail Rudenko 		if (ret) {
469d015aaafSMikhail Rudenko 			pm_runtime_put(dev);
47032a437dbSMikhail Rudenko 			goto unlock_and_return;
47132a437dbSMikhail Rudenko 		}
47232a437dbSMikhail Rudenko 
473*05e8c95eSMikhail Rudenko 		ret = ov4689_setup_timings(ov4689);
474*05e8c95eSMikhail Rudenko 		if (ret) {
475*05e8c95eSMikhail Rudenko 			pm_runtime_put(dev);
476*05e8c95eSMikhail Rudenko 			goto unlock_and_return;
477*05e8c95eSMikhail Rudenko 		}
478*05e8c95eSMikhail Rudenko 
479*05e8c95eSMikhail Rudenko 		ret = ov4689_setup_blc_anchors(ov4689);
480*05e8c95eSMikhail Rudenko 		if (ret) {
481*05e8c95eSMikhail Rudenko 			pm_runtime_put(dev);
482*05e8c95eSMikhail Rudenko 			goto unlock_and_return;
483*05e8c95eSMikhail Rudenko 		}
484*05e8c95eSMikhail Rudenko 
48532a437dbSMikhail Rudenko 		ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
48632a437dbSMikhail Rudenko 		if (ret) {
487d015aaafSMikhail Rudenko 			pm_runtime_put(dev);
48832a437dbSMikhail Rudenko 			goto unlock_and_return;
48932a437dbSMikhail Rudenko 		}
49032a437dbSMikhail Rudenko 
4918fe37e59SMikhail Rudenko 		ret = cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
4928fe37e59SMikhail Rudenko 				OV4689_MODE_STREAMING, NULL);
49332a437dbSMikhail Rudenko 		if (ret) {
494d015aaafSMikhail Rudenko 			pm_runtime_put(dev);
49532a437dbSMikhail Rudenko 			goto unlock_and_return;
49632a437dbSMikhail Rudenko 		}
49732a437dbSMikhail Rudenko 	} else {
4988fe37e59SMikhail Rudenko 		cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
4998fe37e59SMikhail Rudenko 			  OV4689_MODE_SW_STANDBY, NULL);
50047e4cf3dSMikhail Rudenko 		pm_runtime_mark_last_busy(dev);
50147e4cf3dSMikhail Rudenko 		pm_runtime_put_autosuspend(dev);
50232a437dbSMikhail Rudenko 	}
50332a437dbSMikhail Rudenko 
50432a437dbSMikhail Rudenko unlock_and_return:
5055e2974acSMikhail Rudenko 	v4l2_subdev_unlock_state(sd_state);
50632a437dbSMikhail Rudenko 
50732a437dbSMikhail Rudenko 	return ret;
50832a437dbSMikhail Rudenko }
50932a437dbSMikhail Rudenko 
51032a437dbSMikhail Rudenko /* Calculate the delay in us by clock rate and clock cycles */
ov4689_cal_delay(struct ov4689 * ov4689,u32 cycles)51132a437dbSMikhail Rudenko static inline u32 ov4689_cal_delay(struct ov4689 *ov4689, u32 cycles)
51232a437dbSMikhail Rudenko {
51332a437dbSMikhail Rudenko 	return DIV_ROUND_UP(cycles * 1000,
51432a437dbSMikhail Rudenko 			    DIV_ROUND_UP(ov4689->clock_rate, 1000));
51532a437dbSMikhail Rudenko }
51632a437dbSMikhail Rudenko 
ov4689_power_on(struct device * dev)51732a437dbSMikhail Rudenko static int __maybe_unused ov4689_power_on(struct device *dev)
51832a437dbSMikhail Rudenko {
51932a437dbSMikhail Rudenko 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
52032a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
52132a437dbSMikhail Rudenko 	u32 delay_us;
52232a437dbSMikhail Rudenko 	int ret;
52332a437dbSMikhail Rudenko 
52432a437dbSMikhail Rudenko 	ret = clk_prepare_enable(ov4689->xvclk);
52532a437dbSMikhail Rudenko 	if (ret < 0) {
52632a437dbSMikhail Rudenko 		dev_err(dev, "Failed to enable xvclk\n");
52732a437dbSMikhail Rudenko 		return ret;
52832a437dbSMikhail Rudenko 	}
52932a437dbSMikhail Rudenko 
53032a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
53132a437dbSMikhail Rudenko 
53232a437dbSMikhail Rudenko 	ret = regulator_bulk_enable(ARRAY_SIZE(ov4689_supply_names),
53332a437dbSMikhail Rudenko 				    ov4689->supplies);
53432a437dbSMikhail Rudenko 	if (ret < 0) {
53532a437dbSMikhail Rudenko 		dev_err(dev, "Failed to enable regulators\n");
53632a437dbSMikhail Rudenko 		goto disable_clk;
53732a437dbSMikhail Rudenko 	}
53832a437dbSMikhail Rudenko 
53932a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
54032a437dbSMikhail Rudenko 	usleep_range(500, 1000);
54132a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
54232a437dbSMikhail Rudenko 
54332a437dbSMikhail Rudenko 	/* 8192 cycles prior to first SCCB transaction */
54432a437dbSMikhail Rudenko 	delay_us = ov4689_cal_delay(ov4689, 8192);
54532a437dbSMikhail Rudenko 	usleep_range(delay_us, delay_us * 2);
54632a437dbSMikhail Rudenko 
54732a437dbSMikhail Rudenko 	return 0;
54832a437dbSMikhail Rudenko 
54932a437dbSMikhail Rudenko disable_clk:
55032a437dbSMikhail Rudenko 	clk_disable_unprepare(ov4689->xvclk);
55132a437dbSMikhail Rudenko 
55232a437dbSMikhail Rudenko 	return ret;
55332a437dbSMikhail Rudenko }
55432a437dbSMikhail Rudenko 
ov4689_power_off(struct device * dev)55532a437dbSMikhail Rudenko static int __maybe_unused ov4689_power_off(struct device *dev)
55632a437dbSMikhail Rudenko {
55732a437dbSMikhail Rudenko 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
55832a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
55932a437dbSMikhail Rudenko 
56032a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
56132a437dbSMikhail Rudenko 	clk_disable_unprepare(ov4689->xvclk);
56232a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
56332a437dbSMikhail Rudenko 	regulator_bulk_disable(ARRAY_SIZE(ov4689_supply_names),
56432a437dbSMikhail Rudenko 			       ov4689->supplies);
56532a437dbSMikhail Rudenko 	return 0;
56632a437dbSMikhail Rudenko }
56732a437dbSMikhail Rudenko 
ov4689_init_state(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)5685e2974acSMikhail Rudenko static int ov4689_init_state(struct v4l2_subdev *sd,
5695e2974acSMikhail Rudenko 			     struct v4l2_subdev_state *sd_state)
57032a437dbSMikhail Rudenko {
5715e2974acSMikhail Rudenko 	struct v4l2_mbus_framefmt *fmt =
5725e2974acSMikhail Rudenko 		v4l2_subdev_state_get_format(sd_state, 0);
57332a437dbSMikhail Rudenko 
5745e2974acSMikhail Rudenko 	ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], fmt);
57532a437dbSMikhail Rudenko 
57632a437dbSMikhail Rudenko 	return 0;
57732a437dbSMikhail Rudenko }
57832a437dbSMikhail Rudenko 
57932a437dbSMikhail Rudenko static const struct dev_pm_ops ov4689_pm_ops = {
58032a437dbSMikhail Rudenko 	SET_RUNTIME_PM_OPS(ov4689_power_off, ov4689_power_on, NULL)
58132a437dbSMikhail Rudenko };
58232a437dbSMikhail Rudenko 
58332a437dbSMikhail Rudenko static const struct v4l2_subdev_video_ops ov4689_video_ops = {
58432a437dbSMikhail Rudenko 	.s_stream = ov4689_s_stream,
58532a437dbSMikhail Rudenko };
58632a437dbSMikhail Rudenko 
58732a437dbSMikhail Rudenko static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
58832a437dbSMikhail Rudenko 	.enum_mbus_code = ov4689_enum_mbus_code,
58932a437dbSMikhail Rudenko 	.enum_frame_size = ov4689_enum_frame_sizes,
5905e2974acSMikhail Rudenko 	.get_fmt = v4l2_subdev_get_fmt,
59132a437dbSMikhail Rudenko 	.set_fmt = ov4689_set_fmt,
59232a437dbSMikhail Rudenko 	.get_selection = ov4689_get_selection,
59332a437dbSMikhail Rudenko };
59432a437dbSMikhail Rudenko 
5955e2974acSMikhail Rudenko static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
5965e2974acSMikhail Rudenko 	.init_state = ov4689_init_state,
5975e2974acSMikhail Rudenko };
5985e2974acSMikhail Rudenko 
59932a437dbSMikhail Rudenko static const struct v4l2_subdev_ops ov4689_subdev_ops = {
60032a437dbSMikhail Rudenko 	.video = &ov4689_video_ops,
60132a437dbSMikhail Rudenko 	.pad = &ov4689_pad_ops,
60232a437dbSMikhail Rudenko };
60332a437dbSMikhail Rudenko 
60432a437dbSMikhail Rudenko /*
60532a437dbSMikhail Rudenko  * Map userspace (logical) gain to sensor (physical) gain using
60632a437dbSMikhail Rudenko  * ov4689_gain_ranges table.
60732a437dbSMikhail Rudenko  */
ov4689_map_gain(struct ov4689 * ov4689,int logical_gain,int * result)60832a437dbSMikhail Rudenko static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result)
60932a437dbSMikhail Rudenko {
61032a437dbSMikhail Rudenko 	const struct ov4689_gain_range *range;
61132a437dbSMikhail Rudenko 	unsigned int n;
61232a437dbSMikhail Rudenko 
61332a437dbSMikhail Rudenko 	for (n = 0; n < ARRAY_SIZE(ov4689_gain_ranges); n++) {
61432a437dbSMikhail Rudenko 		if (logical_gain >= ov4689_gain_ranges[n].logical_min &&
6157336c54aSMikhail Rudenko 		    logical_gain <= ov4689_gain_ranges[n].logical_max)
61632a437dbSMikhail Rudenko 			break;
61732a437dbSMikhail Rudenko 	}
61832a437dbSMikhail Rudenko 
61932a437dbSMikhail Rudenko 	if (n == ARRAY_SIZE(ov4689_gain_ranges)) {
620d015aaafSMikhail Rudenko 		dev_warn_ratelimited(ov4689->dev,
621d015aaafSMikhail Rudenko 				     "no mapping found for gain %d\n",
62232a437dbSMikhail Rudenko 				     logical_gain);
62332a437dbSMikhail Rudenko 		return -EINVAL;
62432a437dbSMikhail Rudenko 	}
62532a437dbSMikhail Rudenko 
62632a437dbSMikhail Rudenko 	range = &ov4689_gain_ranges[n];
62732a437dbSMikhail Rudenko 
62832a437dbSMikhail Rudenko 	*result = clamp(range->offset + (logical_gain) / range->divider,
62932a437dbSMikhail Rudenko 			range->physical_min, range->physical_max);
63032a437dbSMikhail Rudenko 	return 0;
63132a437dbSMikhail Rudenko }
63232a437dbSMikhail Rudenko 
ov4689_set_ctrl(struct v4l2_ctrl * ctrl)63332a437dbSMikhail Rudenko static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
63432a437dbSMikhail Rudenko {
63532a437dbSMikhail Rudenko 	struct ov4689 *ov4689 =
63632a437dbSMikhail Rudenko 		container_of(ctrl->handler, struct ov4689, ctrl_handler);
637d1e90c25SMikhail Rudenko 	struct regmap *regmap = ov4689->regmap;
638d015aaafSMikhail Rudenko 	struct device *dev = ov4689->dev;
6398fe37e59SMikhail Rudenko 	int sensor_gain = 0;
64032a437dbSMikhail Rudenko 	s64 max_expo;
641d1e90c25SMikhail Rudenko 	int ret = 0;
64232a437dbSMikhail Rudenko 
64332a437dbSMikhail Rudenko 	/* Propagate change of current control to all related controls */
64432a437dbSMikhail Rudenko 	switch (ctrl->id) {
64532a437dbSMikhail Rudenko 	case V4L2_CID_VBLANK:
64632a437dbSMikhail Rudenko 		/* Update max exposure while meeting expected vblanking */
64732a437dbSMikhail Rudenko 		max_expo = ov4689->cur_mode->height + ctrl->val - 4;
64832a437dbSMikhail Rudenko 		__v4l2_ctrl_modify_range(ov4689->exposure,
64932a437dbSMikhail Rudenko 					 ov4689->exposure->minimum, max_expo,
65032a437dbSMikhail Rudenko 					 ov4689->exposure->step,
65132a437dbSMikhail Rudenko 					 ov4689->exposure->default_value);
65232a437dbSMikhail Rudenko 		break;
65332a437dbSMikhail Rudenko 	}
65432a437dbSMikhail Rudenko 
655d015aaafSMikhail Rudenko 	if (!pm_runtime_get_if_in_use(dev))
65632a437dbSMikhail Rudenko 		return 0;
65732a437dbSMikhail Rudenko 
65832a437dbSMikhail Rudenko 	switch (ctrl->id) {
65932a437dbSMikhail Rudenko 	case V4L2_CID_EXPOSURE:
66061198ad4SMikhail Rudenko 		/* 4 least significant bits of exposure are fractional part */
661d1e90c25SMikhail Rudenko 		cci_write(regmap, OV4689_REG_EXPOSURE, ctrl->val << 4, &ret);
66232a437dbSMikhail Rudenko 		break;
66332a437dbSMikhail Rudenko 	case V4L2_CID_ANALOGUE_GAIN:
66432a437dbSMikhail Rudenko 		ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain);
665d1e90c25SMikhail Rudenko 		cci_write(regmap, OV4689_REG_GAIN, sensor_gain, &ret);
66632a437dbSMikhail Rudenko 		break;
66732a437dbSMikhail Rudenko 	case V4L2_CID_VBLANK:
668d1e90c25SMikhail Rudenko 		cci_write(regmap, OV4689_REG_VTS,
669d1e90c25SMikhail Rudenko 			  ctrl->val + ov4689->cur_mode->height, &ret);
67032a437dbSMikhail Rudenko 		break;
67132a437dbSMikhail Rudenko 	case V4L2_CID_TEST_PATTERN:
67232a437dbSMikhail Rudenko 		ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
67332a437dbSMikhail Rudenko 		break;
67462911feaSMikhail Rudenko 	case V4L2_CID_HBLANK:
67562911feaSMikhail Rudenko 		cci_write(regmap, OV4689_REG_HTS,
67662911feaSMikhail Rudenko 			  (ctrl->val + ov4689->cur_mode->width) /
67762911feaSMikhail Rudenko 			  OV4689_HTS_DIVIDER, &ret);
67862911feaSMikhail Rudenko 		break;
679ec43d634SMikhail Rudenko 	case V4L2_CID_VFLIP:
680ec43d634SMikhail Rudenko 		cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT1,
681ec43d634SMikhail Rudenko 				OV4689_TIMING_FLIP_MASK,
682ec43d634SMikhail Rudenko 				ctrl->val ? OV4689_TIMING_FLIP_BOTH : 0, &ret);
683ec43d634SMikhail Rudenko 		break;
684ec43d634SMikhail Rudenko 	case V4L2_CID_HFLIP:
685ec43d634SMikhail Rudenko 		cci_update_bits(regmap, OV4689_REG_TIMING_FORMAT2,
686ec43d634SMikhail Rudenko 				OV4689_TIMING_FLIP_MASK,
687ec43d634SMikhail Rudenko 				ctrl->val ? 0 : OV4689_TIMING_FLIP_BOTH, &ret);
688ec43d634SMikhail Rudenko 		break;
689398eca19SMikhail Rudenko 	case V4L2_CID_DIGITAL_GAIN:
690398eca19SMikhail Rudenko 		cci_write(regmap, OV4689_REG_DIG_GAIN, ctrl->val, &ret);
691398eca19SMikhail Rudenko 		break;
6926b3ad3bcSMikhail Rudenko 	case V4L2_CID_RED_BALANCE:
6936b3ad3bcSMikhail Rudenko 		cci_write(regmap, OV4689_REG_WB_GAIN_RED, ctrl->val, &ret);
6946b3ad3bcSMikhail Rudenko 		break;
6956b3ad3bcSMikhail Rudenko 	case V4L2_CID_BLUE_BALANCE:
6966b3ad3bcSMikhail Rudenko 		cci_write(regmap, OV4689_REG_WB_GAIN_BLUE, ctrl->val, &ret);
6976b3ad3bcSMikhail Rudenko 		break;
69832a437dbSMikhail Rudenko 	default:
699d015aaafSMikhail Rudenko 		dev_warn(dev, "%s Unhandled id:0x%x, val:0x%x\n",
70032a437dbSMikhail Rudenko 			 __func__, ctrl->id, ctrl->val);
70132a437dbSMikhail Rudenko 		ret = -EINVAL;
70232a437dbSMikhail Rudenko 		break;
70332a437dbSMikhail Rudenko 	}
70432a437dbSMikhail Rudenko 
70547e4cf3dSMikhail Rudenko 	pm_runtime_mark_last_busy(dev);
70647e4cf3dSMikhail Rudenko 	pm_runtime_put_autosuspend(dev);
70732a437dbSMikhail Rudenko 
70832a437dbSMikhail Rudenko 	return ret;
70932a437dbSMikhail Rudenko }
71032a437dbSMikhail Rudenko 
71132a437dbSMikhail Rudenko static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
71232a437dbSMikhail Rudenko 	.s_ctrl = ov4689_set_ctrl,
71332a437dbSMikhail Rudenko };
71432a437dbSMikhail Rudenko 
ov4689_initialize_controls(struct ov4689 * ov4689)71532a437dbSMikhail Rudenko static int ov4689_initialize_controls(struct ov4689 *ov4689)
71632a437dbSMikhail Rudenko {
71732a437dbSMikhail Rudenko 	struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
71832a437dbSMikhail Rudenko 	struct v4l2_fwnode_device_properties props;
71932a437dbSMikhail Rudenko 	struct v4l2_ctrl_handler *handler;
72032a437dbSMikhail Rudenko 	const struct ov4689_mode *mode;
72132a437dbSMikhail Rudenko 	s64 exposure_max, vblank_def;
72262911feaSMikhail Rudenko 	s64 hblank_def, hblank_min;
72332a437dbSMikhail Rudenko 	struct v4l2_ctrl *ctrl;
72432a437dbSMikhail Rudenko 	int ret;
72532a437dbSMikhail Rudenko 
72632a437dbSMikhail Rudenko 	handler = &ov4689->ctrl_handler;
72732a437dbSMikhail Rudenko 	mode = ov4689->cur_mode;
7286b3ad3bcSMikhail Rudenko 	ret = v4l2_ctrl_handler_init(handler, 15);
72932a437dbSMikhail Rudenko 	if (ret)
73032a437dbSMikhail Rudenko 		return ret;
73132a437dbSMikhail Rudenko 
73232a437dbSMikhail Rudenko 	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
73332a437dbSMikhail Rudenko 				      link_freq_menu_items);
73432a437dbSMikhail Rudenko 	if (ctrl)
73532a437dbSMikhail Rudenko 		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
73632a437dbSMikhail Rudenko 
73732a437dbSMikhail Rudenko 	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0,
73832a437dbSMikhail Rudenko 			  mode->pixel_rate, 1, mode->pixel_rate);
73932a437dbSMikhail Rudenko 
74062911feaSMikhail Rudenko 	hblank_def = mode->hts_def - mode->width;
74162911feaSMikhail Rudenko 	hblank_min = mode->hts_min - mode->width;
74262911feaSMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HBLANK,
74362911feaSMikhail Rudenko 			  hblank_min, OV4689_HTS_MAX - mode->width,
74462911feaSMikhail Rudenko 			  OV4689_HTS_DIVIDER, hblank_def);
74532a437dbSMikhail Rudenko 
74632a437dbSMikhail Rudenko 	vblank_def = mode->vts_def - mode->height;
74732a437dbSMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
74832a437dbSMikhail Rudenko 			  vblank_def, OV4689_VTS_MAX - mode->height, 1,
74932a437dbSMikhail Rudenko 			  vblank_def);
75032a437dbSMikhail Rudenko 
75132a437dbSMikhail Rudenko 	exposure_max = mode->vts_def - 4;
75232a437dbSMikhail Rudenko 	ov4689->exposure =
75332a437dbSMikhail Rudenko 		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
75432a437dbSMikhail Rudenko 				  OV4689_EXPOSURE_MIN, exposure_max,
75532a437dbSMikhail Rudenko 				  OV4689_EXPOSURE_STEP, mode->exp_def);
75632a437dbSMikhail Rudenko 
75732a437dbSMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
75832a437dbSMikhail Rudenko 			  ov4689_gain_ranges[0].logical_min,
75932a437dbSMikhail Rudenko 			  ov4689_gain_ranges[ARRAY_SIZE(ov4689_gain_ranges) - 1]
76032a437dbSMikhail Rudenko 				  .logical_max,
76132a437dbSMikhail Rudenko 			  OV4689_GAIN_STEP, OV4689_GAIN_DEFAULT);
76232a437dbSMikhail Rudenko 
76332a437dbSMikhail Rudenko 	v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
76432a437dbSMikhail Rudenko 				     V4L2_CID_TEST_PATTERN,
76532a437dbSMikhail Rudenko 				     ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
76632a437dbSMikhail Rudenko 				     0, 0, ov4689_test_pattern_menu);
76732a437dbSMikhail Rudenko 
768ec43d634SMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
769ec43d634SMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
770ec43d634SMikhail Rudenko 
771398eca19SMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
772398eca19SMikhail Rudenko 			  OV4689_DIG_GAIN_MIN, OV4689_DIG_GAIN_MAX,
773398eca19SMikhail Rudenko 			  OV4689_DIG_GAIN_STEP, OV4689_DIG_GAIN_DEFAULT);
774398eca19SMikhail Rudenko 
7756b3ad3bcSMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_RED_BALANCE,
7766b3ad3bcSMikhail Rudenko 			  OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX,
7776b3ad3bcSMikhail Rudenko 			  OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT);
7786b3ad3bcSMikhail Rudenko 
7796b3ad3bcSMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_BLUE_BALANCE,
7806b3ad3bcSMikhail Rudenko 			  OV4689_WB_GAIN_MIN, OV4689_WB_GAIN_MAX,
7816b3ad3bcSMikhail Rudenko 			  OV4689_WB_GAIN_STEP, OV4689_WB_GAIN_DEFAULT);
7826b3ad3bcSMikhail Rudenko 
78332a437dbSMikhail Rudenko 	if (handler->error) {
78432a437dbSMikhail Rudenko 		ret = handler->error;
785d015aaafSMikhail Rudenko 		dev_err(ov4689->dev, "Failed to init controls(%d)\n", ret);
78632a437dbSMikhail Rudenko 		goto err_free_handler;
78732a437dbSMikhail Rudenko 	}
78832a437dbSMikhail Rudenko 
78932a437dbSMikhail Rudenko 	ret = v4l2_fwnode_device_parse(&client->dev, &props);
79032a437dbSMikhail Rudenko 	if (ret)
79132a437dbSMikhail Rudenko 		goto err_free_handler;
79232a437dbSMikhail Rudenko 
79332a437dbSMikhail Rudenko 	ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
79432a437dbSMikhail Rudenko 					      &props);
79532a437dbSMikhail Rudenko 	if (ret)
79632a437dbSMikhail Rudenko 		goto err_free_handler;
79732a437dbSMikhail Rudenko 
79832a437dbSMikhail Rudenko 	ov4689->subdev.ctrl_handler = handler;
79932a437dbSMikhail Rudenko 
80032a437dbSMikhail Rudenko 	return 0;
80132a437dbSMikhail Rudenko 
80232a437dbSMikhail Rudenko err_free_handler:
80332a437dbSMikhail Rudenko 	v4l2_ctrl_handler_free(handler);
80432a437dbSMikhail Rudenko 
80532a437dbSMikhail Rudenko 	return ret;
80632a437dbSMikhail Rudenko }
80732a437dbSMikhail Rudenko 
ov4689_check_sensor_id(struct ov4689 * ov4689,struct i2c_client * client)80832a437dbSMikhail Rudenko static int ov4689_check_sensor_id(struct ov4689 *ov4689,
80932a437dbSMikhail Rudenko 				  struct i2c_client *client)
81032a437dbSMikhail Rudenko {
811d015aaafSMikhail Rudenko 	struct device *dev = ov4689->dev;
8128fe37e59SMikhail Rudenko 	u64 id = 0;
81332a437dbSMikhail Rudenko 	int ret;
81432a437dbSMikhail Rudenko 
8158fe37e59SMikhail Rudenko 	ret = cci_read(ov4689->regmap, OV4689_REG_CHIP_ID, &id, NULL);
81632a437dbSMikhail Rudenko 	if (ret) {
81732a437dbSMikhail Rudenko 		dev_err(dev, "Cannot read sensor ID\n");
81832a437dbSMikhail Rudenko 		return ret;
81932a437dbSMikhail Rudenko 	}
82032a437dbSMikhail Rudenko 
82132a437dbSMikhail Rudenko 	if (id != CHIP_ID) {
8228fe37e59SMikhail Rudenko 		dev_err(dev, "Unexpected sensor ID %06llx, expected %06x\n",
82332a437dbSMikhail Rudenko 			id, CHIP_ID);
82432a437dbSMikhail Rudenko 		return -ENODEV;
82532a437dbSMikhail Rudenko 	}
82632a437dbSMikhail Rudenko 
82732a437dbSMikhail Rudenko 	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
82832a437dbSMikhail Rudenko 
82932a437dbSMikhail Rudenko 	return 0;
83032a437dbSMikhail Rudenko }
83132a437dbSMikhail Rudenko 
ov4689_configure_regulators(struct ov4689 * ov4689)83232a437dbSMikhail Rudenko static int ov4689_configure_regulators(struct ov4689 *ov4689)
83332a437dbSMikhail Rudenko {
83432a437dbSMikhail Rudenko 	unsigned int i;
83532a437dbSMikhail Rudenko 
8367336c54aSMikhail Rudenko 	for (i = 0; i < ARRAY_SIZE(ov4689_supply_names); i++)
83732a437dbSMikhail Rudenko 		ov4689->supplies[i].supply = ov4689_supply_names[i];
83832a437dbSMikhail Rudenko 
839d015aaafSMikhail Rudenko 	return devm_regulator_bulk_get(ov4689->dev,
8407336c54aSMikhail Rudenko 				       ARRAY_SIZE(ov4689_supply_names),
84132a437dbSMikhail Rudenko 				       ov4689->supplies);
84232a437dbSMikhail Rudenko }
84332a437dbSMikhail Rudenko 
ov4689_check_link_frequency(struct v4l2_fwnode_endpoint * ep)84432a437dbSMikhail Rudenko static u64 ov4689_check_link_frequency(struct v4l2_fwnode_endpoint *ep)
84532a437dbSMikhail Rudenko {
84632a437dbSMikhail Rudenko 	const u64 *freqs = link_freq_menu_items;
84732a437dbSMikhail Rudenko 	unsigned int i, j;
84832a437dbSMikhail Rudenko 
8497336c54aSMikhail Rudenko 	for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
85032a437dbSMikhail Rudenko 		for (j = 0; j < ep->nr_of_link_frequencies; j++)
85132a437dbSMikhail Rudenko 			if (freqs[i] == ep->link_frequencies[j])
85232a437dbSMikhail Rudenko 				return freqs[i];
85332a437dbSMikhail Rudenko 	}
85432a437dbSMikhail Rudenko 
85532a437dbSMikhail Rudenko 	return 0;
85632a437dbSMikhail Rudenko }
85732a437dbSMikhail Rudenko 
ov4689_check_hwcfg(struct device * dev)85832a437dbSMikhail Rudenko static int ov4689_check_hwcfg(struct device *dev)
85932a437dbSMikhail Rudenko {
86032a437dbSMikhail Rudenko 	struct fwnode_handle *fwnode = dev_fwnode(dev);
86132a437dbSMikhail Rudenko 	struct v4l2_fwnode_endpoint bus_cfg = {
86232a437dbSMikhail Rudenko 		.bus_type = V4L2_MBUS_CSI2_DPHY,
86332a437dbSMikhail Rudenko 	};
86432a437dbSMikhail Rudenko 	struct fwnode_handle *endpoint;
86532a437dbSMikhail Rudenko 	int ret;
86632a437dbSMikhail Rudenko 
86732a437dbSMikhail Rudenko 	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
86832a437dbSMikhail Rudenko 	if (!endpoint)
86932a437dbSMikhail Rudenko 		return -EINVAL;
87032a437dbSMikhail Rudenko 
87132a437dbSMikhail Rudenko 	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
87232a437dbSMikhail Rudenko 	fwnode_handle_put(endpoint);
87332a437dbSMikhail Rudenko 	if (ret)
87432a437dbSMikhail Rudenko 		return ret;
87532a437dbSMikhail Rudenko 
87632a437dbSMikhail Rudenko 	if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV4689_LANES) {
87732a437dbSMikhail Rudenko 		dev_err(dev, "Only a 4-lane CSI2 config is supported");
87832a437dbSMikhail Rudenko 		ret = -EINVAL;
87932a437dbSMikhail Rudenko 		goto out_free_bus_cfg;
88032a437dbSMikhail Rudenko 	}
88132a437dbSMikhail Rudenko 
88232a437dbSMikhail Rudenko 	if (!ov4689_check_link_frequency(&bus_cfg)) {
88332a437dbSMikhail Rudenko 		dev_err(dev, "No supported link frequency found\n");
88432a437dbSMikhail Rudenko 		ret = -EINVAL;
88532a437dbSMikhail Rudenko 	}
88632a437dbSMikhail Rudenko 
88732a437dbSMikhail Rudenko out_free_bus_cfg:
88832a437dbSMikhail Rudenko 	v4l2_fwnode_endpoint_free(&bus_cfg);
88932a437dbSMikhail Rudenko 
89032a437dbSMikhail Rudenko 	return ret;
89132a437dbSMikhail Rudenko }
89232a437dbSMikhail Rudenko 
ov4689_probe(struct i2c_client * client)89332a437dbSMikhail Rudenko static int ov4689_probe(struct i2c_client *client)
89432a437dbSMikhail Rudenko {
89532a437dbSMikhail Rudenko 	struct device *dev = &client->dev;
89632a437dbSMikhail Rudenko 	struct v4l2_subdev *sd;
89732a437dbSMikhail Rudenko 	struct ov4689 *ov4689;
89832a437dbSMikhail Rudenko 	int ret;
89932a437dbSMikhail Rudenko 
90032a437dbSMikhail Rudenko 	ret = ov4689_check_hwcfg(dev);
90132a437dbSMikhail Rudenko 	if (ret)
90232a437dbSMikhail Rudenko 		return ret;
90332a437dbSMikhail Rudenko 
90432a437dbSMikhail Rudenko 	ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
90532a437dbSMikhail Rudenko 	if (!ov4689)
90632a437dbSMikhail Rudenko 		return -ENOMEM;
90732a437dbSMikhail Rudenko 
908d015aaafSMikhail Rudenko 	ov4689->dev = dev;
909d015aaafSMikhail Rudenko 
91032a437dbSMikhail Rudenko 	ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520];
91132a437dbSMikhail Rudenko 
91232a437dbSMikhail Rudenko 	ov4689->xvclk = devm_clk_get_optional(dev, NULL);
91332a437dbSMikhail Rudenko 	if (IS_ERR(ov4689->xvclk))
91432a437dbSMikhail Rudenko 		return dev_err_probe(dev, PTR_ERR(ov4689->xvclk),
91532a437dbSMikhail Rudenko 				     "Failed to get external clock\n");
91632a437dbSMikhail Rudenko 
91732a437dbSMikhail Rudenko 	if (!ov4689->xvclk) {
91832a437dbSMikhail Rudenko 		dev_dbg(dev,
91932a437dbSMikhail Rudenko 			"No clock provided, using clock-frequency property\n");
92032a437dbSMikhail Rudenko 		device_property_read_u32(dev, "clock-frequency",
92132a437dbSMikhail Rudenko 					 &ov4689->clock_rate);
92232a437dbSMikhail Rudenko 	} else {
92332a437dbSMikhail Rudenko 		ov4689->clock_rate = clk_get_rate(ov4689->xvclk);
92432a437dbSMikhail Rudenko 	}
92532a437dbSMikhail Rudenko 
92632a437dbSMikhail Rudenko 	if (ov4689->clock_rate != OV4689_XVCLK_FREQ) {
92732a437dbSMikhail Rudenko 		dev_err(dev,
92832a437dbSMikhail Rudenko 			"External clock rate mismatch: got %d Hz, expected %d Hz\n",
92932a437dbSMikhail Rudenko 			ov4689->clock_rate, OV4689_XVCLK_FREQ);
93032a437dbSMikhail Rudenko 		return -EINVAL;
93132a437dbSMikhail Rudenko 	}
93232a437dbSMikhail Rudenko 
9338fe37e59SMikhail Rudenko 	ov4689->regmap = devm_cci_regmap_init_i2c(client, 16);
9348fe37e59SMikhail Rudenko 	if (IS_ERR(ov4689->regmap)) {
9358fe37e59SMikhail Rudenko 		ret = PTR_ERR(ov4689->regmap);
9368fe37e59SMikhail Rudenko 		dev_err(dev, "failed to initialize CCI: %d\n", ret);
9378fe37e59SMikhail Rudenko 		return ret;
9388fe37e59SMikhail Rudenko 	}
9398fe37e59SMikhail Rudenko 
94032a437dbSMikhail Rudenko 	ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset",
94132a437dbSMikhail Rudenko 						     GPIOD_OUT_LOW);
94232a437dbSMikhail Rudenko 	if (IS_ERR(ov4689->reset_gpio)) {
94332a437dbSMikhail Rudenko 		dev_err(dev, "Failed to get reset-gpios\n");
94432a437dbSMikhail Rudenko 		return PTR_ERR(ov4689->reset_gpio);
94532a437dbSMikhail Rudenko 	}
94632a437dbSMikhail Rudenko 
94732a437dbSMikhail Rudenko 	ov4689->pwdn_gpio = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_LOW);
94832a437dbSMikhail Rudenko 	if (IS_ERR(ov4689->pwdn_gpio)) {
94932a437dbSMikhail Rudenko 		dev_err(dev, "Failed to get pwdn-gpios\n");
95032a437dbSMikhail Rudenko 		return PTR_ERR(ov4689->pwdn_gpio);
95132a437dbSMikhail Rudenko 	}
95232a437dbSMikhail Rudenko 
95332a437dbSMikhail Rudenko 	ret = ov4689_configure_regulators(ov4689);
95432a437dbSMikhail Rudenko 	if (ret)
95532a437dbSMikhail Rudenko 		return dev_err_probe(dev, ret,
95632a437dbSMikhail Rudenko 				     "Failed to get power regulators\n");
95732a437dbSMikhail Rudenko 
95832a437dbSMikhail Rudenko 	sd = &ov4689->subdev;
95932a437dbSMikhail Rudenko 	v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
9605e2974acSMikhail Rudenko 	sd->internal_ops = &ov4689_internal_ops;
9615e2974acSMikhail Rudenko 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
96232a437dbSMikhail Rudenko 	ret = ov4689_initialize_controls(ov4689);
9635e2974acSMikhail Rudenko 	if (ret) {
9645e2974acSMikhail Rudenko 		dev_err(dev, "Failed to initialize controls\n");
9655e2974acSMikhail Rudenko 		return ret;
9665e2974acSMikhail Rudenko 	}
96732a437dbSMikhail Rudenko 
96832a437dbSMikhail Rudenko 	ret = ov4689_power_on(dev);
96932a437dbSMikhail Rudenko 	if (ret)
97032a437dbSMikhail Rudenko 		goto err_free_handler;
97132a437dbSMikhail Rudenko 
97232a437dbSMikhail Rudenko 	ret = ov4689_check_sensor_id(ov4689, client);
97332a437dbSMikhail Rudenko 	if (ret)
97432a437dbSMikhail Rudenko 		goto err_power_off;
97532a437dbSMikhail Rudenko 
97632a437dbSMikhail Rudenko 
97732a437dbSMikhail Rudenko 	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
9785e2974acSMikhail Rudenko 	ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
97932a437dbSMikhail Rudenko 	ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
98032a437dbSMikhail Rudenko 	if (ret < 0)
98132a437dbSMikhail Rudenko 		goto err_power_off;
98232a437dbSMikhail Rudenko 
9835e2974acSMikhail Rudenko 	sd->state_lock = ov4689->ctrl_handler.lock;
9845e2974acSMikhail Rudenko 	ret = v4l2_subdev_init_finalize(sd);
9855e2974acSMikhail Rudenko 	if (ret) {
9865e2974acSMikhail Rudenko 		dev_err(dev, "Could not register v4l2 device\n");
9875e2974acSMikhail Rudenko 		goto err_clean_entity;
9885e2974acSMikhail Rudenko 	}
9895e2974acSMikhail Rudenko 
99032a437dbSMikhail Rudenko 	pm_runtime_set_active(dev);
99147e4cf3dSMikhail Rudenko 	pm_runtime_get_noresume(dev);
99232a437dbSMikhail Rudenko 	pm_runtime_enable(dev);
99347e4cf3dSMikhail Rudenko 	pm_runtime_set_autosuspend_delay(dev, 1000);
99447e4cf3dSMikhail Rudenko 	pm_runtime_use_autosuspend(dev);
99532a437dbSMikhail Rudenko 
99648f3197aSMikhail Rudenko 	ret = v4l2_async_register_subdev_sensor(sd);
99748f3197aSMikhail Rudenko 	if (ret) {
99848f3197aSMikhail Rudenko 		dev_err(dev, "v4l2 async register subdev failed\n");
99948f3197aSMikhail Rudenko 		goto err_clean_subdev_pm;
100048f3197aSMikhail Rudenko 	}
100148f3197aSMikhail Rudenko 
100247e4cf3dSMikhail Rudenko 	pm_runtime_mark_last_busy(dev);
100347e4cf3dSMikhail Rudenko 	pm_runtime_put_autosuspend(dev);
100447e4cf3dSMikhail Rudenko 
100532a437dbSMikhail Rudenko 	return 0;
100632a437dbSMikhail Rudenko 
100748f3197aSMikhail Rudenko err_clean_subdev_pm:
100848f3197aSMikhail Rudenko 	pm_runtime_disable(dev);
100947e4cf3dSMikhail Rudenko 	pm_runtime_put_noidle(dev);
10105e2974acSMikhail Rudenko 	v4l2_subdev_cleanup(sd);
101132a437dbSMikhail Rudenko err_clean_entity:
101232a437dbSMikhail Rudenko 	media_entity_cleanup(&sd->entity);
101332a437dbSMikhail Rudenko err_power_off:
101432a437dbSMikhail Rudenko 	ov4689_power_off(dev);
101532a437dbSMikhail Rudenko err_free_handler:
101632a437dbSMikhail Rudenko 	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
101732a437dbSMikhail Rudenko 
101832a437dbSMikhail Rudenko 	return ret;
101932a437dbSMikhail Rudenko }
102032a437dbSMikhail Rudenko 
ov4689_remove(struct i2c_client * client)102132a437dbSMikhail Rudenko static void ov4689_remove(struct i2c_client *client)
102232a437dbSMikhail Rudenko {
102332a437dbSMikhail Rudenko 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
102432a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
102532a437dbSMikhail Rudenko 
102632a437dbSMikhail Rudenko 	v4l2_async_unregister_subdev(sd);
102732a437dbSMikhail Rudenko 	media_entity_cleanup(&sd->entity);
10285e2974acSMikhail Rudenko 	v4l2_subdev_cleanup(sd);
102932a437dbSMikhail Rudenko 	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
103032a437dbSMikhail Rudenko 
103132a437dbSMikhail Rudenko 	pm_runtime_disable(&client->dev);
103232a437dbSMikhail Rudenko 	if (!pm_runtime_status_suspended(&client->dev))
103332a437dbSMikhail Rudenko 		ov4689_power_off(&client->dev);
103432a437dbSMikhail Rudenko 	pm_runtime_set_suspended(&client->dev);
103532a437dbSMikhail Rudenko }
103632a437dbSMikhail Rudenko 
103732a437dbSMikhail Rudenko static const struct of_device_id ov4689_of_match[] = {
103832a437dbSMikhail Rudenko 	{ .compatible = "ovti,ov4689" },
103932a437dbSMikhail Rudenko 	{},
104032a437dbSMikhail Rudenko };
104132a437dbSMikhail Rudenko MODULE_DEVICE_TABLE(of, ov4689_of_match);
104232a437dbSMikhail Rudenko 
104332a437dbSMikhail Rudenko static struct i2c_driver ov4689_i2c_driver = {
104432a437dbSMikhail Rudenko 	.driver = {
104532a437dbSMikhail Rudenko 		.name = "ov4689",
104632a437dbSMikhail Rudenko 		.pm = &ov4689_pm_ops,
104732a437dbSMikhail Rudenko 		.of_match_table = ov4689_of_match,
104832a437dbSMikhail Rudenko 	},
1049aaeb31c0SUwe Kleine-König 	.probe = ov4689_probe,
105032a437dbSMikhail Rudenko 	.remove	= ov4689_remove,
105132a437dbSMikhail Rudenko };
105232a437dbSMikhail Rudenko 
105332a437dbSMikhail Rudenko module_i2c_driver(ov4689_i2c_driver);
105432a437dbSMikhail Rudenko 
105532a437dbSMikhail Rudenko MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
105632a437dbSMikhail Rudenko MODULE_LICENSE("GPL");
1057