xref: /linux/drivers/media/i2c/ov4689.c (revision 47e4cf3d5aeed6cd4a690d8f30c441807d2834e8)
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 #define OV4689_VTS_MAX			0x7fff
3432a437dbSMikhail Rudenko 
358fe37e59SMikhail Rudenko #define OV4689_REG_GAIN			CCI_REG16(0x3508)
3632a437dbSMikhail Rudenko #define OV4689_GAIN_STEP		1
3732a437dbSMikhail Rudenko #define OV4689_GAIN_DEFAULT		0x80
3832a437dbSMikhail Rudenko 
398fe37e59SMikhail Rudenko #define OV4689_REG_VTS			CCI_REG16(0x380e)
40f3adec37SMikhail Rudenko 
418fe37e59SMikhail Rudenko #define OV4689_REG_TEST_PATTERN		CCI_REG8(0x5040)
4232a437dbSMikhail Rudenko #define OV4689_TEST_PATTERN_ENABLE	0x80
4332a437dbSMikhail Rudenko #define OV4689_TEST_PATTERN_DISABLE	0x0
4432a437dbSMikhail Rudenko 
4532a437dbSMikhail Rudenko #define OV4689_LANES			4
468fe37e59SMikhail Rudenko #define OV4689_XVCLK_FREQ		24000000
4732a437dbSMikhail Rudenko 
4832a437dbSMikhail Rudenko static const char *const ov4689_supply_names[] = {
4932a437dbSMikhail Rudenko 	"avdd", /* Analog power */
5032a437dbSMikhail Rudenko 	"dovdd", /* Digital I/O power */
5132a437dbSMikhail Rudenko 	"dvdd", /* Digital core power */
5232a437dbSMikhail Rudenko };
5332a437dbSMikhail Rudenko 
5432a437dbSMikhail Rudenko enum ov4689_mode_id {
5532a437dbSMikhail Rudenko 	OV4689_MODE_2688_1520 = 0,
5632a437dbSMikhail Rudenko 	OV4689_NUM_MODES,
5732a437dbSMikhail Rudenko };
5832a437dbSMikhail Rudenko 
5932a437dbSMikhail Rudenko struct ov4689_mode {
6032a437dbSMikhail Rudenko 	enum ov4689_mode_id id;
6132a437dbSMikhail Rudenko 	u32 width;
6232a437dbSMikhail Rudenko 	u32 height;
6332a437dbSMikhail Rudenko 	u32 max_fps;
6432a437dbSMikhail Rudenko 	u32 hts_def;
6532a437dbSMikhail Rudenko 	u32 vts_def;
6632a437dbSMikhail Rudenko 	u32 exp_def;
6732a437dbSMikhail Rudenko 	u32 pixel_rate;
6832a437dbSMikhail Rudenko 	u32 sensor_width;
6932a437dbSMikhail Rudenko 	u32 sensor_height;
7032a437dbSMikhail Rudenko 	u32 crop_top;
7132a437dbSMikhail Rudenko 	u32 crop_left;
728fe37e59SMikhail Rudenko 	const struct cci_reg_sequence *reg_list;
738fe37e59SMikhail Rudenko 	unsigned int num_regs;
7432a437dbSMikhail Rudenko };
7532a437dbSMikhail Rudenko 
7632a437dbSMikhail Rudenko struct ov4689 {
77d015aaafSMikhail Rudenko 	struct device *dev;
788fe37e59SMikhail Rudenko 	struct regmap *regmap;
7932a437dbSMikhail Rudenko 	struct clk *xvclk;
8032a437dbSMikhail Rudenko 	struct gpio_desc *reset_gpio;
8132a437dbSMikhail Rudenko 	struct gpio_desc *pwdn_gpio;
8232a437dbSMikhail Rudenko 	struct regulator_bulk_data supplies[ARRAY_SIZE(ov4689_supply_names)];
8332a437dbSMikhail Rudenko 
8432a437dbSMikhail Rudenko 	struct v4l2_subdev subdev;
8532a437dbSMikhail Rudenko 	struct media_pad pad;
8632a437dbSMikhail Rudenko 
8732a437dbSMikhail Rudenko 	u32 clock_rate;
8832a437dbSMikhail Rudenko 
8932a437dbSMikhail Rudenko 	struct v4l2_ctrl_handler ctrl_handler;
9032a437dbSMikhail Rudenko 	struct v4l2_ctrl *exposure;
9132a437dbSMikhail Rudenko 
9232a437dbSMikhail Rudenko 	const struct ov4689_mode *cur_mode;
9332a437dbSMikhail Rudenko };
9432a437dbSMikhail Rudenko 
9532a437dbSMikhail Rudenko #define to_ov4689(sd) container_of(sd, struct ov4689, subdev)
9632a437dbSMikhail Rudenko 
9732a437dbSMikhail Rudenko struct ov4689_gain_range {
9832a437dbSMikhail Rudenko 	u32 logical_min;
9932a437dbSMikhail Rudenko 	u32 logical_max;
10032a437dbSMikhail Rudenko 	u32 offset;
10132a437dbSMikhail Rudenko 	u32 divider;
10232a437dbSMikhail Rudenko 	u32 physical_min;
10332a437dbSMikhail Rudenko 	u32 physical_max;
10432a437dbSMikhail Rudenko };
10532a437dbSMikhail Rudenko 
10632a437dbSMikhail Rudenko /*
10732a437dbSMikhail Rudenko  * Xclk 24Mhz
10832a437dbSMikhail Rudenko  * max_framerate 30fps
10932a437dbSMikhail Rudenko  * mipi_datarate per lane 1008Mbps
11032a437dbSMikhail Rudenko  */
1118fe37e59SMikhail Rudenko static const struct cci_reg_sequence ov4689_2688x1520_regs[] = {
112af9874c9SMikhail Rudenko 	/* System control*/
1138fe37e59SMikhail Rudenko 	{ CCI_REG8(0x0103), 0x01 }, /* SC_CTRL0103 software_reset = 1 */
1148fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3000), 0x20 }, /* SC_CMMN_PAD_OEN0 FSIN_output_enable = 1 */
1158fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3021), 0x03 }, /*
116af9874c9SMikhail Rudenko 				     * SC_CMMN_MISC_CTRL fst_stby_ctr = 0,
117af9874c9SMikhail Rudenko 				     * sleep_no_latch_enable = 0
118af9874c9SMikhail Rudenko 				     */
119af9874c9SMikhail Rudenko 
120af9874c9SMikhail Rudenko 	/* AEC PK */
1218fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3503), 0x04 }, /* AEC_MANUAL gain_input_as_sensor_gain_format = 1 */
1228fe37e59SMikhail Rudenko 	{ CCI_REG8(0x352a), 0x08 }, /* DIG_GAIN_FRAC_LONG dig_gain_long[14:8] = 0x08 (2x) */
123af9874c9SMikhail Rudenko 
124af9874c9SMikhail Rudenko 	/* ADC and analog control*/
1258fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3603), 0x40 },
1268fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3604), 0x02 },
1278fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3609), 0x12 },
1288fe37e59SMikhail Rudenko 	{ CCI_REG8(0x360c), 0x08 },
1298fe37e59SMikhail Rudenko 	{ CCI_REG8(0x360f), 0xe5 },
1308fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3608), 0x8f },
1318fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3611), 0x00 },
1328fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3613), 0xf7 },
1338fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3616), 0x58 },
1348fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3619), 0x99 },
1358fe37e59SMikhail Rudenko 	{ CCI_REG8(0x361b), 0x60 },
1368fe37e59SMikhail Rudenko 	{ CCI_REG8(0x361e), 0x79 },
1378fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3634), 0x10 },
1388fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3635), 0x10 },
1398fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3636), 0x15 },
1408fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3646), 0x86 },
1418fe37e59SMikhail Rudenko 	{ CCI_REG8(0x364a), 0x0b },
142af9874c9SMikhail Rudenko 
143af9874c9SMikhail Rudenko 	/* Sensor control */
1448fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3700), 0x17 },
1458fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3701), 0x22 },
1468fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3703), 0x10 },
1478fe37e59SMikhail Rudenko 	{ CCI_REG8(0x370a), 0x37 },
1488fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3706), 0x63 },
1498fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3709), 0x3c },
1508fe37e59SMikhail Rudenko 	{ CCI_REG8(0x370c), 0x30 },
1518fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3710), 0x24 },
1528fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3720), 0x28 },
1538fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3729), 0x7b },
1548fe37e59SMikhail Rudenko 	{ CCI_REG8(0x372b), 0xbd },
1558fe37e59SMikhail Rudenko 	{ CCI_REG8(0x372c), 0xbc },
1568fe37e59SMikhail Rudenko 	{ CCI_REG8(0x372e), 0x52 },
1578fe37e59SMikhail Rudenko 	{ CCI_REG8(0x373c), 0x0e },
1588fe37e59SMikhail Rudenko 	{ CCI_REG8(0x373e), 0x33 },
1598fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3743), 0x10 },
1608fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3744), 0x88 },
1618fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3745), 0xc0 },
1628fe37e59SMikhail Rudenko 	{ CCI_REG8(0x374c), 0x00 },
1638fe37e59SMikhail Rudenko 	{ CCI_REG8(0x374e), 0x23 },
1648fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3751), 0x7b },
1658fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3753), 0xbd },
1668fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3754), 0xbc },
1678fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3756), 0x52 },
1688fe37e59SMikhail Rudenko 	{ CCI_REG8(0x376b), 0x20 },
1698fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3774), 0x51 },
1708fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3776), 0xbd },
1718fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3777), 0xbd },
1728fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3781), 0x18 },
1738fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3783), 0x25 },
1748fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3798), 0x1b },
175af9874c9SMikhail Rudenko 
176af9874c9SMikhail Rudenko 	/* Timing control */
1778fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3801), 0x08 }, /* H_CROP_START_L h_crop_start[7:0] = 0x08 */
1788fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3805), 0x97 }, /* H_CROP_END_L h_crop_end[7:0] = 0x97 */
1798fe37e59SMikhail Rudenko 	{ CCI_REG8(0x380c), 0x0a }, /* TIMING_HTS_H hts[14:8] = 0x0a */
1808fe37e59SMikhail Rudenko 	{ CCI_REG8(0x380d), 0x0e }, /* TIMING_HTS_L hts[7:0] = 0x0e */
1818fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3811), 0x08 }, /* H_WIN_OFF_L h_win_off[7:0] = 0x08*/
1828fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3813), 0x04 }, /* V_WIN_OFF_L v_win_off[7:0] = 0x04 */
1838fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3819), 0x01 }, /* VSYNC_END_L vsync_end_point[7:0] = 0x01 */
1848fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3821), 0x06 }, /* TIMING_FORMAT2 array_h_mirror = 1, digital_h_mirror = 1 */
185af9874c9SMikhail Rudenko 
186af9874c9SMikhail Rudenko 	/* OTP control */
1878fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3d85), 0x36 }, /* OTP_REG85 OTP_power_up_load_setting_enable = 1,
188af9874c9SMikhail Rudenko 				     * OTP_power_up_load_data_enable = 1,
189af9874c9SMikhail Rudenko 				     * OTP_bist_select = 1 (compare with zero)
190af9874c9SMikhail Rudenko 				     */
1918fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3d8c), 0x71 }, /* OTP_SETTING_STT_ADDRESS_H */
1928fe37e59SMikhail Rudenko 	{ CCI_REG8(0x3d8d), 0xcb }, /* OTP_SETTING_STT_ADDRESS_L */
193af9874c9SMikhail Rudenko 
194af9874c9SMikhail Rudenko 	/* BLC registers*/
1958fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4001), 0x40 }, /* DEBUG_MODE */
1968fe37e59SMikhail Rudenko 	{ CCI_REG8(0x401b), 0x00 }, /* DEBUG_MODE */
1978fe37e59SMikhail Rudenko 	{ CCI_REG8(0x401d), 0x00 }, /* DEBUG_MODE */
1988fe37e59SMikhail Rudenko 	{ CCI_REG8(0x401f), 0x00 }, /* DEBUG_MODE */
1998fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4020), 0x00 }, /* ANCHOR_LEFT_START_H anchor_left_start[11:8] = 0 */
2008fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4021), 0x10 }, /* ANCHOR_LEFT_START_L anchor_left_start[7:0] = 0x10 */
2018fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4022), 0x07 }, /* ANCHOR_LEFT_END_H anchor_left_end[11:8] = 0x07 */
2028fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4023), 0xcf }, /* ANCHOR_LEFT_END_L anchor_left_end[7:0] = 0xcf */
2038fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4024), 0x09 }, /* ANCHOR_RIGHT_START_H anchor_right_start[11:8] = 0x09 */
2048fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4025), 0x60 }, /* ANCHOR_RIGHT_START_L anchor_right_start[7:0] = 0x60 */
2058fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4026), 0x09 }, /* ANCHOR_RIGHT_END_H anchor_right_end[11:8] = 0x09 */
2068fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4027), 0x6f }, /* ANCHOR_RIGHT_END_L anchor_right_end[7:0] = 0x6f */
207af9874c9SMikhail Rudenko 
208af9874c9SMikhail Rudenko 	/* ADC sync control */
2098fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4500), 0x6c }, /* ADC_SYNC_CTRL */
2108fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4503), 0x01 }, /* ADC_SYNC_CTRL */
211af9874c9SMikhail Rudenko 
212af9874c9SMikhail Rudenko 	/* VFIFO */
2138fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4601), 0xa7 }, /* VFIFO_CTRL_01 r_vfifo_read_start[7:0] = 0xa7 */
214af9874c9SMikhail Rudenko 
215af9874c9SMikhail Rudenko 	/* Temperature monitor */
2168fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d00), 0x04 }, /* TPM_CTRL_00 tmp_slope[15:8] = 0x04 */
2178fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d01), 0x42 }, /* TPM_CTRL_01 tmp_slope[7:0] = 0x42 */
2188fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d02), 0xd1 }, /* TPM_CTRL_02 tpm_offset[31:24] = 0xd1 */
2198fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d03), 0x93 }, /* TPM_CTRL_03 tpm_offset[23:16] = 0x93 */
2208fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d04), 0xf5 }, /* TPM_CTRL_04 tpm_offset[15:8]  = 0xf5 */
2218fe37e59SMikhail Rudenko 	{ CCI_REG8(0x4d05), 0xc1 }, /* TPM_CTRL_05 tpm_offset[7:0]   = 0xc1 */
222af9874c9SMikhail Rudenko 
223af9874c9SMikhail Rudenko 	/* pre-ISP control */
2248fe37e59SMikhail Rudenko 	{ CCI_REG8(0x5050), 0x0c }, /* DEBUG_MODE */
225af9874c9SMikhail Rudenko 
226af9874c9SMikhail Rudenko 	/* OTP-DPC control */
2278fe37e59SMikhail Rudenko 	{ CCI_REG8(0x5501), 0x10 }, /* OTP_DPC_START_L otp_start_address[7:0] = 0x10 */
2288fe37e59SMikhail Rudenko 	{ CCI_REG8(0x5503), 0x0f }, /* OTP_DPC_END_L otp_end_address[7:0] = 0x0f */
22932a437dbSMikhail Rudenko };
23032a437dbSMikhail Rudenko 
23132a437dbSMikhail Rudenko static const struct ov4689_mode supported_modes[] = {
23232a437dbSMikhail Rudenko 	{
23332a437dbSMikhail Rudenko 		.id = OV4689_MODE_2688_1520,
23432a437dbSMikhail Rudenko 		.width = 2688,
23532a437dbSMikhail Rudenko 		.height = 1520,
23632a437dbSMikhail Rudenko 		.sensor_width = 2720,
23732a437dbSMikhail Rudenko 		.sensor_height = 1536,
23832a437dbSMikhail Rudenko 		.crop_top = 8,
23932a437dbSMikhail Rudenko 		.crop_left = 16,
24032a437dbSMikhail Rudenko 		.max_fps = 30,
24132a437dbSMikhail Rudenko 		.exp_def = 1536,
24232a437dbSMikhail Rudenko 		.hts_def = 4 * 2574,
24332a437dbSMikhail Rudenko 		.vts_def = 1554,
24432a437dbSMikhail Rudenko 		.pixel_rate = 480000000,
24532a437dbSMikhail Rudenko 		.reg_list = ov4689_2688x1520_regs,
2468fe37e59SMikhail Rudenko 		.num_regs = ARRAY_SIZE(ov4689_2688x1520_regs),
24732a437dbSMikhail Rudenko 	},
24832a437dbSMikhail Rudenko };
24932a437dbSMikhail Rudenko 
25032a437dbSMikhail Rudenko static const u64 link_freq_menu_items[] = { 504000000 };
25132a437dbSMikhail Rudenko 
25232a437dbSMikhail Rudenko static const char *const ov4689_test_pattern_menu[] = {
25332a437dbSMikhail Rudenko 	"Disabled",
25432a437dbSMikhail Rudenko 	"Vertical Color Bar Type 1",
25532a437dbSMikhail Rudenko 	"Vertical Color Bar Type 2",
25632a437dbSMikhail Rudenko 	"Vertical Color Bar Type 3",
25732a437dbSMikhail Rudenko 	"Vertical Color Bar Type 4"
25832a437dbSMikhail Rudenko };
25932a437dbSMikhail Rudenko 
26032a437dbSMikhail Rudenko /*
26132a437dbSMikhail Rudenko  * These coefficients are based on those used in Rockchip's camera
26232a437dbSMikhail Rudenko  * engine, with minor tweaks for continuity.
26332a437dbSMikhail Rudenko  */
26432a437dbSMikhail Rudenko static const struct ov4689_gain_range ov4689_gain_ranges[] = {
26532a437dbSMikhail Rudenko 	{
26632a437dbSMikhail Rudenko 		.logical_min = 0,
26732a437dbSMikhail Rudenko 		.logical_max = 255,
26832a437dbSMikhail Rudenko 		.offset = 0,
26932a437dbSMikhail Rudenko 		.divider = 1,
27032a437dbSMikhail Rudenko 		.physical_min = 0,
27132a437dbSMikhail Rudenko 		.physical_max = 255,
27232a437dbSMikhail Rudenko 	},
27332a437dbSMikhail Rudenko 	{
27432a437dbSMikhail Rudenko 		.logical_min = 256,
27532a437dbSMikhail Rudenko 		.logical_max = 511,
27632a437dbSMikhail Rudenko 		.offset = 252,
27732a437dbSMikhail Rudenko 		.divider = 2,
27832a437dbSMikhail Rudenko 		.physical_min = 376,
27932a437dbSMikhail Rudenko 		.physical_max = 504,
28032a437dbSMikhail Rudenko 	},
28132a437dbSMikhail Rudenko 	{
28232a437dbSMikhail Rudenko 		.logical_min = 512,
28332a437dbSMikhail Rudenko 		.logical_max = 1023,
28432a437dbSMikhail Rudenko 		.offset = 758,
28532a437dbSMikhail Rudenko 		.divider = 4,
28632a437dbSMikhail Rudenko 		.physical_min = 884,
28732a437dbSMikhail Rudenko 		.physical_max = 1012,
28832a437dbSMikhail Rudenko 	},
28932a437dbSMikhail Rudenko 	{
29032a437dbSMikhail Rudenko 		.logical_min = 1024,
29132a437dbSMikhail Rudenko 		.logical_max = 2047,
29232a437dbSMikhail Rudenko 		.offset = 1788,
29332a437dbSMikhail Rudenko 		.divider = 8,
29432a437dbSMikhail Rudenko 		.physical_min = 1912,
29532a437dbSMikhail Rudenko 		.physical_max = 2047,
29632a437dbSMikhail Rudenko 	},
29732a437dbSMikhail Rudenko };
29832a437dbSMikhail Rudenko 
29932a437dbSMikhail Rudenko static void ov4689_fill_fmt(const struct ov4689_mode *mode,
30032a437dbSMikhail Rudenko 			    struct v4l2_mbus_framefmt *fmt)
30132a437dbSMikhail Rudenko {
30232a437dbSMikhail Rudenko 	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
30332a437dbSMikhail Rudenko 	fmt->width = mode->width;
30432a437dbSMikhail Rudenko 	fmt->height = mode->height;
30532a437dbSMikhail Rudenko 	fmt->field = V4L2_FIELD_NONE;
30632a437dbSMikhail Rudenko }
30732a437dbSMikhail Rudenko 
30832a437dbSMikhail Rudenko static int ov4689_set_fmt(struct v4l2_subdev *sd,
30932a437dbSMikhail Rudenko 			  struct v4l2_subdev_state *sd_state,
31032a437dbSMikhail Rudenko 			  struct v4l2_subdev_format *fmt)
31132a437dbSMikhail Rudenko {
31232a437dbSMikhail Rudenko 	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
31332a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
31432a437dbSMikhail Rudenko 
31532a437dbSMikhail Rudenko 	/* only one mode supported for now */
31632a437dbSMikhail Rudenko 	ov4689_fill_fmt(ov4689->cur_mode, mbus_fmt);
31732a437dbSMikhail Rudenko 
31832a437dbSMikhail Rudenko 	return 0;
31932a437dbSMikhail Rudenko }
32032a437dbSMikhail Rudenko 
32132a437dbSMikhail Rudenko static int ov4689_enum_mbus_code(struct v4l2_subdev *sd,
32232a437dbSMikhail Rudenko 				 struct v4l2_subdev_state *sd_state,
32332a437dbSMikhail Rudenko 				 struct v4l2_subdev_mbus_code_enum *code)
32432a437dbSMikhail Rudenko {
32532a437dbSMikhail Rudenko 	if (code->index != 0)
32632a437dbSMikhail Rudenko 		return -EINVAL;
32732a437dbSMikhail Rudenko 	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
32832a437dbSMikhail Rudenko 
32932a437dbSMikhail Rudenko 	return 0;
33032a437dbSMikhail Rudenko }
33132a437dbSMikhail Rudenko 
33232a437dbSMikhail Rudenko static int ov4689_enum_frame_sizes(struct v4l2_subdev *sd,
33332a437dbSMikhail Rudenko 				   struct v4l2_subdev_state *sd_state,
33432a437dbSMikhail Rudenko 				   struct v4l2_subdev_frame_size_enum *fse)
33532a437dbSMikhail Rudenko {
33632a437dbSMikhail Rudenko 	if (fse->index >= ARRAY_SIZE(supported_modes))
33732a437dbSMikhail Rudenko 		return -EINVAL;
33832a437dbSMikhail Rudenko 
33932a437dbSMikhail Rudenko 	if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
34032a437dbSMikhail Rudenko 		return -EINVAL;
34132a437dbSMikhail Rudenko 
34232a437dbSMikhail Rudenko 	fse->min_width = supported_modes[fse->index].width;
34332a437dbSMikhail Rudenko 	fse->max_width = supported_modes[fse->index].width;
34432a437dbSMikhail Rudenko 	fse->max_height = supported_modes[fse->index].height;
34532a437dbSMikhail Rudenko 	fse->min_height = supported_modes[fse->index].height;
34632a437dbSMikhail Rudenko 
34732a437dbSMikhail Rudenko 	return 0;
34832a437dbSMikhail Rudenko }
34932a437dbSMikhail Rudenko 
35032a437dbSMikhail Rudenko static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
35132a437dbSMikhail Rudenko {
35232a437dbSMikhail Rudenko 	u32 val;
35332a437dbSMikhail Rudenko 
35432a437dbSMikhail Rudenko 	if (pattern)
35532a437dbSMikhail Rudenko 		val = (pattern - 1) | OV4689_TEST_PATTERN_ENABLE;
35632a437dbSMikhail Rudenko 	else
35732a437dbSMikhail Rudenko 		val = OV4689_TEST_PATTERN_DISABLE;
35832a437dbSMikhail Rudenko 
3598fe37e59SMikhail Rudenko 	return cci_write(ov4689->regmap, OV4689_REG_TEST_PATTERN,
3608fe37e59SMikhail Rudenko 			 val, NULL);
36132a437dbSMikhail Rudenko }
36232a437dbSMikhail Rudenko 
36332a437dbSMikhail Rudenko static int ov4689_get_selection(struct v4l2_subdev *sd,
36432a437dbSMikhail Rudenko 				struct v4l2_subdev_state *state,
36532a437dbSMikhail Rudenko 				struct v4l2_subdev_selection *sel)
36632a437dbSMikhail Rudenko {
36732a437dbSMikhail Rudenko 	const struct ov4689_mode *mode = to_ov4689(sd)->cur_mode;
36832a437dbSMikhail Rudenko 
36932a437dbSMikhail Rudenko 	if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
37032a437dbSMikhail Rudenko 		return -EINVAL;
37132a437dbSMikhail Rudenko 
37232a437dbSMikhail Rudenko 	switch (sel->target) {
37332a437dbSMikhail Rudenko 	case V4L2_SEL_TGT_CROP_BOUNDS:
37432a437dbSMikhail Rudenko 		sel->r.top = 0;
37532a437dbSMikhail Rudenko 		sel->r.left = 0;
37632a437dbSMikhail Rudenko 		sel->r.width = mode->sensor_width;
37732a437dbSMikhail Rudenko 		sel->r.height = mode->sensor_height;
37832a437dbSMikhail Rudenko 		return 0;
37932a437dbSMikhail Rudenko 	case V4L2_SEL_TGT_CROP:
38032a437dbSMikhail Rudenko 	case V4L2_SEL_TGT_CROP_DEFAULT:
38132a437dbSMikhail Rudenko 		sel->r.top = mode->crop_top;
38232a437dbSMikhail Rudenko 		sel->r.left = mode->crop_left;
38332a437dbSMikhail Rudenko 		sel->r.width = mode->width;
38432a437dbSMikhail Rudenko 		sel->r.height = mode->height;
38532a437dbSMikhail Rudenko 		return 0;
38632a437dbSMikhail Rudenko 	}
38732a437dbSMikhail Rudenko 
38832a437dbSMikhail Rudenko 	return -EINVAL;
38932a437dbSMikhail Rudenko }
39032a437dbSMikhail Rudenko 
39132a437dbSMikhail Rudenko static int ov4689_s_stream(struct v4l2_subdev *sd, int on)
39232a437dbSMikhail Rudenko {
39332a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
3945e2974acSMikhail Rudenko 	struct v4l2_subdev_state *sd_state;
395d015aaafSMikhail Rudenko 	struct device *dev = ov4689->dev;
39632a437dbSMikhail Rudenko 	int ret = 0;
39732a437dbSMikhail Rudenko 
3985e2974acSMikhail Rudenko 	sd_state = v4l2_subdev_lock_and_get_active_state(&ov4689->subdev);
39932a437dbSMikhail Rudenko 
40032a437dbSMikhail Rudenko 	if (on) {
401d015aaafSMikhail Rudenko 		ret = pm_runtime_resume_and_get(dev);
40232a437dbSMikhail Rudenko 		if (ret < 0)
40332a437dbSMikhail Rudenko 			goto unlock_and_return;
40432a437dbSMikhail Rudenko 
4058fe37e59SMikhail Rudenko 		ret = cci_multi_reg_write(ov4689->regmap,
4068fe37e59SMikhail Rudenko 					  ov4689->cur_mode->reg_list,
4078fe37e59SMikhail Rudenko 					  ov4689->cur_mode->num_regs,
4088fe37e59SMikhail Rudenko 					  NULL);
40932a437dbSMikhail Rudenko 		if (ret) {
410d015aaafSMikhail Rudenko 			pm_runtime_put(dev);
41132a437dbSMikhail Rudenko 			goto unlock_and_return;
41232a437dbSMikhail Rudenko 		}
41332a437dbSMikhail Rudenko 
41432a437dbSMikhail Rudenko 		ret = __v4l2_ctrl_handler_setup(&ov4689->ctrl_handler);
41532a437dbSMikhail Rudenko 		if (ret) {
416d015aaafSMikhail Rudenko 			pm_runtime_put(dev);
41732a437dbSMikhail Rudenko 			goto unlock_and_return;
41832a437dbSMikhail Rudenko 		}
41932a437dbSMikhail Rudenko 
4208fe37e59SMikhail Rudenko 		ret = cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
4218fe37e59SMikhail Rudenko 				OV4689_MODE_STREAMING, NULL);
42232a437dbSMikhail Rudenko 		if (ret) {
423d015aaafSMikhail Rudenko 			pm_runtime_put(dev);
42432a437dbSMikhail Rudenko 			goto unlock_and_return;
42532a437dbSMikhail Rudenko 		}
42632a437dbSMikhail Rudenko 	} else {
4278fe37e59SMikhail Rudenko 		cci_write(ov4689->regmap, OV4689_REG_CTRL_MODE,
4288fe37e59SMikhail Rudenko 			  OV4689_MODE_SW_STANDBY, NULL);
429*47e4cf3dSMikhail Rudenko 		pm_runtime_mark_last_busy(dev);
430*47e4cf3dSMikhail Rudenko 		pm_runtime_put_autosuspend(dev);
43132a437dbSMikhail Rudenko 	}
43232a437dbSMikhail Rudenko 
43332a437dbSMikhail Rudenko unlock_and_return:
4345e2974acSMikhail Rudenko 	v4l2_subdev_unlock_state(sd_state);
43532a437dbSMikhail Rudenko 
43632a437dbSMikhail Rudenko 	return ret;
43732a437dbSMikhail Rudenko }
43832a437dbSMikhail Rudenko 
43932a437dbSMikhail Rudenko /* Calculate the delay in us by clock rate and clock cycles */
44032a437dbSMikhail Rudenko static inline u32 ov4689_cal_delay(struct ov4689 *ov4689, u32 cycles)
44132a437dbSMikhail Rudenko {
44232a437dbSMikhail Rudenko 	return DIV_ROUND_UP(cycles * 1000,
44332a437dbSMikhail Rudenko 			    DIV_ROUND_UP(ov4689->clock_rate, 1000));
44432a437dbSMikhail Rudenko }
44532a437dbSMikhail Rudenko 
44632a437dbSMikhail Rudenko static int __maybe_unused ov4689_power_on(struct device *dev)
44732a437dbSMikhail Rudenko {
44832a437dbSMikhail Rudenko 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
44932a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
45032a437dbSMikhail Rudenko 	u32 delay_us;
45132a437dbSMikhail Rudenko 	int ret;
45232a437dbSMikhail Rudenko 
45332a437dbSMikhail Rudenko 	ret = clk_prepare_enable(ov4689->xvclk);
45432a437dbSMikhail Rudenko 	if (ret < 0) {
45532a437dbSMikhail Rudenko 		dev_err(dev, "Failed to enable xvclk\n");
45632a437dbSMikhail Rudenko 		return ret;
45732a437dbSMikhail Rudenko 	}
45832a437dbSMikhail Rudenko 
45932a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
46032a437dbSMikhail Rudenko 
46132a437dbSMikhail Rudenko 	ret = regulator_bulk_enable(ARRAY_SIZE(ov4689_supply_names),
46232a437dbSMikhail Rudenko 				    ov4689->supplies);
46332a437dbSMikhail Rudenko 	if (ret < 0) {
46432a437dbSMikhail Rudenko 		dev_err(dev, "Failed to enable regulators\n");
46532a437dbSMikhail Rudenko 		goto disable_clk;
46632a437dbSMikhail Rudenko 	}
46732a437dbSMikhail Rudenko 
46832a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->reset_gpio, 0);
46932a437dbSMikhail Rudenko 	usleep_range(500, 1000);
47032a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 0);
47132a437dbSMikhail Rudenko 
47232a437dbSMikhail Rudenko 	/* 8192 cycles prior to first SCCB transaction */
47332a437dbSMikhail Rudenko 	delay_us = ov4689_cal_delay(ov4689, 8192);
47432a437dbSMikhail Rudenko 	usleep_range(delay_us, delay_us * 2);
47532a437dbSMikhail Rudenko 
47632a437dbSMikhail Rudenko 	return 0;
47732a437dbSMikhail Rudenko 
47832a437dbSMikhail Rudenko disable_clk:
47932a437dbSMikhail Rudenko 	clk_disable_unprepare(ov4689->xvclk);
48032a437dbSMikhail Rudenko 
48132a437dbSMikhail Rudenko 	return ret;
48232a437dbSMikhail Rudenko }
48332a437dbSMikhail Rudenko 
48432a437dbSMikhail Rudenko static int __maybe_unused ov4689_power_off(struct device *dev)
48532a437dbSMikhail Rudenko {
48632a437dbSMikhail Rudenko 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
48732a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
48832a437dbSMikhail Rudenko 
48932a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->pwdn_gpio, 1);
49032a437dbSMikhail Rudenko 	clk_disable_unprepare(ov4689->xvclk);
49132a437dbSMikhail Rudenko 	gpiod_set_value_cansleep(ov4689->reset_gpio, 1);
49232a437dbSMikhail Rudenko 	regulator_bulk_disable(ARRAY_SIZE(ov4689_supply_names),
49332a437dbSMikhail Rudenko 			       ov4689->supplies);
49432a437dbSMikhail Rudenko 	return 0;
49532a437dbSMikhail Rudenko }
49632a437dbSMikhail Rudenko 
4975e2974acSMikhail Rudenko static int ov4689_init_state(struct v4l2_subdev *sd,
4985e2974acSMikhail Rudenko 			     struct v4l2_subdev_state *sd_state)
49932a437dbSMikhail Rudenko {
5005e2974acSMikhail Rudenko 	struct v4l2_mbus_framefmt *fmt =
5015e2974acSMikhail Rudenko 		v4l2_subdev_state_get_format(sd_state, 0);
50232a437dbSMikhail Rudenko 
5035e2974acSMikhail Rudenko 	ov4689_fill_fmt(&supported_modes[OV4689_MODE_2688_1520], fmt);
50432a437dbSMikhail Rudenko 
50532a437dbSMikhail Rudenko 	return 0;
50632a437dbSMikhail Rudenko }
50732a437dbSMikhail Rudenko 
50832a437dbSMikhail Rudenko static const struct dev_pm_ops ov4689_pm_ops = {
50932a437dbSMikhail Rudenko 	SET_RUNTIME_PM_OPS(ov4689_power_off, ov4689_power_on, NULL)
51032a437dbSMikhail Rudenko };
51132a437dbSMikhail Rudenko 
51232a437dbSMikhail Rudenko static const struct v4l2_subdev_video_ops ov4689_video_ops = {
51332a437dbSMikhail Rudenko 	.s_stream = ov4689_s_stream,
51432a437dbSMikhail Rudenko };
51532a437dbSMikhail Rudenko 
51632a437dbSMikhail Rudenko static const struct v4l2_subdev_pad_ops ov4689_pad_ops = {
51732a437dbSMikhail Rudenko 	.enum_mbus_code = ov4689_enum_mbus_code,
51832a437dbSMikhail Rudenko 	.enum_frame_size = ov4689_enum_frame_sizes,
5195e2974acSMikhail Rudenko 	.get_fmt = v4l2_subdev_get_fmt,
52032a437dbSMikhail Rudenko 	.set_fmt = ov4689_set_fmt,
52132a437dbSMikhail Rudenko 	.get_selection = ov4689_get_selection,
52232a437dbSMikhail Rudenko };
52332a437dbSMikhail Rudenko 
5245e2974acSMikhail Rudenko static const struct v4l2_subdev_internal_ops ov4689_internal_ops = {
5255e2974acSMikhail Rudenko 	.init_state = ov4689_init_state,
5265e2974acSMikhail Rudenko };
5275e2974acSMikhail Rudenko 
52832a437dbSMikhail Rudenko static const struct v4l2_subdev_ops ov4689_subdev_ops = {
52932a437dbSMikhail Rudenko 	.video = &ov4689_video_ops,
53032a437dbSMikhail Rudenko 	.pad = &ov4689_pad_ops,
53132a437dbSMikhail Rudenko };
53232a437dbSMikhail Rudenko 
53332a437dbSMikhail Rudenko /*
53432a437dbSMikhail Rudenko  * Map userspace (logical) gain to sensor (physical) gain using
53532a437dbSMikhail Rudenko  * ov4689_gain_ranges table.
53632a437dbSMikhail Rudenko  */
53732a437dbSMikhail Rudenko static int ov4689_map_gain(struct ov4689 *ov4689, int logical_gain, int *result)
53832a437dbSMikhail Rudenko {
53932a437dbSMikhail Rudenko 	const struct ov4689_gain_range *range;
54032a437dbSMikhail Rudenko 	unsigned int n;
54132a437dbSMikhail Rudenko 
54232a437dbSMikhail Rudenko 	for (n = 0; n < ARRAY_SIZE(ov4689_gain_ranges); n++) {
54332a437dbSMikhail Rudenko 		if (logical_gain >= ov4689_gain_ranges[n].logical_min &&
5447336c54aSMikhail Rudenko 		    logical_gain <= ov4689_gain_ranges[n].logical_max)
54532a437dbSMikhail Rudenko 			break;
54632a437dbSMikhail Rudenko 	}
54732a437dbSMikhail Rudenko 
54832a437dbSMikhail Rudenko 	if (n == ARRAY_SIZE(ov4689_gain_ranges)) {
549d015aaafSMikhail Rudenko 		dev_warn_ratelimited(ov4689->dev,
550d015aaafSMikhail Rudenko 				     "no mapping found for gain %d\n",
55132a437dbSMikhail Rudenko 				     logical_gain);
55232a437dbSMikhail Rudenko 		return -EINVAL;
55332a437dbSMikhail Rudenko 	}
55432a437dbSMikhail Rudenko 
55532a437dbSMikhail Rudenko 	range = &ov4689_gain_ranges[n];
55632a437dbSMikhail Rudenko 
55732a437dbSMikhail Rudenko 	*result = clamp(range->offset + (logical_gain) / range->divider,
55832a437dbSMikhail Rudenko 			range->physical_min, range->physical_max);
55932a437dbSMikhail Rudenko 	return 0;
56032a437dbSMikhail Rudenko }
56132a437dbSMikhail Rudenko 
56232a437dbSMikhail Rudenko static int ov4689_set_ctrl(struct v4l2_ctrl *ctrl)
56332a437dbSMikhail Rudenko {
56432a437dbSMikhail Rudenko 	struct ov4689 *ov4689 =
56532a437dbSMikhail Rudenko 		container_of(ctrl->handler, struct ov4689, ctrl_handler);
566d1e90c25SMikhail Rudenko 	struct regmap *regmap = ov4689->regmap;
567d015aaafSMikhail Rudenko 	struct device *dev = ov4689->dev;
5688fe37e59SMikhail Rudenko 	int sensor_gain = 0;
56932a437dbSMikhail Rudenko 	s64 max_expo;
570d1e90c25SMikhail Rudenko 	int ret = 0;
57132a437dbSMikhail Rudenko 
57232a437dbSMikhail Rudenko 	/* Propagate change of current control to all related controls */
57332a437dbSMikhail Rudenko 	switch (ctrl->id) {
57432a437dbSMikhail Rudenko 	case V4L2_CID_VBLANK:
57532a437dbSMikhail Rudenko 		/* Update max exposure while meeting expected vblanking */
57632a437dbSMikhail Rudenko 		max_expo = ov4689->cur_mode->height + ctrl->val - 4;
57732a437dbSMikhail Rudenko 		__v4l2_ctrl_modify_range(ov4689->exposure,
57832a437dbSMikhail Rudenko 					 ov4689->exposure->minimum, max_expo,
57932a437dbSMikhail Rudenko 					 ov4689->exposure->step,
58032a437dbSMikhail Rudenko 					 ov4689->exposure->default_value);
58132a437dbSMikhail Rudenko 		break;
58232a437dbSMikhail Rudenko 	}
58332a437dbSMikhail Rudenko 
584d015aaafSMikhail Rudenko 	if (!pm_runtime_get_if_in_use(dev))
58532a437dbSMikhail Rudenko 		return 0;
58632a437dbSMikhail Rudenko 
58732a437dbSMikhail Rudenko 	switch (ctrl->id) {
58832a437dbSMikhail Rudenko 	case V4L2_CID_EXPOSURE:
58961198ad4SMikhail Rudenko 		/* 4 least significant bits of exposure are fractional part */
590d1e90c25SMikhail Rudenko 		cci_write(regmap, OV4689_REG_EXPOSURE, ctrl->val << 4, &ret);
59132a437dbSMikhail Rudenko 		break;
59232a437dbSMikhail Rudenko 	case V4L2_CID_ANALOGUE_GAIN:
59332a437dbSMikhail Rudenko 		ret = ov4689_map_gain(ov4689, ctrl->val, &sensor_gain);
594d1e90c25SMikhail Rudenko 		cci_write(regmap, OV4689_REG_GAIN, sensor_gain, &ret);
59532a437dbSMikhail Rudenko 		break;
59632a437dbSMikhail Rudenko 	case V4L2_CID_VBLANK:
597d1e90c25SMikhail Rudenko 		cci_write(regmap, OV4689_REG_VTS,
598d1e90c25SMikhail Rudenko 			  ctrl->val + ov4689->cur_mode->height, &ret);
59932a437dbSMikhail Rudenko 		break;
60032a437dbSMikhail Rudenko 	case V4L2_CID_TEST_PATTERN:
60132a437dbSMikhail Rudenko 		ret = ov4689_enable_test_pattern(ov4689, ctrl->val);
60232a437dbSMikhail Rudenko 		break;
60332a437dbSMikhail Rudenko 	default:
604d015aaafSMikhail Rudenko 		dev_warn(dev, "%s Unhandled id:0x%x, val:0x%x\n",
60532a437dbSMikhail Rudenko 			 __func__, ctrl->id, ctrl->val);
60632a437dbSMikhail Rudenko 		ret = -EINVAL;
60732a437dbSMikhail Rudenko 		break;
60832a437dbSMikhail Rudenko 	}
60932a437dbSMikhail Rudenko 
610*47e4cf3dSMikhail Rudenko 	pm_runtime_mark_last_busy(dev);
611*47e4cf3dSMikhail Rudenko 	pm_runtime_put_autosuspend(dev);
61232a437dbSMikhail Rudenko 
61332a437dbSMikhail Rudenko 	return ret;
61432a437dbSMikhail Rudenko }
61532a437dbSMikhail Rudenko 
61632a437dbSMikhail Rudenko static const struct v4l2_ctrl_ops ov4689_ctrl_ops = {
61732a437dbSMikhail Rudenko 	.s_ctrl = ov4689_set_ctrl,
61832a437dbSMikhail Rudenko };
61932a437dbSMikhail Rudenko 
62032a437dbSMikhail Rudenko static int ov4689_initialize_controls(struct ov4689 *ov4689)
62132a437dbSMikhail Rudenko {
62232a437dbSMikhail Rudenko 	struct i2c_client *client = v4l2_get_subdevdata(&ov4689->subdev);
62332a437dbSMikhail Rudenko 	struct v4l2_fwnode_device_properties props;
62432a437dbSMikhail Rudenko 	struct v4l2_ctrl_handler *handler;
62532a437dbSMikhail Rudenko 	const struct ov4689_mode *mode;
62632a437dbSMikhail Rudenko 	s64 exposure_max, vblank_def;
62732a437dbSMikhail Rudenko 	struct v4l2_ctrl *ctrl;
62832a437dbSMikhail Rudenko 	s64 h_blank_def;
62932a437dbSMikhail Rudenko 	int ret;
63032a437dbSMikhail Rudenko 
63132a437dbSMikhail Rudenko 	handler = &ov4689->ctrl_handler;
63232a437dbSMikhail Rudenko 	mode = ov4689->cur_mode;
63332a437dbSMikhail Rudenko 	ret = v4l2_ctrl_handler_init(handler, 10);
63432a437dbSMikhail Rudenko 	if (ret)
63532a437dbSMikhail Rudenko 		return ret;
63632a437dbSMikhail Rudenko 
63732a437dbSMikhail Rudenko 	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, 0, 0,
63832a437dbSMikhail Rudenko 				      link_freq_menu_items);
63932a437dbSMikhail Rudenko 	if (ctrl)
64032a437dbSMikhail Rudenko 		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
64132a437dbSMikhail Rudenko 
64232a437dbSMikhail Rudenko 	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0,
64332a437dbSMikhail Rudenko 			  mode->pixel_rate, 1, mode->pixel_rate);
64432a437dbSMikhail Rudenko 
64532a437dbSMikhail Rudenko 	h_blank_def = mode->hts_def - mode->width;
64632a437dbSMikhail Rudenko 	ctrl = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, h_blank_def,
64732a437dbSMikhail Rudenko 				 h_blank_def, 1, h_blank_def);
64832a437dbSMikhail Rudenko 	if (ctrl)
64932a437dbSMikhail Rudenko 		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
65032a437dbSMikhail Rudenko 
65132a437dbSMikhail Rudenko 	vblank_def = mode->vts_def - mode->height;
65232a437dbSMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_VBLANK,
65332a437dbSMikhail Rudenko 			  vblank_def, OV4689_VTS_MAX - mode->height, 1,
65432a437dbSMikhail Rudenko 			  vblank_def);
65532a437dbSMikhail Rudenko 
65632a437dbSMikhail Rudenko 	exposure_max = mode->vts_def - 4;
65732a437dbSMikhail Rudenko 	ov4689->exposure =
65832a437dbSMikhail Rudenko 		v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_EXPOSURE,
65932a437dbSMikhail Rudenko 				  OV4689_EXPOSURE_MIN, exposure_max,
66032a437dbSMikhail Rudenko 				  OV4689_EXPOSURE_STEP, mode->exp_def);
66132a437dbSMikhail Rudenko 
66232a437dbSMikhail Rudenko 	v4l2_ctrl_new_std(handler, &ov4689_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
66332a437dbSMikhail Rudenko 			  ov4689_gain_ranges[0].logical_min,
66432a437dbSMikhail Rudenko 			  ov4689_gain_ranges[ARRAY_SIZE(ov4689_gain_ranges) - 1]
66532a437dbSMikhail Rudenko 				  .logical_max,
66632a437dbSMikhail Rudenko 			  OV4689_GAIN_STEP, OV4689_GAIN_DEFAULT);
66732a437dbSMikhail Rudenko 
66832a437dbSMikhail Rudenko 	v4l2_ctrl_new_std_menu_items(handler, &ov4689_ctrl_ops,
66932a437dbSMikhail Rudenko 				     V4L2_CID_TEST_PATTERN,
67032a437dbSMikhail Rudenko 				     ARRAY_SIZE(ov4689_test_pattern_menu) - 1,
67132a437dbSMikhail Rudenko 				     0, 0, ov4689_test_pattern_menu);
67232a437dbSMikhail Rudenko 
67332a437dbSMikhail Rudenko 	if (handler->error) {
67432a437dbSMikhail Rudenko 		ret = handler->error;
675d015aaafSMikhail Rudenko 		dev_err(ov4689->dev, "Failed to init controls(%d)\n", ret);
67632a437dbSMikhail Rudenko 		goto err_free_handler;
67732a437dbSMikhail Rudenko 	}
67832a437dbSMikhail Rudenko 
67932a437dbSMikhail Rudenko 	ret = v4l2_fwnode_device_parse(&client->dev, &props);
68032a437dbSMikhail Rudenko 	if (ret)
68132a437dbSMikhail Rudenko 		goto err_free_handler;
68232a437dbSMikhail Rudenko 
68332a437dbSMikhail Rudenko 	ret = v4l2_ctrl_new_fwnode_properties(handler, &ov4689_ctrl_ops,
68432a437dbSMikhail Rudenko 					      &props);
68532a437dbSMikhail Rudenko 	if (ret)
68632a437dbSMikhail Rudenko 		goto err_free_handler;
68732a437dbSMikhail Rudenko 
68832a437dbSMikhail Rudenko 	ov4689->subdev.ctrl_handler = handler;
68932a437dbSMikhail Rudenko 
69032a437dbSMikhail Rudenko 	return 0;
69132a437dbSMikhail Rudenko 
69232a437dbSMikhail Rudenko err_free_handler:
69332a437dbSMikhail Rudenko 	v4l2_ctrl_handler_free(handler);
69432a437dbSMikhail Rudenko 
69532a437dbSMikhail Rudenko 	return ret;
69632a437dbSMikhail Rudenko }
69732a437dbSMikhail Rudenko 
69832a437dbSMikhail Rudenko static int ov4689_check_sensor_id(struct ov4689 *ov4689,
69932a437dbSMikhail Rudenko 				  struct i2c_client *client)
70032a437dbSMikhail Rudenko {
701d015aaafSMikhail Rudenko 	struct device *dev = ov4689->dev;
7028fe37e59SMikhail Rudenko 	u64 id = 0;
70332a437dbSMikhail Rudenko 	int ret;
70432a437dbSMikhail Rudenko 
7058fe37e59SMikhail Rudenko 	ret = cci_read(ov4689->regmap, OV4689_REG_CHIP_ID, &id, NULL);
70632a437dbSMikhail Rudenko 	if (ret) {
70732a437dbSMikhail Rudenko 		dev_err(dev, "Cannot read sensor ID\n");
70832a437dbSMikhail Rudenko 		return ret;
70932a437dbSMikhail Rudenko 	}
71032a437dbSMikhail Rudenko 
71132a437dbSMikhail Rudenko 	if (id != CHIP_ID) {
7128fe37e59SMikhail Rudenko 		dev_err(dev, "Unexpected sensor ID %06llx, expected %06x\n",
71332a437dbSMikhail Rudenko 			id, CHIP_ID);
71432a437dbSMikhail Rudenko 		return -ENODEV;
71532a437dbSMikhail Rudenko 	}
71632a437dbSMikhail Rudenko 
71732a437dbSMikhail Rudenko 	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
71832a437dbSMikhail Rudenko 
71932a437dbSMikhail Rudenko 	return 0;
72032a437dbSMikhail Rudenko }
72132a437dbSMikhail Rudenko 
72232a437dbSMikhail Rudenko static int ov4689_configure_regulators(struct ov4689 *ov4689)
72332a437dbSMikhail Rudenko {
72432a437dbSMikhail Rudenko 	unsigned int i;
72532a437dbSMikhail Rudenko 
7267336c54aSMikhail Rudenko 	for (i = 0; i < ARRAY_SIZE(ov4689_supply_names); i++)
72732a437dbSMikhail Rudenko 		ov4689->supplies[i].supply = ov4689_supply_names[i];
72832a437dbSMikhail Rudenko 
729d015aaafSMikhail Rudenko 	return devm_regulator_bulk_get(ov4689->dev,
7307336c54aSMikhail Rudenko 				       ARRAY_SIZE(ov4689_supply_names),
73132a437dbSMikhail Rudenko 				       ov4689->supplies);
73232a437dbSMikhail Rudenko }
73332a437dbSMikhail Rudenko 
73432a437dbSMikhail Rudenko static u64 ov4689_check_link_frequency(struct v4l2_fwnode_endpoint *ep)
73532a437dbSMikhail Rudenko {
73632a437dbSMikhail Rudenko 	const u64 *freqs = link_freq_menu_items;
73732a437dbSMikhail Rudenko 	unsigned int i, j;
73832a437dbSMikhail Rudenko 
7397336c54aSMikhail Rudenko 	for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) {
74032a437dbSMikhail Rudenko 		for (j = 0; j < ep->nr_of_link_frequencies; j++)
74132a437dbSMikhail Rudenko 			if (freqs[i] == ep->link_frequencies[j])
74232a437dbSMikhail Rudenko 				return freqs[i];
74332a437dbSMikhail Rudenko 	}
74432a437dbSMikhail Rudenko 
74532a437dbSMikhail Rudenko 	return 0;
74632a437dbSMikhail Rudenko }
74732a437dbSMikhail Rudenko 
74832a437dbSMikhail Rudenko static int ov4689_check_hwcfg(struct device *dev)
74932a437dbSMikhail Rudenko {
75032a437dbSMikhail Rudenko 	struct fwnode_handle *fwnode = dev_fwnode(dev);
75132a437dbSMikhail Rudenko 	struct v4l2_fwnode_endpoint bus_cfg = {
75232a437dbSMikhail Rudenko 		.bus_type = V4L2_MBUS_CSI2_DPHY,
75332a437dbSMikhail Rudenko 	};
75432a437dbSMikhail Rudenko 	struct fwnode_handle *endpoint;
75532a437dbSMikhail Rudenko 	int ret;
75632a437dbSMikhail Rudenko 
75732a437dbSMikhail Rudenko 	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
75832a437dbSMikhail Rudenko 	if (!endpoint)
75932a437dbSMikhail Rudenko 		return -EINVAL;
76032a437dbSMikhail Rudenko 
76132a437dbSMikhail Rudenko 	ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
76232a437dbSMikhail Rudenko 	fwnode_handle_put(endpoint);
76332a437dbSMikhail Rudenko 	if (ret)
76432a437dbSMikhail Rudenko 		return ret;
76532a437dbSMikhail Rudenko 
76632a437dbSMikhail Rudenko 	if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV4689_LANES) {
76732a437dbSMikhail Rudenko 		dev_err(dev, "Only a 4-lane CSI2 config is supported");
76832a437dbSMikhail Rudenko 		ret = -EINVAL;
76932a437dbSMikhail Rudenko 		goto out_free_bus_cfg;
77032a437dbSMikhail Rudenko 	}
77132a437dbSMikhail Rudenko 
77232a437dbSMikhail Rudenko 	if (!ov4689_check_link_frequency(&bus_cfg)) {
77332a437dbSMikhail Rudenko 		dev_err(dev, "No supported link frequency found\n");
77432a437dbSMikhail Rudenko 		ret = -EINVAL;
77532a437dbSMikhail Rudenko 	}
77632a437dbSMikhail Rudenko 
77732a437dbSMikhail Rudenko out_free_bus_cfg:
77832a437dbSMikhail Rudenko 	v4l2_fwnode_endpoint_free(&bus_cfg);
77932a437dbSMikhail Rudenko 
78032a437dbSMikhail Rudenko 	return ret;
78132a437dbSMikhail Rudenko }
78232a437dbSMikhail Rudenko 
78332a437dbSMikhail Rudenko static int ov4689_probe(struct i2c_client *client)
78432a437dbSMikhail Rudenko {
78532a437dbSMikhail Rudenko 	struct device *dev = &client->dev;
78632a437dbSMikhail Rudenko 	struct v4l2_subdev *sd;
78732a437dbSMikhail Rudenko 	struct ov4689 *ov4689;
78832a437dbSMikhail Rudenko 	int ret;
78932a437dbSMikhail Rudenko 
79032a437dbSMikhail Rudenko 	ret = ov4689_check_hwcfg(dev);
79132a437dbSMikhail Rudenko 	if (ret)
79232a437dbSMikhail Rudenko 		return ret;
79332a437dbSMikhail Rudenko 
79432a437dbSMikhail Rudenko 	ov4689 = devm_kzalloc(dev, sizeof(*ov4689), GFP_KERNEL);
79532a437dbSMikhail Rudenko 	if (!ov4689)
79632a437dbSMikhail Rudenko 		return -ENOMEM;
79732a437dbSMikhail Rudenko 
798d015aaafSMikhail Rudenko 	ov4689->dev = dev;
799d015aaafSMikhail Rudenko 
80032a437dbSMikhail Rudenko 	ov4689->cur_mode = &supported_modes[OV4689_MODE_2688_1520];
80132a437dbSMikhail Rudenko 
80232a437dbSMikhail Rudenko 	ov4689->xvclk = devm_clk_get_optional(dev, NULL);
80332a437dbSMikhail Rudenko 	if (IS_ERR(ov4689->xvclk))
80432a437dbSMikhail Rudenko 		return dev_err_probe(dev, PTR_ERR(ov4689->xvclk),
80532a437dbSMikhail Rudenko 				     "Failed to get external clock\n");
80632a437dbSMikhail Rudenko 
80732a437dbSMikhail Rudenko 	if (!ov4689->xvclk) {
80832a437dbSMikhail Rudenko 		dev_dbg(dev,
80932a437dbSMikhail Rudenko 			"No clock provided, using clock-frequency property\n");
81032a437dbSMikhail Rudenko 		device_property_read_u32(dev, "clock-frequency",
81132a437dbSMikhail Rudenko 					 &ov4689->clock_rate);
81232a437dbSMikhail Rudenko 	} else {
81332a437dbSMikhail Rudenko 		ov4689->clock_rate = clk_get_rate(ov4689->xvclk);
81432a437dbSMikhail Rudenko 	}
81532a437dbSMikhail Rudenko 
81632a437dbSMikhail Rudenko 	if (ov4689->clock_rate != OV4689_XVCLK_FREQ) {
81732a437dbSMikhail Rudenko 		dev_err(dev,
81832a437dbSMikhail Rudenko 			"External clock rate mismatch: got %d Hz, expected %d Hz\n",
81932a437dbSMikhail Rudenko 			ov4689->clock_rate, OV4689_XVCLK_FREQ);
82032a437dbSMikhail Rudenko 		return -EINVAL;
82132a437dbSMikhail Rudenko 	}
82232a437dbSMikhail Rudenko 
8238fe37e59SMikhail Rudenko 	ov4689->regmap = devm_cci_regmap_init_i2c(client, 16);
8248fe37e59SMikhail Rudenko 	if (IS_ERR(ov4689->regmap)) {
8258fe37e59SMikhail Rudenko 		ret = PTR_ERR(ov4689->regmap);
8268fe37e59SMikhail Rudenko 		dev_err(dev, "failed to initialize CCI: %d\n", ret);
8278fe37e59SMikhail Rudenko 		return ret;
8288fe37e59SMikhail Rudenko 	}
8298fe37e59SMikhail Rudenko 
83032a437dbSMikhail Rudenko 	ov4689->reset_gpio = devm_gpiod_get_optional(dev, "reset",
83132a437dbSMikhail Rudenko 						     GPIOD_OUT_LOW);
83232a437dbSMikhail Rudenko 	if (IS_ERR(ov4689->reset_gpio)) {
83332a437dbSMikhail Rudenko 		dev_err(dev, "Failed to get reset-gpios\n");
83432a437dbSMikhail Rudenko 		return PTR_ERR(ov4689->reset_gpio);
83532a437dbSMikhail Rudenko 	}
83632a437dbSMikhail Rudenko 
83732a437dbSMikhail Rudenko 	ov4689->pwdn_gpio = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_LOW);
83832a437dbSMikhail Rudenko 	if (IS_ERR(ov4689->pwdn_gpio)) {
83932a437dbSMikhail Rudenko 		dev_err(dev, "Failed to get pwdn-gpios\n");
84032a437dbSMikhail Rudenko 		return PTR_ERR(ov4689->pwdn_gpio);
84132a437dbSMikhail Rudenko 	}
84232a437dbSMikhail Rudenko 
84332a437dbSMikhail Rudenko 	ret = ov4689_configure_regulators(ov4689);
84432a437dbSMikhail Rudenko 	if (ret)
84532a437dbSMikhail Rudenko 		return dev_err_probe(dev, ret,
84632a437dbSMikhail Rudenko 				     "Failed to get power regulators\n");
84732a437dbSMikhail Rudenko 
84832a437dbSMikhail Rudenko 	sd = &ov4689->subdev;
84932a437dbSMikhail Rudenko 	v4l2_i2c_subdev_init(sd, client, &ov4689_subdev_ops);
8505e2974acSMikhail Rudenko 	sd->internal_ops = &ov4689_internal_ops;
8515e2974acSMikhail Rudenko 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
85232a437dbSMikhail Rudenko 	ret = ov4689_initialize_controls(ov4689);
8535e2974acSMikhail Rudenko 	if (ret) {
8545e2974acSMikhail Rudenko 		dev_err(dev, "Failed to initialize controls\n");
8555e2974acSMikhail Rudenko 		return ret;
8565e2974acSMikhail Rudenko 	}
85732a437dbSMikhail Rudenko 
85832a437dbSMikhail Rudenko 	ret = ov4689_power_on(dev);
85932a437dbSMikhail Rudenko 	if (ret)
86032a437dbSMikhail Rudenko 		goto err_free_handler;
86132a437dbSMikhail Rudenko 
86232a437dbSMikhail Rudenko 	ret = ov4689_check_sensor_id(ov4689, client);
86332a437dbSMikhail Rudenko 	if (ret)
86432a437dbSMikhail Rudenko 		goto err_power_off;
86532a437dbSMikhail Rudenko 
86632a437dbSMikhail Rudenko 
86732a437dbSMikhail Rudenko 	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
8685e2974acSMikhail Rudenko 	ov4689->pad.flags = MEDIA_PAD_FL_SOURCE;
86932a437dbSMikhail Rudenko 	ret = media_entity_pads_init(&sd->entity, 1, &ov4689->pad);
87032a437dbSMikhail Rudenko 	if (ret < 0)
87132a437dbSMikhail Rudenko 		goto err_power_off;
87232a437dbSMikhail Rudenko 
8735e2974acSMikhail Rudenko 	sd->state_lock = ov4689->ctrl_handler.lock;
8745e2974acSMikhail Rudenko 	ret = v4l2_subdev_init_finalize(sd);
8755e2974acSMikhail Rudenko 	if (ret) {
8765e2974acSMikhail Rudenko 		dev_err(dev, "Could not register v4l2 device\n");
8775e2974acSMikhail Rudenko 		goto err_clean_entity;
8785e2974acSMikhail Rudenko 	}
8795e2974acSMikhail Rudenko 
88032a437dbSMikhail Rudenko 	pm_runtime_set_active(dev);
881*47e4cf3dSMikhail Rudenko 	pm_runtime_get_noresume(dev);
88232a437dbSMikhail Rudenko 	pm_runtime_enable(dev);
883*47e4cf3dSMikhail Rudenko 	pm_runtime_set_autosuspend_delay(dev, 1000);
884*47e4cf3dSMikhail Rudenko 	pm_runtime_use_autosuspend(dev);
88532a437dbSMikhail Rudenko 
88648f3197aSMikhail Rudenko 	ret = v4l2_async_register_subdev_sensor(sd);
88748f3197aSMikhail Rudenko 	if (ret) {
88848f3197aSMikhail Rudenko 		dev_err(dev, "v4l2 async register subdev failed\n");
88948f3197aSMikhail Rudenko 		goto err_clean_subdev_pm;
89048f3197aSMikhail Rudenko 	}
89148f3197aSMikhail Rudenko 
892*47e4cf3dSMikhail Rudenko 	pm_runtime_mark_last_busy(dev);
893*47e4cf3dSMikhail Rudenko 	pm_runtime_put_autosuspend(dev);
894*47e4cf3dSMikhail Rudenko 
89532a437dbSMikhail Rudenko 	return 0;
89632a437dbSMikhail Rudenko 
89748f3197aSMikhail Rudenko err_clean_subdev_pm:
89848f3197aSMikhail Rudenko 	pm_runtime_disable(dev);
899*47e4cf3dSMikhail Rudenko 	pm_runtime_put_noidle(dev);
9005e2974acSMikhail Rudenko 	v4l2_subdev_cleanup(sd);
90132a437dbSMikhail Rudenko err_clean_entity:
90232a437dbSMikhail Rudenko 	media_entity_cleanup(&sd->entity);
90332a437dbSMikhail Rudenko err_power_off:
90432a437dbSMikhail Rudenko 	ov4689_power_off(dev);
90532a437dbSMikhail Rudenko err_free_handler:
90632a437dbSMikhail Rudenko 	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
90732a437dbSMikhail Rudenko 
90832a437dbSMikhail Rudenko 	return ret;
90932a437dbSMikhail Rudenko }
91032a437dbSMikhail Rudenko 
91132a437dbSMikhail Rudenko static void ov4689_remove(struct i2c_client *client)
91232a437dbSMikhail Rudenko {
91332a437dbSMikhail Rudenko 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
91432a437dbSMikhail Rudenko 	struct ov4689 *ov4689 = to_ov4689(sd);
91532a437dbSMikhail Rudenko 
91632a437dbSMikhail Rudenko 	v4l2_async_unregister_subdev(sd);
91732a437dbSMikhail Rudenko 	media_entity_cleanup(&sd->entity);
9185e2974acSMikhail Rudenko 	v4l2_subdev_cleanup(sd);
91932a437dbSMikhail Rudenko 	v4l2_ctrl_handler_free(&ov4689->ctrl_handler);
92032a437dbSMikhail Rudenko 
92132a437dbSMikhail Rudenko 	pm_runtime_disable(&client->dev);
92232a437dbSMikhail Rudenko 	if (!pm_runtime_status_suspended(&client->dev))
92332a437dbSMikhail Rudenko 		ov4689_power_off(&client->dev);
92432a437dbSMikhail Rudenko 	pm_runtime_set_suspended(&client->dev);
92532a437dbSMikhail Rudenko }
92632a437dbSMikhail Rudenko 
92732a437dbSMikhail Rudenko static const struct of_device_id ov4689_of_match[] = {
92832a437dbSMikhail Rudenko 	{ .compatible = "ovti,ov4689" },
92932a437dbSMikhail Rudenko 	{},
93032a437dbSMikhail Rudenko };
93132a437dbSMikhail Rudenko MODULE_DEVICE_TABLE(of, ov4689_of_match);
93232a437dbSMikhail Rudenko 
93332a437dbSMikhail Rudenko static struct i2c_driver ov4689_i2c_driver = {
93432a437dbSMikhail Rudenko 	.driver = {
93532a437dbSMikhail Rudenko 		.name = "ov4689",
93632a437dbSMikhail Rudenko 		.pm = &ov4689_pm_ops,
93732a437dbSMikhail Rudenko 		.of_match_table = ov4689_of_match,
93832a437dbSMikhail Rudenko 	},
939aaeb31c0SUwe Kleine-König 	.probe = ov4689_probe,
94032a437dbSMikhail Rudenko 	.remove	= ov4689_remove,
94132a437dbSMikhail Rudenko };
94232a437dbSMikhail Rudenko 
94332a437dbSMikhail Rudenko module_i2c_driver(ov4689_i2c_driver);
94432a437dbSMikhail Rudenko 
94532a437dbSMikhail Rudenko MODULE_DESCRIPTION("OmniVision ov4689 sensor driver");
94632a437dbSMikhail Rudenko MODULE_LICENSE("GPL");
947