xref: /openbmc/linux/drivers/media/i2c/ov9650.c (revision aaeb31c0)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
284a15dedSSylwester Nawrocki /*
384a15dedSSylwester Nawrocki  * Omnivision OV9650/OV9652 CMOS Image Sensor driver
484a15dedSSylwester Nawrocki  *
584a15dedSSylwester Nawrocki  * Copyright (C) 2013, Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
684a15dedSSylwester Nawrocki  *
784a15dedSSylwester Nawrocki  * Register definitions and initial settings based on a driver written
884a15dedSSylwester Nawrocki  * by Vladimir Fonov.
984a15dedSSylwester Nawrocki  * Copyright (c) 2010, Vladimir Fonov
1084a15dedSSylwester Nawrocki  */
11b1f5d0aeSAkinobu Mita #include <linux/clk.h>
1284a15dedSSylwester Nawrocki #include <linux/delay.h>
13b1f5d0aeSAkinobu Mita #include <linux/gpio/consumer.h>
1484a15dedSSylwester Nawrocki #include <linux/i2c.h>
1584a15dedSSylwester Nawrocki #include <linux/kernel.h>
1684a15dedSSylwester Nawrocki #include <linux/media.h>
1784a15dedSSylwester Nawrocki #include <linux/module.h>
1884a15dedSSylwester Nawrocki #include <linux/ratelimit.h>
19361f3803SAkinobu Mita #include <linux/regmap.h>
2084a15dedSSylwester Nawrocki #include <linux/slab.h>
2184a15dedSSylwester Nawrocki #include <linux/string.h>
2284a15dedSSylwester Nawrocki #include <linux/videodev2.h>
2384a15dedSSylwester Nawrocki 
2484a15dedSSylwester Nawrocki #include <media/media-entity.h>
256dca6cf0SJavier Martinez Canillas #include <media/v4l2-async.h>
2684a15dedSSylwester Nawrocki #include <media/v4l2-ctrls.h>
2784a15dedSSylwester Nawrocki #include <media/v4l2-device.h>
2884a15dedSSylwester Nawrocki #include <media/v4l2-event.h>
2984a15dedSSylwester Nawrocki #include <media/v4l2-image-sizes.h>
3084a15dedSSylwester Nawrocki #include <media/v4l2-subdev.h>
3184a15dedSSylwester Nawrocki #include <media/v4l2-mediabus.h>
3284a15dedSSylwester Nawrocki 
3384a15dedSSylwester Nawrocki static int debug;
3484a15dedSSylwester Nawrocki module_param(debug, int, 0644);
3584a15dedSSylwester Nawrocki MODULE_PARM_DESC(debug, "Debug level (0-2)");
3684a15dedSSylwester Nawrocki 
3784a15dedSSylwester Nawrocki #define DRIVER_NAME "OV9650"
3884a15dedSSylwester Nawrocki 
3984a15dedSSylwester Nawrocki /*
4084a15dedSSylwester Nawrocki  * OV9650/OV9652 register definitions
4184a15dedSSylwester Nawrocki  */
4284a15dedSSylwester Nawrocki #define REG_GAIN		0x00	/* Gain control, AGC[7:0] */
43f8a7647dSMauro Carvalho Chehab #define REG_BLUE		0x01	/* AWB - Blue channel gain */
44f8a7647dSMauro Carvalho Chehab #define REG_RED			0x02	/* AWB - Red channel gain */
4584a15dedSSylwester Nawrocki #define REG_VREF		0x03	/* [7:6] - AGC[9:8], [5:3]/[2:0] */
4684a15dedSSylwester Nawrocki #define  VREF_GAIN_MASK		0xc0	/* - VREF end/start low 3 bits */
4784a15dedSSylwester Nawrocki #define REG_COM1		0x04
4884a15dedSSylwester Nawrocki #define  COM1_CCIR656		0x40
4984a15dedSSylwester Nawrocki #define REG_B_AVE		0x05
5084a15dedSSylwester Nawrocki #define REG_GB_AVE		0x06
5184a15dedSSylwester Nawrocki #define REG_GR_AVE		0x07
5284a15dedSSylwester Nawrocki #define REG_R_AVE		0x08
5384a15dedSSylwester Nawrocki #define REG_COM2		0x09
5484a15dedSSylwester Nawrocki #define REG_PID			0x0a	/* Product ID MSB */
5584a15dedSSylwester Nawrocki #define REG_VER			0x0b	/* Product ID LSB */
5684a15dedSSylwester Nawrocki #define REG_COM3		0x0c
5784a15dedSSylwester Nawrocki #define  COM3_SWAP		0x40
5884a15dedSSylwester Nawrocki #define  COM3_VARIOPIXEL1	0x04
5984a15dedSSylwester Nawrocki #define REG_COM4		0x0d	/* Vario Pixels  */
6084a15dedSSylwester Nawrocki #define  COM4_VARIOPIXEL2	0x80
6184a15dedSSylwester Nawrocki #define REG_COM5		0x0e	/* System clock options */
6284a15dedSSylwester Nawrocki #define  COM5_SLAVE_MODE	0x10
6384a15dedSSylwester Nawrocki #define  COM5_SYSTEMCLOCK48MHZ	0x80
6484a15dedSSylwester Nawrocki #define REG_COM6		0x0f	/* HREF & ADBLC options */
6584a15dedSSylwester Nawrocki #define REG_AECH		0x10	/* Exposure value, AEC[9:2] */
6684a15dedSSylwester Nawrocki #define REG_CLKRC		0x11	/* Clock control */
6784a15dedSSylwester Nawrocki #define  CLK_EXT		0x40	/* Use external clock directly */
6884a15dedSSylwester Nawrocki #define  CLK_SCALE		0x3f	/* Mask for internal clock scale */
6984a15dedSSylwester Nawrocki #define REG_COM7		0x12	/* SCCB reset, output format */
7084a15dedSSylwester Nawrocki #define  COM7_RESET		0x80
7184a15dedSSylwester Nawrocki #define  COM7_FMT_MASK		0x38
7284a15dedSSylwester Nawrocki #define  COM7_FMT_VGA		0x40
7384a15dedSSylwester Nawrocki #define	 COM7_FMT_CIF		0x20
7484a15dedSSylwester Nawrocki #define  COM7_FMT_QVGA		0x10
7584a15dedSSylwester Nawrocki #define  COM7_FMT_QCIF		0x08
7684a15dedSSylwester Nawrocki #define	 COM7_RGB		0x04
7784a15dedSSylwester Nawrocki #define	 COM7_YUV		0x00
7884a15dedSSylwester Nawrocki #define	 COM7_BAYER		0x01
7984a15dedSSylwester Nawrocki #define	 COM7_PBAYER		0x05
8084a15dedSSylwester Nawrocki #define REG_COM8		0x13	/* AGC/AEC options */
8184a15dedSSylwester Nawrocki #define  COM8_FASTAEC		0x80	/* Enable fast AGC/AEC */
8284a15dedSSylwester Nawrocki #define  COM8_AECSTEP		0x40	/* Unlimited AEC step size */
8384a15dedSSylwester Nawrocki #define  COM8_BFILT		0x20	/* Band filter enable */
8484a15dedSSylwester Nawrocki #define  COM8_AGC		0x04	/* Auto gain enable */
8584a15dedSSylwester Nawrocki #define  COM8_AWB		0x02	/* White balance enable */
8684a15dedSSylwester Nawrocki #define  COM8_AEC		0x01	/* Auto exposure enable */
8784a15dedSSylwester Nawrocki #define REG_COM9		0x14	/* Gain ceiling */
8884a15dedSSylwester Nawrocki #define  COM9_GAIN_CEIL_MASK	0x70	/* */
8984a15dedSSylwester Nawrocki #define REG_COM10		0x15	/* PCLK, HREF, HSYNC signals polarity */
9084a15dedSSylwester Nawrocki #define  COM10_HSYNC		0x40	/* HSYNC instead of HREF */
9184a15dedSSylwester Nawrocki #define  COM10_PCLK_HB		0x20	/* Suppress PCLK on horiz blank */
9284a15dedSSylwester Nawrocki #define  COM10_HREF_REV		0x08	/* Reverse HREF */
9384a15dedSSylwester Nawrocki #define  COM10_VS_LEAD		0x04	/* VSYNC on clock leading edge */
9484a15dedSSylwester Nawrocki #define  COM10_VS_NEG		0x02	/* VSYNC negative */
9584a15dedSSylwester Nawrocki #define  COM10_HS_NEG		0x01	/* HSYNC negative */
9684a15dedSSylwester Nawrocki #define REG_HSTART		0x17	/* Horiz start high bits */
9784a15dedSSylwester Nawrocki #define REG_HSTOP		0x18	/* Horiz stop high bits */
9884a15dedSSylwester Nawrocki #define REG_VSTART		0x19	/* Vert start high bits */
9984a15dedSSylwester Nawrocki #define REG_VSTOP		0x1a	/* Vert stop high bits */
10084a15dedSSylwester Nawrocki #define REG_PSHFT		0x1b	/* Pixel delay after HREF */
10184a15dedSSylwester Nawrocki #define REG_MIDH		0x1c	/* Manufacturer ID MSB */
10284a15dedSSylwester Nawrocki #define REG_MIDL		0x1d	/* Manufufacturer ID LSB */
10384a15dedSSylwester Nawrocki #define REG_MVFP		0x1e	/* Image mirror/flip */
10484a15dedSSylwester Nawrocki #define  MVFP_MIRROR		0x20	/* Mirror image */
10584a15dedSSylwester Nawrocki #define  MVFP_FLIP		0x10	/* Vertical flip */
10684a15dedSSylwester Nawrocki #define REG_BOS			0x20	/* B channel Offset */
10784a15dedSSylwester Nawrocki #define REG_GBOS		0x21	/* Gb channel Offset */
10884a15dedSSylwester Nawrocki #define REG_GROS		0x22	/* Gr channel Offset */
10984a15dedSSylwester Nawrocki #define REG_ROS			0x23	/* R channel Offset */
11084a15dedSSylwester Nawrocki #define REG_AEW			0x24	/* AGC upper limit */
11184a15dedSSylwester Nawrocki #define REG_AEB			0x25	/* AGC lower limit */
11284a15dedSSylwester Nawrocki #define REG_VPT			0x26	/* AGC/AEC fast mode op region */
11384a15dedSSylwester Nawrocki #define REG_BBIAS		0x27	/* B channel output bias */
11484a15dedSSylwester Nawrocki #define REG_GBBIAS		0x28	/* Gb channel output bias */
11584a15dedSSylwester Nawrocki #define REG_GRCOM		0x29	/* Analog BLC & regulator */
11684a15dedSSylwester Nawrocki #define REG_EXHCH		0x2a	/* Dummy pixel insert MSB */
11784a15dedSSylwester Nawrocki #define REG_EXHCL		0x2b	/* Dummy pixel insert LSB */
11884a15dedSSylwester Nawrocki #define REG_RBIAS		0x2c	/* R channel output bias */
11984a15dedSSylwester Nawrocki #define REG_ADVFL		0x2d	/* LSB of dummy line insert */
12084a15dedSSylwester Nawrocki #define REG_ADVFH		0x2e	/* MSB of dummy line insert */
12184a15dedSSylwester Nawrocki #define REG_YAVE		0x2f	/* Y/G channel average value */
12284a15dedSSylwester Nawrocki #define REG_HSYST		0x30	/* HSYNC rising edge delay LSB*/
12384a15dedSSylwester Nawrocki #define REG_HSYEN		0x31	/* HSYNC falling edge delay LSB*/
12484a15dedSSylwester Nawrocki #define REG_HREF		0x32	/* HREF pieces */
12584a15dedSSylwester Nawrocki #define REG_CHLF		0x33	/* reserved */
12684a15dedSSylwester Nawrocki #define REG_ADC			0x37	/* reserved */
12784a15dedSSylwester Nawrocki #define REG_ACOM		0x38	/* reserved */
12884a15dedSSylwester Nawrocki #define REG_OFON		0x39	/* Power down register */
12984a15dedSSylwester Nawrocki #define  OFON_PWRDN		0x08	/* Power down bit */
13084a15dedSSylwester Nawrocki #define REG_TSLB		0x3a	/* YUVU format */
13184a15dedSSylwester Nawrocki #define  TSLB_YUYV_MASK		0x0c	/* UYVY or VYUY - see com13 */
13284a15dedSSylwester Nawrocki #define REG_COM11		0x3b	/* Night mode, banding filter enable */
13384a15dedSSylwester Nawrocki #define  COM11_NIGHT		0x80	/* Night mode enable */
13484a15dedSSylwester Nawrocki #define  COM11_NMFR		0x60	/* Two bit NM frame rate */
13584a15dedSSylwester Nawrocki #define  COM11_BANDING		0x01	/* Banding filter */
13684a15dedSSylwester Nawrocki #define  COM11_AEC_REF_MASK	0x18	/* AEC reference area selection */
13784a15dedSSylwester Nawrocki #define REG_COM12		0x3c	/* HREF option, UV average */
13884a15dedSSylwester Nawrocki #define  COM12_HREF		0x80	/* HREF always */
13984a15dedSSylwester Nawrocki #define REG_COM13		0x3d	/* Gamma selection, Color matrix en. */
14084a15dedSSylwester Nawrocki #define  COM13_GAMMA		0x80	/* Gamma enable */
14184a15dedSSylwester Nawrocki #define	 COM13_UVSAT		0x40	/* UV saturation auto adjustment */
14284a15dedSSylwester Nawrocki #define  COM13_UVSWAP		0x01	/* V before U - w/TSLB */
14384a15dedSSylwester Nawrocki #define REG_COM14		0x3e	/* Edge enhancement options */
14484a15dedSSylwester Nawrocki #define  COM14_EDGE_EN		0x02
14584a15dedSSylwester Nawrocki #define  COM14_EEF_X2		0x01
14684a15dedSSylwester Nawrocki #define REG_EDGE		0x3f	/* Edge enhancement factor */
14784a15dedSSylwester Nawrocki #define  EDGE_FACTOR_MASK	0x0f
14884a15dedSSylwester Nawrocki #define REG_COM15		0x40	/* Output range, RGB 555/565 */
14984a15dedSSylwester Nawrocki #define  COM15_R10F0		0x00	/* Data range 10 to F0 */
15084a15dedSSylwester Nawrocki #define	 COM15_R01FE		0x80	/* 01 to FE */
15184a15dedSSylwester Nawrocki #define  COM15_R00FF		0xc0	/* 00 to FF */
15284a15dedSSylwester Nawrocki #define  COM15_RGB565		0x10	/* RGB565 output */
15384a15dedSSylwester Nawrocki #define  COM15_RGB555		0x30	/* RGB555 output */
15484a15dedSSylwester Nawrocki #define  COM15_SWAPRB		0x04	/* Swap R&B */
15584a15dedSSylwester Nawrocki #define REG_COM16		0x41	/* Color matrix coeff options */
15684a15dedSSylwester Nawrocki #define REG_COM17		0x42	/* Single frame out, banding filter */
15784a15dedSSylwester Nawrocki /* n = 1...9, 0x4f..0x57 */
15884a15dedSSylwester Nawrocki #define	REG_MTX(__n)		(0x4f + (__n) - 1)
15984a15dedSSylwester Nawrocki #define REG_MTXS		0x58
16084a15dedSSylwester Nawrocki /* Lens Correction Option 1...5, __n = 0...5 */
16184a15dedSSylwester Nawrocki #define REG_LCC(__n)		(0x62 + (__n) - 1)
16284a15dedSSylwester Nawrocki #define  LCC5_LCC_ENABLE	0x01	/* LCC5, enable lens correction */
16384a15dedSSylwester Nawrocki #define  LCC5_LCC_COLOR		0x04
16484a15dedSSylwester Nawrocki #define REG_MANU		0x67	/* Manual U value */
16584a15dedSSylwester Nawrocki #define REG_MANV		0x68	/* Manual V value */
16684a15dedSSylwester Nawrocki #define REG_HV			0x69	/* Manual banding filter MSB */
16784a15dedSSylwester Nawrocki #define REG_MBD			0x6a	/* Manual banding filter value */
16884a15dedSSylwester Nawrocki #define REG_DBLV		0x6b	/* reserved */
16984a15dedSSylwester Nawrocki #define REG_GSP			0x6c	/* Gamma curve */
17084a15dedSSylwester Nawrocki #define  GSP_LEN		15
17184a15dedSSylwester Nawrocki #define REG_GST			0x7c	/* Gamma curve */
17284a15dedSSylwester Nawrocki #define  GST_LEN		15
17384a15dedSSylwester Nawrocki #define REG_COM21		0x8b
17484a15dedSSylwester Nawrocki #define REG_COM22		0x8c	/* Edge enhancement, denoising */
17584a15dedSSylwester Nawrocki #define  COM22_WHTPCOR		0x02	/* White pixel correction enable */
17684a15dedSSylwester Nawrocki #define  COM22_WHTPCOROPT	0x01	/* White pixel correction option */
17784a15dedSSylwester Nawrocki #define  COM22_DENOISE		0x10	/* White pixel correction option */
17884a15dedSSylwester Nawrocki #define REG_COM23		0x8d	/* Color bar test, color gain */
17984a15dedSSylwester Nawrocki #define  COM23_TEST_MODE	0x10
18084a15dedSSylwester Nawrocki #define REG_DBLC1		0x8f	/* Digital BLC */
18184a15dedSSylwester Nawrocki #define REG_DBLC_B		0x90	/* Digital BLC B channel offset */
18284a15dedSSylwester Nawrocki #define REG_DBLC_R		0x91	/* Digital BLC R channel offset */
18384a15dedSSylwester Nawrocki #define REG_DM_LNL		0x92	/* Dummy line low 8 bits */
18484a15dedSSylwester Nawrocki #define REG_DM_LNH		0x93	/* Dummy line high 8 bits */
18584a15dedSSylwester Nawrocki #define REG_LCCFB		0x9d	/* Lens Correction B channel */
18684a15dedSSylwester Nawrocki #define REG_LCCFR		0x9e	/* Lens Correction R channel */
18784a15dedSSylwester Nawrocki #define REG_DBLC_GB		0x9f	/* Digital BLC GB chan offset */
18884a15dedSSylwester Nawrocki #define REG_DBLC_GR		0xa0	/* Digital BLC GR chan offset */
18984a15dedSSylwester Nawrocki #define REG_AECHM		0xa1	/* Exposure value - bits AEC[15:10] */
19084a15dedSSylwester Nawrocki #define REG_BD50ST		0xa2	/* Banding filter value for 50Hz */
19184a15dedSSylwester Nawrocki #define REG_BD60ST		0xa3	/* Banding filter value for 60Hz */
19284a15dedSSylwester Nawrocki #define REG_NULL		0xff	/* Array end token */
19384a15dedSSylwester Nawrocki 
19484a15dedSSylwester Nawrocki #define DEF_CLKRC		0x80
19584a15dedSSylwester Nawrocki 
19684a15dedSSylwester Nawrocki #define OV965X_ID(_msb, _lsb)	((_msb) << 8 | (_lsb))
19784a15dedSSylwester Nawrocki #define OV9650_ID		0x9650
19884a15dedSSylwester Nawrocki #define OV9652_ID		0x9652
19984a15dedSSylwester Nawrocki 
20084a15dedSSylwester Nawrocki struct ov965x_ctrls {
20184a15dedSSylwester Nawrocki 	struct v4l2_ctrl_handler handler;
20284a15dedSSylwester Nawrocki 	struct {
20384a15dedSSylwester Nawrocki 		struct v4l2_ctrl *auto_exp;
20484a15dedSSylwester Nawrocki 		struct v4l2_ctrl *exposure;
20584a15dedSSylwester Nawrocki 	};
20684a15dedSSylwester Nawrocki 	struct {
20784a15dedSSylwester Nawrocki 		struct v4l2_ctrl *auto_wb;
20884a15dedSSylwester Nawrocki 		struct v4l2_ctrl *blue_balance;
20984a15dedSSylwester Nawrocki 		struct v4l2_ctrl *red_balance;
21084a15dedSSylwester Nawrocki 	};
21184a15dedSSylwester Nawrocki 	struct {
21284a15dedSSylwester Nawrocki 		struct v4l2_ctrl *hflip;
21384a15dedSSylwester Nawrocki 		struct v4l2_ctrl *vflip;
21484a15dedSSylwester Nawrocki 	};
21584a15dedSSylwester Nawrocki 	struct {
21684a15dedSSylwester Nawrocki 		struct v4l2_ctrl *auto_gain;
21784a15dedSSylwester Nawrocki 		struct v4l2_ctrl *gain;
21884a15dedSSylwester Nawrocki 	};
21984a15dedSSylwester Nawrocki 	struct v4l2_ctrl *brightness;
22084a15dedSSylwester Nawrocki 	struct v4l2_ctrl *saturation;
22184a15dedSSylwester Nawrocki 	struct v4l2_ctrl *sharpness;
22284a15dedSSylwester Nawrocki 	struct v4l2_ctrl *light_freq;
22384a15dedSSylwester Nawrocki 	u8 update;
22484a15dedSSylwester Nawrocki };
22584a15dedSSylwester Nawrocki 
22684a15dedSSylwester Nawrocki struct ov965x_framesize {
22784a15dedSSylwester Nawrocki 	u16 width;
22884a15dedSSylwester Nawrocki 	u16 height;
22984a15dedSSylwester Nawrocki 	u16 max_exp_lines;
23084a15dedSSylwester Nawrocki 	const u8 *regs;
23184a15dedSSylwester Nawrocki };
23284a15dedSSylwester Nawrocki 
23384a15dedSSylwester Nawrocki struct ov965x_interval {
23484a15dedSSylwester Nawrocki 	struct v4l2_fract interval;
23584a15dedSSylwester Nawrocki 	/* Maximum resolution for this interval */
23684a15dedSSylwester Nawrocki 	struct v4l2_frmsize_discrete size;
23784a15dedSSylwester Nawrocki 	u8 clkrc_div;
23884a15dedSSylwester Nawrocki };
23984a15dedSSylwester Nawrocki 
24084a15dedSSylwester Nawrocki enum gpio_id {
24184a15dedSSylwester Nawrocki 	GPIO_PWDN,
24284a15dedSSylwester Nawrocki 	GPIO_RST,
24384a15dedSSylwester Nawrocki 	NUM_GPIOS,
24484a15dedSSylwester Nawrocki };
24584a15dedSSylwester Nawrocki 
24684a15dedSSylwester Nawrocki struct ov965x {
24784a15dedSSylwester Nawrocki 	struct v4l2_subdev sd;
24884a15dedSSylwester Nawrocki 	struct media_pad pad;
24984a15dedSSylwester Nawrocki 	enum v4l2_mbus_type bus_type;
250b1f5d0aeSAkinobu Mita 	struct gpio_desc *gpios[NUM_GPIOS];
25184a15dedSSylwester Nawrocki 	/* External master clock frequency */
25284a15dedSSylwester Nawrocki 	unsigned long mclk_frequency;
253b1f5d0aeSAkinobu Mita 	struct clk *clk;
25484a15dedSSylwester Nawrocki 
25584a15dedSSylwester Nawrocki 	/* Protects the struct fields below */
25684a15dedSSylwester Nawrocki 	struct mutex lock;
25784a15dedSSylwester Nawrocki 
258361f3803SAkinobu Mita 	struct regmap *regmap;
25984a15dedSSylwester Nawrocki 
26084a15dedSSylwester Nawrocki 	/* Exposure row interval in us */
26184a15dedSSylwester Nawrocki 	unsigned int exp_row_interval;
26284a15dedSSylwester Nawrocki 
26384a15dedSSylwester Nawrocki 	unsigned short id;
26484a15dedSSylwester Nawrocki 	const struct ov965x_framesize *frame_size;
26584a15dedSSylwester Nawrocki 	/* YUYV sequence (pixel format) control register */
26684a15dedSSylwester Nawrocki 	u8 tslb_reg;
26784a15dedSSylwester Nawrocki 	struct v4l2_mbus_framefmt format;
26884a15dedSSylwester Nawrocki 
26984a15dedSSylwester Nawrocki 	struct ov965x_ctrls ctrls;
27084a15dedSSylwester Nawrocki 	/* Pointer to frame rate control data structure */
27184a15dedSSylwester Nawrocki 	const struct ov965x_interval *fiv;
27284a15dedSSylwester Nawrocki 
27384a15dedSSylwester Nawrocki 	int streaming;
27484a15dedSSylwester Nawrocki 	int power;
27584a15dedSSylwester Nawrocki 
27684a15dedSSylwester Nawrocki 	u8 apply_frame_fmt;
27784a15dedSSylwester Nawrocki };
27884a15dedSSylwester Nawrocki 
27984a15dedSSylwester Nawrocki struct i2c_rv {
28084a15dedSSylwester Nawrocki 	u8 addr;
28184a15dedSSylwester Nawrocki 	u8 value;
28284a15dedSSylwester Nawrocki };
28384a15dedSSylwester Nawrocki 
28484a15dedSSylwester Nawrocki static const struct i2c_rv ov965x_init_regs[] = {
28584a15dedSSylwester Nawrocki 	{ REG_COM2, 0x10 },	/* Set soft sleep mode */
28684a15dedSSylwester Nawrocki 	{ REG_COM5, 0x00 },	/* System clock options */
28784a15dedSSylwester Nawrocki 	{ REG_COM2, 0x01 },	/* Output drive, soft sleep mode */
28884a15dedSSylwester Nawrocki 	{ REG_COM10, 0x00 },	/* Slave mode, HREF vs HSYNC, signals negate */
28984a15dedSSylwester Nawrocki 	{ REG_EDGE, 0xa6 },	/* Edge enhancement treshhold and factor */
29084a15dedSSylwester Nawrocki 	{ REG_COM16, 0x02 },	/* Color matrix coeff double option */
29184a15dedSSylwester Nawrocki 	{ REG_COM17, 0x08 },	/* Single frame out, banding filter */
29284a15dedSSylwester Nawrocki 	{ 0x16, 0x06 },
29384a15dedSSylwester Nawrocki 	{ REG_CHLF, 0xc0 },	/* Reserved  */
29484a15dedSSylwester Nawrocki 	{ 0x34, 0xbf },
29584a15dedSSylwester Nawrocki 	{ 0xa8, 0x80 },
29684a15dedSSylwester Nawrocki 	{ 0x96, 0x04 },
29784a15dedSSylwester Nawrocki 	{ 0x8e, 0x00 },
29884a15dedSSylwester Nawrocki 	{ REG_COM12, 0x77 },	/* HREF option, UV average  */
29984a15dedSSylwester Nawrocki 	{ 0x8b, 0x06 },
30084a15dedSSylwester Nawrocki 	{ 0x35, 0x91 },
30184a15dedSSylwester Nawrocki 	{ 0x94, 0x88 },
30284a15dedSSylwester Nawrocki 	{ 0x95, 0x88 },
30384a15dedSSylwester Nawrocki 	{ REG_COM15, 0xc1 },	/* Output range, RGB 555/565 */
30484a15dedSSylwester Nawrocki 	{ REG_GRCOM, 0x2f },	/* Analog BLC & regulator */
30584a15dedSSylwester Nawrocki 	{ REG_COM6, 0x43 },	/* HREF & ADBLC options */
30684a15dedSSylwester Nawrocki 	{ REG_COM8, 0xe5 },	/* AGC/AEC options */
30784a15dedSSylwester Nawrocki 	{ REG_COM13, 0x90 },	/* Gamma selection, colour matrix, UV delay */
30884a15dedSSylwester Nawrocki 	{ REG_HV, 0x80 },	/* Manual banding filter MSB  */
30984a15dedSSylwester Nawrocki 	{ 0x5c, 0x96 },		/* Reserved up to 0xa5 */
31084a15dedSSylwester Nawrocki 	{ 0x5d, 0x96 },
31184a15dedSSylwester Nawrocki 	{ 0x5e, 0x10 },
31284a15dedSSylwester Nawrocki 	{ 0x59, 0xeb },
31384a15dedSSylwester Nawrocki 	{ 0x5a, 0x9c },
31484a15dedSSylwester Nawrocki 	{ 0x5b, 0x55 },
31584a15dedSSylwester Nawrocki 	{ 0x43, 0xf0 },
31684a15dedSSylwester Nawrocki 	{ 0x44, 0x10 },
31784a15dedSSylwester Nawrocki 	{ 0x45, 0x55 },
31884a15dedSSylwester Nawrocki 	{ 0x46, 0x86 },
31984a15dedSSylwester Nawrocki 	{ 0x47, 0x64 },
32084a15dedSSylwester Nawrocki 	{ 0x48, 0x86 },
32184a15dedSSylwester Nawrocki 	{ 0x5f, 0xe0 },
32284a15dedSSylwester Nawrocki 	{ 0x60, 0x8c },
32384a15dedSSylwester Nawrocki 	{ 0x61, 0x20 },
32484a15dedSSylwester Nawrocki 	{ 0xa5, 0xd9 },
32584a15dedSSylwester Nawrocki 	{ 0xa4, 0x74 },		/* reserved */
32684a15dedSSylwester Nawrocki 	{ REG_COM23, 0x02 },	/* Color gain analog/_digital_ */
32784a15dedSSylwester Nawrocki 	{ REG_COM8, 0xe7 },	/* Enable AEC, AWB, AEC */
32884a15dedSSylwester Nawrocki 	{ REG_COM22, 0x23 },	/* Edge enhancement, denoising */
32984a15dedSSylwester Nawrocki 	{ 0xa9, 0xb8 },
33084a15dedSSylwester Nawrocki 	{ 0xaa, 0x92 },
33184a15dedSSylwester Nawrocki 	{ 0xab, 0x0a },
33284a15dedSSylwester Nawrocki 	{ REG_DBLC1, 0xdf },	/* Digital BLC */
33384a15dedSSylwester Nawrocki 	{ REG_DBLC_B, 0x00 },	/* Digital BLC B chan offset */
33484a15dedSSylwester Nawrocki 	{ REG_DBLC_R, 0x00 },	/* Digital BLC R chan offset */
33584a15dedSSylwester Nawrocki 	{ REG_DBLC_GB, 0x00 },	/* Digital BLC GB chan offset */
33684a15dedSSylwester Nawrocki 	{ REG_DBLC_GR, 0x00 },
33784a15dedSSylwester Nawrocki 	{ REG_COM9, 0x3a },	/* Gain ceiling 16x */
33884a15dedSSylwester Nawrocki 	{ REG_NULL, 0 }
33984a15dedSSylwester Nawrocki };
34084a15dedSSylwester Nawrocki 
34184a15dedSSylwester Nawrocki #define NUM_FMT_REGS 14
34284a15dedSSylwester Nawrocki /*
34384a15dedSSylwester Nawrocki  * COM7,  COM3,  COM4, HSTART, HSTOP, HREF, VSTART, VSTOP, VREF,
34484a15dedSSylwester Nawrocki  * EXHCH, EXHCL, ADC,  OCOM,   OFON
34584a15dedSSylwester Nawrocki  */
34684a15dedSSylwester Nawrocki static const u8 frame_size_reg_addr[NUM_FMT_REGS] = {
34784a15dedSSylwester Nawrocki 	0x12, 0x0c, 0x0d, 0x17, 0x18, 0x32, 0x19, 0x1a, 0x03,
34884a15dedSSylwester Nawrocki 	0x2a, 0x2b, 0x37, 0x38, 0x39,
34984a15dedSSylwester Nawrocki };
35084a15dedSSylwester Nawrocki 
35184a15dedSSylwester Nawrocki static const u8 ov965x_sxga_regs[NUM_FMT_REGS] = {
35284a15dedSSylwester Nawrocki 	0x00, 0x00, 0x00, 0x1e, 0xbe, 0xbf, 0x01, 0x81, 0x12,
35384a15dedSSylwester Nawrocki 	0x10, 0x34, 0x81, 0x93, 0x51,
35484a15dedSSylwester Nawrocki };
35584a15dedSSylwester Nawrocki 
35684a15dedSSylwester Nawrocki static const u8 ov965x_vga_regs[NUM_FMT_REGS] = {
35784a15dedSSylwester Nawrocki 	0x40, 0x04, 0x80, 0x26, 0xc6, 0xed, 0x01, 0x3d, 0x00,
35884a15dedSSylwester Nawrocki 	0x10, 0x40, 0x91, 0x12, 0x43,
35984a15dedSSylwester Nawrocki };
36084a15dedSSylwester Nawrocki 
36184a15dedSSylwester Nawrocki /* Determined empirically. */
36284a15dedSSylwester Nawrocki static const u8 ov965x_qvga_regs[NUM_FMT_REGS] = {
36384a15dedSSylwester Nawrocki 	0x10, 0x04, 0x80, 0x25, 0xc5, 0xbf, 0x00, 0x80, 0x12,
36484a15dedSSylwester Nawrocki 	0x10, 0x40, 0x91, 0x12, 0x43,
36584a15dedSSylwester Nawrocki };
36684a15dedSSylwester Nawrocki 
36784a15dedSSylwester Nawrocki static const struct ov965x_framesize ov965x_framesizes[] = {
36884a15dedSSylwester Nawrocki 	{
36984a15dedSSylwester Nawrocki 		.width		= SXGA_WIDTH,
37084a15dedSSylwester Nawrocki 		.height		= SXGA_HEIGHT,
37184a15dedSSylwester Nawrocki 		.regs		= ov965x_sxga_regs,
37284a15dedSSylwester Nawrocki 		.max_exp_lines	= 1048,
37384a15dedSSylwester Nawrocki 	}, {
37484a15dedSSylwester Nawrocki 		.width		= VGA_WIDTH,
37584a15dedSSylwester Nawrocki 		.height		= VGA_HEIGHT,
37684a15dedSSylwester Nawrocki 		.regs		= ov965x_vga_regs,
37784a15dedSSylwester Nawrocki 		.max_exp_lines	= 498,
37884a15dedSSylwester Nawrocki 	}, {
37984a15dedSSylwester Nawrocki 		.width		= QVGA_WIDTH,
38084a15dedSSylwester Nawrocki 		.height		= QVGA_HEIGHT,
38184a15dedSSylwester Nawrocki 		.regs		= ov965x_qvga_regs,
38284a15dedSSylwester Nawrocki 		.max_exp_lines	= 248,
38384a15dedSSylwester Nawrocki 	},
38484a15dedSSylwester Nawrocki };
38584a15dedSSylwester Nawrocki 
38684a15dedSSylwester Nawrocki struct ov965x_pixfmt {
387f5fe58fdSBoris BREZILLON 	u32 code;
38884a15dedSSylwester Nawrocki 	u32 colorspace;
38984a15dedSSylwester Nawrocki 	/* REG_TSLB value, only bits [3:2] may be set. */
39084a15dedSSylwester Nawrocki 	u8 tslb_reg;
39184a15dedSSylwester Nawrocki };
39284a15dedSSylwester Nawrocki 
39384a15dedSSylwester Nawrocki static const struct ov965x_pixfmt ov965x_formats[] = {
394f5fe58fdSBoris BREZILLON 	{ MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 0x00},
395f5fe58fdSBoris BREZILLON 	{ MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG, 0x04},
396f5fe58fdSBoris BREZILLON 	{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG, 0x0c},
397f5fe58fdSBoris BREZILLON 	{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 0x08},
39884a15dedSSylwester Nawrocki };
39984a15dedSSylwester Nawrocki 
40084a15dedSSylwester Nawrocki /*
40184a15dedSSylwester Nawrocki  * This table specifies possible frame resolution and interval
40284a15dedSSylwester Nawrocki  * combinations. Default CLKRC[5:0] divider values are valid
40384a15dedSSylwester Nawrocki  * only for 24 MHz external clock frequency.
40484a15dedSSylwester Nawrocki  */
40584a15dedSSylwester Nawrocki static struct ov965x_interval ov965x_intervals[] = {
40684a15dedSSylwester Nawrocki 	{{ 100, 625 }, { SXGA_WIDTH, SXGA_HEIGHT }, 0 },  /* 6.25 fps */
40784a15dedSSylwester Nawrocki 	{{ 10,  125 }, { VGA_WIDTH, VGA_HEIGHT },   1 },  /* 12.5 fps */
40884a15dedSSylwester Nawrocki 	{{ 10,  125 }, { QVGA_WIDTH, QVGA_HEIGHT }, 3 },  /* 12.5 fps */
40984a15dedSSylwester Nawrocki 	{{ 1,   25  }, { VGA_WIDTH, VGA_HEIGHT },   0 },  /* 25 fps */
41084a15dedSSylwester Nawrocki 	{{ 1,   25  }, { QVGA_WIDTH, QVGA_HEIGHT }, 1 },  /* 25 fps */
41184a15dedSSylwester Nawrocki };
41284a15dedSSylwester Nawrocki 
ctrl_to_sd(struct v4l2_ctrl * ctrl)41384a15dedSSylwester Nawrocki static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
41484a15dedSSylwester Nawrocki {
41584a15dedSSylwester Nawrocki 	return &container_of(ctrl->handler, struct ov965x, ctrls.handler)->sd;
41684a15dedSSylwester Nawrocki }
41784a15dedSSylwester Nawrocki 
to_ov965x(struct v4l2_subdev * sd)41884a15dedSSylwester Nawrocki static inline struct ov965x *to_ov965x(struct v4l2_subdev *sd)
41984a15dedSSylwester Nawrocki {
42084a15dedSSylwester Nawrocki 	return container_of(sd, struct ov965x, sd);
42184a15dedSSylwester Nawrocki }
42284a15dedSSylwester Nawrocki 
ov965x_read(struct ov965x * ov965x,u8 addr,u8 * val)423361f3803SAkinobu Mita static int ov965x_read(struct ov965x *ov965x, u8 addr, u8 *val)
42484a15dedSSylwester Nawrocki {
42584a15dedSSylwester Nawrocki 	int ret;
426361f3803SAkinobu Mita 	unsigned int buf;
42784a15dedSSylwester Nawrocki 
428361f3803SAkinobu Mita 	ret = regmap_read(ov965x->regmap, addr, &buf);
429361f3803SAkinobu Mita 	if (!ret)
43084a15dedSSylwester Nawrocki 		*val = buf;
431146c45efSArnd Bergmann 	else
432146c45efSArnd Bergmann 		*val = -1;
43384a15dedSSylwester Nawrocki 
434361f3803SAkinobu Mita 	v4l2_dbg(2, debug, &ov965x->sd, "%s: 0x%02x @ 0x%02x. (%d)\n",
43584a15dedSSylwester Nawrocki 		 __func__, *val, addr, ret);
43684a15dedSSylwester Nawrocki 
437361f3803SAkinobu Mita 	return ret;
43884a15dedSSylwester Nawrocki }
43984a15dedSSylwester Nawrocki 
ov965x_write(struct ov965x * ov965x,u8 addr,u8 val)440361f3803SAkinobu Mita static int ov965x_write(struct ov965x *ov965x, u8 addr, u8 val)
44184a15dedSSylwester Nawrocki {
442361f3803SAkinobu Mita 	int ret;
44384a15dedSSylwester Nawrocki 
444361f3803SAkinobu Mita 	ret = regmap_write(ov965x->regmap, addr, val);
44584a15dedSSylwester Nawrocki 
446361f3803SAkinobu Mita 	v4l2_dbg(2, debug, &ov965x->sd, "%s: 0x%02x @ 0x%02X (%d)\n",
44784a15dedSSylwester Nawrocki 		 __func__, val, addr, ret);
44884a15dedSSylwester Nawrocki 
449361f3803SAkinobu Mita 	return ret;
45084a15dedSSylwester Nawrocki }
45184a15dedSSylwester Nawrocki 
ov965x_write_array(struct ov965x * ov965x,const struct i2c_rv * regs)452361f3803SAkinobu Mita static int ov965x_write_array(struct ov965x *ov965x,
45384a15dedSSylwester Nawrocki 			      const struct i2c_rv *regs)
45484a15dedSSylwester Nawrocki {
45584a15dedSSylwester Nawrocki 	int i, ret = 0;
45684a15dedSSylwester Nawrocki 
45784a15dedSSylwester Nawrocki 	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
458361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, regs[i].addr, regs[i].value);
45984a15dedSSylwester Nawrocki 
46084a15dedSSylwester Nawrocki 	return ret;
46184a15dedSSylwester Nawrocki }
46284a15dedSSylwester Nawrocki 
ov965x_set_default_gamma_curve(struct ov965x * ov965x)46384a15dedSSylwester Nawrocki static int ov965x_set_default_gamma_curve(struct ov965x *ov965x)
46484a15dedSSylwester Nawrocki {
46584a15dedSSylwester Nawrocki 	static const u8 gamma_curve[] = {
46684a15dedSSylwester Nawrocki 		/* Values taken from OV application note. */
46784a15dedSSylwester Nawrocki 		0x40, 0x30, 0x4b, 0x60, 0x70, 0x70, 0x70, 0x70,
46884a15dedSSylwester Nawrocki 		0x60, 0x60, 0x50, 0x48, 0x3a, 0x2e, 0x28, 0x22,
46984a15dedSSylwester Nawrocki 		0x04, 0x07, 0x10, 0x28,	0x36, 0x44, 0x52, 0x60,
47084a15dedSSylwester Nawrocki 		0x6c, 0x78, 0x8c, 0x9e, 0xbb, 0xd2, 0xe6
47184a15dedSSylwester Nawrocki 	};
47284a15dedSSylwester Nawrocki 	u8 addr = REG_GSP;
47384a15dedSSylwester Nawrocki 	unsigned int i;
47484a15dedSSylwester Nawrocki 
47584a15dedSSylwester Nawrocki 	for (i = 0; i < ARRAY_SIZE(gamma_curve); i++) {
476361f3803SAkinobu Mita 		int ret = ov965x_write(ov965x, addr, gamma_curve[i]);
4770a7a1345SHugues Fruchet 
47884a15dedSSylwester Nawrocki 		if (ret < 0)
47984a15dedSSylwester Nawrocki 			return ret;
48084a15dedSSylwester Nawrocki 		addr++;
48184a15dedSSylwester Nawrocki 	}
48284a15dedSSylwester Nawrocki 
48384a15dedSSylwester Nawrocki 	return 0;
48484a15dedSSylwester Nawrocki };
48584a15dedSSylwester Nawrocki 
ov965x_set_color_matrix(struct ov965x * ov965x)48684a15dedSSylwester Nawrocki static int ov965x_set_color_matrix(struct ov965x *ov965x)
48784a15dedSSylwester Nawrocki {
48884a15dedSSylwester Nawrocki 	static const u8 mtx[] = {
48984a15dedSSylwester Nawrocki 		/* MTX1..MTX9, MTXS */
49084a15dedSSylwester Nawrocki 		0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38, 0x40, 0x40, 0x40, 0x0d
49184a15dedSSylwester Nawrocki 	};
49284a15dedSSylwester Nawrocki 	u8 addr = REG_MTX(1);
49384a15dedSSylwester Nawrocki 	unsigned int i;
49484a15dedSSylwester Nawrocki 
49584a15dedSSylwester Nawrocki 	for (i = 0; i < ARRAY_SIZE(mtx); i++) {
496361f3803SAkinobu Mita 		int ret = ov965x_write(ov965x, addr, mtx[i]);
4970a7a1345SHugues Fruchet 
49884a15dedSSylwester Nawrocki 		if (ret < 0)
49984a15dedSSylwester Nawrocki 			return ret;
50084a15dedSSylwester Nawrocki 		addr++;
50184a15dedSSylwester Nawrocki 	}
50284a15dedSSylwester Nawrocki 
50384a15dedSSylwester Nawrocki 	return 0;
50484a15dedSSylwester Nawrocki }
50584a15dedSSylwester Nawrocki 
__ov965x_set_power(struct ov965x * ov965x,int on)506b1f5d0aeSAkinobu Mita static int __ov965x_set_power(struct ov965x *ov965x, int on)
50784a15dedSSylwester Nawrocki {
50884a15dedSSylwester Nawrocki 	if (on) {
509b1f5d0aeSAkinobu Mita 		int ret = clk_prepare_enable(ov965x->clk);
510b1f5d0aeSAkinobu Mita 
511b1f5d0aeSAkinobu Mita 		if (ret)
512b1f5d0aeSAkinobu Mita 			return ret;
513b1f5d0aeSAkinobu Mita 
514b1f5d0aeSAkinobu Mita 		gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 0);
515b1f5d0aeSAkinobu Mita 		gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 0);
5160df03379SNicholas Mc Guire 		msleep(25);
51784a15dedSSylwester Nawrocki 	} else {
518b1f5d0aeSAkinobu Mita 		gpiod_set_value_cansleep(ov965x->gpios[GPIO_RST], 1);
519b1f5d0aeSAkinobu Mita 		gpiod_set_value_cansleep(ov965x->gpios[GPIO_PWDN], 1);
520b1f5d0aeSAkinobu Mita 
521b1f5d0aeSAkinobu Mita 		clk_disable_unprepare(ov965x->clk);
52284a15dedSSylwester Nawrocki 	}
52384a15dedSSylwester Nawrocki 
52484a15dedSSylwester Nawrocki 	ov965x->streaming = 0;
525b1f5d0aeSAkinobu Mita 
526b1f5d0aeSAkinobu Mita 	return 0;
52784a15dedSSylwester Nawrocki }
52884a15dedSSylwester Nawrocki 
ov965x_s_power(struct v4l2_subdev * sd,int on)52984a15dedSSylwester Nawrocki static int ov965x_s_power(struct v4l2_subdev *sd, int on)
53084a15dedSSylwester Nawrocki {
53184a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
53284a15dedSSylwester Nawrocki 	int ret = 0;
53384a15dedSSylwester Nawrocki 
534361f3803SAkinobu Mita 	v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on);
53584a15dedSSylwester Nawrocki 
53684a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
53784a15dedSSylwester Nawrocki 	if (ov965x->power == !on) {
538b1f5d0aeSAkinobu Mita 		ret = __ov965x_set_power(ov965x, on);
539b1f5d0aeSAkinobu Mita 		if (!ret && on) {
540361f3803SAkinobu Mita 			ret = ov965x_write_array(ov965x,
54184a15dedSSylwester Nawrocki 						 ov965x_init_regs);
54284a15dedSSylwester Nawrocki 			ov965x->apply_frame_fmt = 1;
54384a15dedSSylwester Nawrocki 			ov965x->ctrls.update = 1;
54484a15dedSSylwester Nawrocki 		}
54584a15dedSSylwester Nawrocki 	}
54684a15dedSSylwester Nawrocki 	if (!ret)
54784a15dedSSylwester Nawrocki 		ov965x->power += on ? 1 : -1;
54884a15dedSSylwester Nawrocki 
54984a15dedSSylwester Nawrocki 	WARN_ON(ov965x->power < 0);
55084a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
55184a15dedSSylwester Nawrocki 	return ret;
55284a15dedSSylwester Nawrocki }
55384a15dedSSylwester Nawrocki 
55484a15dedSSylwester Nawrocki /*
55584a15dedSSylwester Nawrocki  * V4L2 controls
55684a15dedSSylwester Nawrocki  */
55784a15dedSSylwester Nawrocki 
ov965x_update_exposure_ctrl(struct ov965x * ov965x)55884a15dedSSylwester Nawrocki static void ov965x_update_exposure_ctrl(struct ov965x *ov965x)
55984a15dedSSylwester Nawrocki {
56084a15dedSSylwester Nawrocki 	struct v4l2_ctrl *ctrl = ov965x->ctrls.exposure;
56184a15dedSSylwester Nawrocki 	unsigned long fint, trow;
56284a15dedSSylwester Nawrocki 	int min, max, def;
56384a15dedSSylwester Nawrocki 	u8 clkrc;
56484a15dedSSylwester Nawrocki 
56584a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
56684a15dedSSylwester Nawrocki 	if (WARN_ON(!ctrl || !ov965x->frame_size)) {
56784a15dedSSylwester Nawrocki 		mutex_unlock(&ov965x->lock);
56884a15dedSSylwester Nawrocki 		return;
56984a15dedSSylwester Nawrocki 	}
57084a15dedSSylwester Nawrocki 	clkrc = DEF_CLKRC + ov965x->fiv->clkrc_div;
57184a15dedSSylwester Nawrocki 	/* Calculate internal clock frequency */
57284a15dedSSylwester Nawrocki 	fint = ov965x->mclk_frequency * ((clkrc >> 7) + 1) /
57384a15dedSSylwester Nawrocki 				((2 * ((clkrc & 0x3f) + 1)));
57484a15dedSSylwester Nawrocki 	/* and the row interval (in us). */
57584a15dedSSylwester Nawrocki 	trow = (2 * 1520 * 1000000UL) / fint;
57684a15dedSSylwester Nawrocki 	max = ov965x->frame_size->max_exp_lines * trow;
57784a15dedSSylwester Nawrocki 	ov965x->exp_row_interval = trow;
57884a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
57984a15dedSSylwester Nawrocki 
58084a15dedSSylwester Nawrocki 	v4l2_dbg(1, debug, &ov965x->sd, "clkrc: %#x, fi: %lu, tr: %lu, %d\n",
58184a15dedSSylwester Nawrocki 		 clkrc, fint, trow, max);
58284a15dedSSylwester Nawrocki 
58384a15dedSSylwester Nawrocki 	/* Update exposure time range to match current frame format. */
58484a15dedSSylwester Nawrocki 	min = (trow + 100) / 100;
58584a15dedSSylwester Nawrocki 	max = (max - 100) / 100;
58684a15dedSSylwester Nawrocki 	def = min + (max - min) / 2;
58784a15dedSSylwester Nawrocki 
58884a15dedSSylwester Nawrocki 	if (v4l2_ctrl_modify_range(ctrl, min, max, 1, def))
58984a15dedSSylwester Nawrocki 		v4l2_err(&ov965x->sd, "Exposure ctrl range update failed\n");
59084a15dedSSylwester Nawrocki }
59184a15dedSSylwester Nawrocki 
ov965x_set_banding_filter(struct ov965x * ov965x,int value)59284a15dedSSylwester Nawrocki static int ov965x_set_banding_filter(struct ov965x *ov965x, int value)
59384a15dedSSylwester Nawrocki {
59484a15dedSSylwester Nawrocki 	unsigned long mbd, light_freq;
59584a15dedSSylwester Nawrocki 	int ret;
59684a15dedSSylwester Nawrocki 	u8 reg;
59784a15dedSSylwester Nawrocki 
598361f3803SAkinobu Mita 	ret = ov965x_read(ov965x, REG_COM8, &reg);
59984a15dedSSylwester Nawrocki 	if (!ret) {
60084a15dedSSylwester Nawrocki 		if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED)
60184a15dedSSylwester Nawrocki 			reg &= ~COM8_BFILT;
60284a15dedSSylwester Nawrocki 		else
60384a15dedSSylwester Nawrocki 			reg |= COM8_BFILT;
604361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_COM8, reg);
60584a15dedSSylwester Nawrocki 	}
60684a15dedSSylwester Nawrocki 	if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED)
60784a15dedSSylwester Nawrocki 		return 0;
6080a7a1345SHugues Fruchet 	if (WARN_ON(!ov965x->fiv))
60984a15dedSSylwester Nawrocki 		return -EINVAL;
61084a15dedSSylwester Nawrocki 	/* Set minimal exposure time for 50/60 HZ lighting */
61184a15dedSSylwester Nawrocki 	if (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ)
61284a15dedSSylwester Nawrocki 		light_freq = 50;
61384a15dedSSylwester Nawrocki 	else
61484a15dedSSylwester Nawrocki 		light_freq = 60;
61584a15dedSSylwester Nawrocki 	mbd = (1000UL * ov965x->fiv->interval.denominator *
61684a15dedSSylwester Nawrocki 	       ov965x->frame_size->max_exp_lines) /
61784a15dedSSylwester Nawrocki 	       ov965x->fiv->interval.numerator;
61884a15dedSSylwester Nawrocki 	mbd = ((mbd / (light_freq * 2)) + 500) / 1000UL;
61984a15dedSSylwester Nawrocki 
620361f3803SAkinobu Mita 	return ov965x_write(ov965x, REG_MBD, mbd);
62184a15dedSSylwester Nawrocki }
62284a15dedSSylwester Nawrocki 
ov965x_set_white_balance(struct ov965x * ov965x,int awb)62384a15dedSSylwester Nawrocki static int ov965x_set_white_balance(struct ov965x *ov965x, int awb)
62484a15dedSSylwester Nawrocki {
62584a15dedSSylwester Nawrocki 	int ret;
62684a15dedSSylwester Nawrocki 	u8 reg;
62784a15dedSSylwester Nawrocki 
628361f3803SAkinobu Mita 	ret = ov965x_read(ov965x, REG_COM8, &reg);
62984a15dedSSylwester Nawrocki 	if (!ret) {
63084a15dedSSylwester Nawrocki 		reg = awb ? reg | REG_COM8 : reg & ~REG_COM8;
631361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_COM8, reg);
63284a15dedSSylwester Nawrocki 	}
63384a15dedSSylwester Nawrocki 	if (!ret && !awb) {
634361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_BLUE,
63584a15dedSSylwester Nawrocki 				   ov965x->ctrls.blue_balance->val);
63684a15dedSSylwester Nawrocki 		if (ret < 0)
63784a15dedSSylwester Nawrocki 			return ret;
638361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_RED,
63984a15dedSSylwester Nawrocki 				   ov965x->ctrls.red_balance->val);
64084a15dedSSylwester Nawrocki 	}
64184a15dedSSylwester Nawrocki 	return ret;
64284a15dedSSylwester Nawrocki }
64384a15dedSSylwester Nawrocki 
64484a15dedSSylwester Nawrocki #define NUM_BR_LEVELS	7
64584a15dedSSylwester Nawrocki #define NUM_BR_REGS	3
64684a15dedSSylwester Nawrocki 
ov965x_set_brightness(struct ov965x * ov965x,int val)64784a15dedSSylwester Nawrocki static int ov965x_set_brightness(struct ov965x *ov965x, int val)
64884a15dedSSylwester Nawrocki {
64984a15dedSSylwester Nawrocki 	static const u8 regs[NUM_BR_LEVELS + 1][NUM_BR_REGS] = {
65084a15dedSSylwester Nawrocki 		{ REG_AEW, REG_AEB, REG_VPT },
65184a15dedSSylwester Nawrocki 		{ 0x1c, 0x12, 0x50 }, /* -3 */
65284a15dedSSylwester Nawrocki 		{ 0x3d, 0x30, 0x71 }, /* -2 */
65384a15dedSSylwester Nawrocki 		{ 0x50, 0x44, 0x92 }, /* -1 */
65484a15dedSSylwester Nawrocki 		{ 0x70, 0x64, 0xc3 }, /*  0 */
65584a15dedSSylwester Nawrocki 		{ 0x90, 0x84, 0xd4 }, /* +1 */
65684a15dedSSylwester Nawrocki 		{ 0xc4, 0xbf, 0xf9 }, /* +2 */
65784a15dedSSylwester Nawrocki 		{ 0xd8, 0xd0, 0xfa }, /* +3 */
65884a15dedSSylwester Nawrocki 	};
65984a15dedSSylwester Nawrocki 	int i, ret = 0;
66084a15dedSSylwester Nawrocki 
66184a15dedSSylwester Nawrocki 	val += (NUM_BR_LEVELS / 2 + 1);
66284a15dedSSylwester Nawrocki 	if (val > NUM_BR_LEVELS)
66384a15dedSSylwester Nawrocki 		return -EINVAL;
66484a15dedSSylwester Nawrocki 
66584a15dedSSylwester Nawrocki 	for (i = 0; i < NUM_BR_REGS && !ret; i++)
666361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, regs[0][i],
66784a15dedSSylwester Nawrocki 				   regs[val][i]);
66884a15dedSSylwester Nawrocki 	return ret;
66984a15dedSSylwester Nawrocki }
67084a15dedSSylwester Nawrocki 
ov965x_set_gain(struct ov965x * ov965x,int auto_gain)67184a15dedSSylwester Nawrocki static int ov965x_set_gain(struct ov965x *ov965x, int auto_gain)
67284a15dedSSylwester Nawrocki {
67384a15dedSSylwester Nawrocki 	struct ov965x_ctrls *ctrls = &ov965x->ctrls;
67484a15dedSSylwester Nawrocki 	int ret = 0;
67584a15dedSSylwester Nawrocki 	u8 reg;
67684a15dedSSylwester Nawrocki 	/*
67784a15dedSSylwester Nawrocki 	 * For manual mode we need to disable AGC first, so
67884a15dedSSylwester Nawrocki 	 * gain value in REG_VREF, REG_GAIN is not overwritten.
67984a15dedSSylwester Nawrocki 	 */
68084a15dedSSylwester Nawrocki 	if (ctrls->auto_gain->is_new) {
681361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_COM8, &reg);
68284a15dedSSylwester Nawrocki 		if (ret < 0)
68384a15dedSSylwester Nawrocki 			return ret;
68484a15dedSSylwester Nawrocki 		if (ctrls->auto_gain->val)
68584a15dedSSylwester Nawrocki 			reg |= COM8_AGC;
68684a15dedSSylwester Nawrocki 		else
68784a15dedSSylwester Nawrocki 			reg &= ~COM8_AGC;
688361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_COM8, reg);
68984a15dedSSylwester Nawrocki 		if (ret < 0)
69084a15dedSSylwester Nawrocki 			return ret;
69184a15dedSSylwester Nawrocki 	}
69284a15dedSSylwester Nawrocki 
69384a15dedSSylwester Nawrocki 	if (ctrls->gain->is_new && !auto_gain) {
69484a15dedSSylwester Nawrocki 		unsigned int gain = ctrls->gain->val;
69584a15dedSSylwester Nawrocki 		unsigned int rgain;
69684a15dedSSylwester Nawrocki 		int m;
69784a15dedSSylwester Nawrocki 		/*
69884a15dedSSylwester Nawrocki 		 * Convert gain control value to the sensor's gain
69984a15dedSSylwester Nawrocki 		 * registers (VREF[7:6], GAIN[7:0]) format.
70084a15dedSSylwester Nawrocki 		 */
70184a15dedSSylwester Nawrocki 		for (m = 6; m >= 0; m--)
70284a15dedSSylwester Nawrocki 			if (gain >= (1 << m) * 16)
70384a15dedSSylwester Nawrocki 				break;
704093347abSMauro Carvalho Chehab 
705093347abSMauro Carvalho Chehab 		/* Sanity check: don't adjust the gain with a negative value */
706093347abSMauro Carvalho Chehab 		if (m < 0)
707093347abSMauro Carvalho Chehab 			return -EINVAL;
708093347abSMauro Carvalho Chehab 
70984a15dedSSylwester Nawrocki 		rgain = (gain - ((1 << m) * 16)) / (1 << m);
71084a15dedSSylwester Nawrocki 		rgain |= (((1 << m) - 1) << 4);
71184a15dedSSylwester Nawrocki 
712361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_GAIN, rgain & 0xff);
71384a15dedSSylwester Nawrocki 		if (ret < 0)
71484a15dedSSylwester Nawrocki 			return ret;
715361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_VREF, &reg);
71684a15dedSSylwester Nawrocki 		if (ret < 0)
71784a15dedSSylwester Nawrocki 			return ret;
71884a15dedSSylwester Nawrocki 		reg &= ~VREF_GAIN_MASK;
71984a15dedSSylwester Nawrocki 		reg |= (((rgain >> 8) & 0x3) << 6);
720361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_VREF, reg);
72184a15dedSSylwester Nawrocki 		if (ret < 0)
72284a15dedSSylwester Nawrocki 			return ret;
72384a15dedSSylwester Nawrocki 		/* Return updated control's value to userspace */
72484a15dedSSylwester Nawrocki 		ctrls->gain->val = (1 << m) * (16 + (rgain & 0xf));
72584a15dedSSylwester Nawrocki 	}
72684a15dedSSylwester Nawrocki 
72784a15dedSSylwester Nawrocki 	return ret;
72884a15dedSSylwester Nawrocki }
72984a15dedSSylwester Nawrocki 
ov965x_set_sharpness(struct ov965x * ov965x,unsigned int value)73084a15dedSSylwester Nawrocki static int ov965x_set_sharpness(struct ov965x *ov965x, unsigned int value)
73184a15dedSSylwester Nawrocki {
73284a15dedSSylwester Nawrocki 	u8 com14, edge;
73384a15dedSSylwester Nawrocki 	int ret;
73484a15dedSSylwester Nawrocki 
735361f3803SAkinobu Mita 	ret = ov965x_read(ov965x, REG_COM14, &com14);
73684a15dedSSylwester Nawrocki 	if (ret < 0)
73784a15dedSSylwester Nawrocki 		return ret;
738361f3803SAkinobu Mita 	ret = ov965x_read(ov965x, REG_EDGE, &edge);
73984a15dedSSylwester Nawrocki 	if (ret < 0)
74084a15dedSSylwester Nawrocki 		return ret;
74184a15dedSSylwester Nawrocki 	com14 = value ? com14 | COM14_EDGE_EN : com14 & ~COM14_EDGE_EN;
74284a15dedSSylwester Nawrocki 	value--;
74384a15dedSSylwester Nawrocki 	if (value > 0x0f) {
74484a15dedSSylwester Nawrocki 		com14 |= COM14_EEF_X2;
74584a15dedSSylwester Nawrocki 		value >>= 1;
74684a15dedSSylwester Nawrocki 	} else {
74784a15dedSSylwester Nawrocki 		com14 &= ~COM14_EEF_X2;
74884a15dedSSylwester Nawrocki 	}
749361f3803SAkinobu Mita 	ret = ov965x_write(ov965x, REG_COM14, com14);
75084a15dedSSylwester Nawrocki 	if (ret < 0)
75184a15dedSSylwester Nawrocki 		return ret;
75284a15dedSSylwester Nawrocki 
75384a15dedSSylwester Nawrocki 	edge &= ~EDGE_FACTOR_MASK;
75484a15dedSSylwester Nawrocki 	edge |= ((u8)value & 0x0f);
75584a15dedSSylwester Nawrocki 
756361f3803SAkinobu Mita 	return ov965x_write(ov965x, REG_EDGE, edge);
75784a15dedSSylwester Nawrocki }
75884a15dedSSylwester Nawrocki 
ov965x_set_exposure(struct ov965x * ov965x,int exp)75984a15dedSSylwester Nawrocki static int ov965x_set_exposure(struct ov965x *ov965x, int exp)
76084a15dedSSylwester Nawrocki {
76184a15dedSSylwester Nawrocki 	struct ov965x_ctrls *ctrls = &ov965x->ctrls;
76284a15dedSSylwester Nawrocki 	bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO);
76384a15dedSSylwester Nawrocki 	int ret;
76484a15dedSSylwester Nawrocki 	u8 reg;
76584a15dedSSylwester Nawrocki 
76684a15dedSSylwester Nawrocki 	if (ctrls->auto_exp->is_new) {
767361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_COM8, &reg);
76884a15dedSSylwester Nawrocki 		if (ret < 0)
76984a15dedSSylwester Nawrocki 			return ret;
77084a15dedSSylwester Nawrocki 		if (auto_exposure)
77184a15dedSSylwester Nawrocki 			reg |= (COM8_AEC | COM8_AGC);
77284a15dedSSylwester Nawrocki 		else
77384a15dedSSylwester Nawrocki 			reg &= ~(COM8_AEC | COM8_AGC);
774361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_COM8, reg);
77584a15dedSSylwester Nawrocki 		if (ret < 0)
77684a15dedSSylwester Nawrocki 			return ret;
77784a15dedSSylwester Nawrocki 	}
77884a15dedSSylwester Nawrocki 
77984a15dedSSylwester Nawrocki 	if (!auto_exposure && ctrls->exposure->is_new) {
78084a15dedSSylwester Nawrocki 		unsigned int exposure = (ctrls->exposure->val * 100)
78184a15dedSSylwester Nawrocki 					 / ov965x->exp_row_interval;
78284a15dedSSylwester Nawrocki 		/*
78384a15dedSSylwester Nawrocki 		 * Manual exposure value
78484a15dedSSylwester Nawrocki 		 * [b15:b0] - AECHM (b15:b10), AECH (b9:b2), COM1 (b1:b0)
78584a15dedSSylwester Nawrocki 		 */
786361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_COM1, exposure & 0x3);
78784a15dedSSylwester Nawrocki 		if (!ret)
788361f3803SAkinobu Mita 			ret = ov965x_write(ov965x, REG_AECH,
78984a15dedSSylwester Nawrocki 					   (exposure >> 2) & 0xff);
79084a15dedSSylwester Nawrocki 		if (!ret)
791361f3803SAkinobu Mita 			ret = ov965x_write(ov965x, REG_AECHM,
79284a15dedSSylwester Nawrocki 					   (exposure >> 10) & 0x3f);
79384a15dedSSylwester Nawrocki 		/* Update the value to minimize rounding errors */
79484a15dedSSylwester Nawrocki 		ctrls->exposure->val = ((exposure * ov965x->exp_row_interval)
79584a15dedSSylwester Nawrocki 							+ 50) / 100;
79684a15dedSSylwester Nawrocki 		if (ret < 0)
79784a15dedSSylwester Nawrocki 			return ret;
79884a15dedSSylwester Nawrocki 	}
79984a15dedSSylwester Nawrocki 
80084a15dedSSylwester Nawrocki 	v4l2_ctrl_activate(ov965x->ctrls.brightness, !exp);
80184a15dedSSylwester Nawrocki 	return 0;
80284a15dedSSylwester Nawrocki }
80384a15dedSSylwester Nawrocki 
ov965x_set_flip(struct ov965x * ov965x)80484a15dedSSylwester Nawrocki static int ov965x_set_flip(struct ov965x *ov965x)
80584a15dedSSylwester Nawrocki {
80684a15dedSSylwester Nawrocki 	u8 mvfp = 0;
80784a15dedSSylwester Nawrocki 
80884a15dedSSylwester Nawrocki 	if (ov965x->ctrls.hflip->val)
80984a15dedSSylwester Nawrocki 		mvfp |= MVFP_MIRROR;
81084a15dedSSylwester Nawrocki 
81184a15dedSSylwester Nawrocki 	if (ov965x->ctrls.vflip->val)
81284a15dedSSylwester Nawrocki 		mvfp |= MVFP_FLIP;
81384a15dedSSylwester Nawrocki 
814361f3803SAkinobu Mita 	return ov965x_write(ov965x, REG_MVFP, mvfp);
81584a15dedSSylwester Nawrocki }
81684a15dedSSylwester Nawrocki 
81784a15dedSSylwester Nawrocki #define NUM_SAT_LEVELS	5
81884a15dedSSylwester Nawrocki #define NUM_SAT_REGS	6
81984a15dedSSylwester Nawrocki 
ov965x_set_saturation(struct ov965x * ov965x,int val)82084a15dedSSylwester Nawrocki static int ov965x_set_saturation(struct ov965x *ov965x, int val)
82184a15dedSSylwester Nawrocki {
82284a15dedSSylwester Nawrocki 	static const u8 regs[NUM_SAT_LEVELS][NUM_SAT_REGS] = {
82384a15dedSSylwester Nawrocki 		/* MTX(1)...MTX(6) */
82484a15dedSSylwester Nawrocki 		{ 0x1d, 0x1f, 0x02, 0x09, 0x13, 0x1c }, /* -2 */
82584a15dedSSylwester Nawrocki 		{ 0x2e, 0x31, 0x02, 0x0e, 0x1e, 0x2d }, /* -1 */
82684a15dedSSylwester Nawrocki 		{ 0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38 }, /*  0 */
82784a15dedSSylwester Nawrocki 		{ 0x46, 0x49, 0x04, 0x16, 0x2e, 0x43 }, /* +1 */
82884a15dedSSylwester Nawrocki 		{ 0x57, 0x5c, 0x05, 0x1b, 0x39, 0x54 }, /* +2 */
82984a15dedSSylwester Nawrocki 	};
83084a15dedSSylwester Nawrocki 	u8 addr = REG_MTX(1);
83184a15dedSSylwester Nawrocki 	int i, ret = 0;
83284a15dedSSylwester Nawrocki 
83384a15dedSSylwester Nawrocki 	val += (NUM_SAT_LEVELS / 2);
83484a15dedSSylwester Nawrocki 	if (val >= NUM_SAT_LEVELS)
83584a15dedSSylwester Nawrocki 		return -EINVAL;
83684a15dedSSylwester Nawrocki 
83784a15dedSSylwester Nawrocki 	for (i = 0; i < NUM_SAT_REGS && !ret; i++)
838361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, addr + i, regs[val][i]);
83984a15dedSSylwester Nawrocki 
84084a15dedSSylwester Nawrocki 	return ret;
84184a15dedSSylwester Nawrocki }
84284a15dedSSylwester Nawrocki 
ov965x_set_test_pattern(struct ov965x * ov965x,int value)84384a15dedSSylwester Nawrocki static int ov965x_set_test_pattern(struct ov965x *ov965x, int value)
84484a15dedSSylwester Nawrocki {
84584a15dedSSylwester Nawrocki 	int ret;
84684a15dedSSylwester Nawrocki 	u8 reg;
84784a15dedSSylwester Nawrocki 
848361f3803SAkinobu Mita 	ret = ov965x_read(ov965x, REG_COM23, &reg);
84984a15dedSSylwester Nawrocki 	if (ret < 0)
85084a15dedSSylwester Nawrocki 		return ret;
85184a15dedSSylwester Nawrocki 	reg = value ? reg | COM23_TEST_MODE : reg & ~COM23_TEST_MODE;
852361f3803SAkinobu Mita 	return ov965x_write(ov965x, REG_COM23, reg);
85384a15dedSSylwester Nawrocki }
85484a15dedSSylwester Nawrocki 
__g_volatile_ctrl(struct ov965x * ov965x,struct v4l2_ctrl * ctrl)85584a15dedSSylwester Nawrocki static int __g_volatile_ctrl(struct ov965x *ov965x, struct v4l2_ctrl *ctrl)
85684a15dedSSylwester Nawrocki {
85784a15dedSSylwester Nawrocki 	unsigned int exposure, gain, m;
85884a15dedSSylwester Nawrocki 	u8 reg0, reg1, reg2;
85984a15dedSSylwester Nawrocki 	int ret;
86084a15dedSSylwester Nawrocki 
86184a15dedSSylwester Nawrocki 	if (!ov965x->power)
86284a15dedSSylwester Nawrocki 		return 0;
86384a15dedSSylwester Nawrocki 
86484a15dedSSylwester Nawrocki 	switch (ctrl->id) {
86584a15dedSSylwester Nawrocki 	case V4L2_CID_AUTOGAIN:
86684a15dedSSylwester Nawrocki 		if (!ctrl->val)
86784a15dedSSylwester Nawrocki 			return 0;
868361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_GAIN, &reg0);
86984a15dedSSylwester Nawrocki 		if (ret < 0)
87084a15dedSSylwester Nawrocki 			return ret;
871361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_VREF, &reg1);
87284a15dedSSylwester Nawrocki 		if (ret < 0)
87384a15dedSSylwester Nawrocki 			return ret;
87484a15dedSSylwester Nawrocki 		gain = ((reg1 >> 6) << 8) | reg0;
87584a15dedSSylwester Nawrocki 		m = 0x01 << fls(gain >> 4);
87684a15dedSSylwester Nawrocki 		ov965x->ctrls.gain->val = m * (16 + (gain & 0xf));
87784a15dedSSylwester Nawrocki 		break;
87884a15dedSSylwester Nawrocki 
87984a15dedSSylwester Nawrocki 	case V4L2_CID_EXPOSURE_AUTO:
88084a15dedSSylwester Nawrocki 		if (ctrl->val == V4L2_EXPOSURE_MANUAL)
88184a15dedSSylwester Nawrocki 			return 0;
882361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_COM1, &reg0);
88329236349SMauro Carvalho Chehab 		if (ret < 0)
88429236349SMauro Carvalho Chehab 			return ret;
885361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_AECH, &reg1);
88629236349SMauro Carvalho Chehab 		if (ret < 0)
88729236349SMauro Carvalho Chehab 			return ret;
888361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_AECHM, &reg2);
88984a15dedSSylwester Nawrocki 		if (ret < 0)
89084a15dedSSylwester Nawrocki 			return ret;
89184a15dedSSylwester Nawrocki 		exposure = ((reg2 & 0x3f) << 10) | (reg1 << 2) |
89284a15dedSSylwester Nawrocki 						(reg0 & 0x3);
89384a15dedSSylwester Nawrocki 		ov965x->ctrls.exposure->val = ((exposure *
89484a15dedSSylwester Nawrocki 				ov965x->exp_row_interval) + 50) / 100;
89584a15dedSSylwester Nawrocki 		break;
89684a15dedSSylwester Nawrocki 	}
89784a15dedSSylwester Nawrocki 
89884a15dedSSylwester Nawrocki 	return 0;
89984a15dedSSylwester Nawrocki }
90084a15dedSSylwester Nawrocki 
ov965x_g_volatile_ctrl(struct v4l2_ctrl * ctrl)90184a15dedSSylwester Nawrocki static int ov965x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
90284a15dedSSylwester Nawrocki {
90384a15dedSSylwester Nawrocki 	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
90484a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
90584a15dedSSylwester Nawrocki 	int ret;
90684a15dedSSylwester Nawrocki 
90784a15dedSSylwester Nawrocki 	v4l2_dbg(1, debug, sd, "g_ctrl: %s\n", ctrl->name);
90884a15dedSSylwester Nawrocki 
90984a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
91084a15dedSSylwester Nawrocki 	ret = __g_volatile_ctrl(ov965x, ctrl);
91184a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
91284a15dedSSylwester Nawrocki 	return ret;
91384a15dedSSylwester Nawrocki }
91484a15dedSSylwester Nawrocki 
ov965x_s_ctrl(struct v4l2_ctrl * ctrl)91584a15dedSSylwester Nawrocki static int ov965x_s_ctrl(struct v4l2_ctrl *ctrl)
91684a15dedSSylwester Nawrocki {
91784a15dedSSylwester Nawrocki 	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
91884a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
91984a15dedSSylwester Nawrocki 	int ret = -EINVAL;
92084a15dedSSylwester Nawrocki 
92184a15dedSSylwester Nawrocki 	v4l2_dbg(1, debug, sd, "s_ctrl: %s, value: %d. power: %d\n",
92284a15dedSSylwester Nawrocki 		 ctrl->name, ctrl->val, ov965x->power);
92384a15dedSSylwester Nawrocki 
92484a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
92584a15dedSSylwester Nawrocki 	/*
92684a15dedSSylwester Nawrocki 	 * If the device is not powered up now postpone applying control's
92784a15dedSSylwester Nawrocki 	 * value to the hardware, until it is ready to accept commands.
92884a15dedSSylwester Nawrocki 	 */
92984a15dedSSylwester Nawrocki 	if (ov965x->power == 0) {
93084a15dedSSylwester Nawrocki 		mutex_unlock(&ov965x->lock);
93184a15dedSSylwester Nawrocki 		return 0;
93284a15dedSSylwester Nawrocki 	}
93384a15dedSSylwester Nawrocki 
93484a15dedSSylwester Nawrocki 	switch (ctrl->id) {
93584a15dedSSylwester Nawrocki 	case V4L2_CID_AUTO_WHITE_BALANCE:
93684a15dedSSylwester Nawrocki 		ret = ov965x_set_white_balance(ov965x, ctrl->val);
93784a15dedSSylwester Nawrocki 		break;
93884a15dedSSylwester Nawrocki 
93984a15dedSSylwester Nawrocki 	case V4L2_CID_BRIGHTNESS:
94084a15dedSSylwester Nawrocki 		ret = ov965x_set_brightness(ov965x, ctrl->val);
94184a15dedSSylwester Nawrocki 		break;
94284a15dedSSylwester Nawrocki 
94384a15dedSSylwester Nawrocki 	case V4L2_CID_EXPOSURE_AUTO:
94484a15dedSSylwester Nawrocki 		ret = ov965x_set_exposure(ov965x, ctrl->val);
94584a15dedSSylwester Nawrocki 		break;
94684a15dedSSylwester Nawrocki 
94784a15dedSSylwester Nawrocki 	case V4L2_CID_AUTOGAIN:
94884a15dedSSylwester Nawrocki 		ret = ov965x_set_gain(ov965x, ctrl->val);
94984a15dedSSylwester Nawrocki 		break;
95084a15dedSSylwester Nawrocki 
95184a15dedSSylwester Nawrocki 	case V4L2_CID_HFLIP:
95284a15dedSSylwester Nawrocki 		ret = ov965x_set_flip(ov965x);
95384a15dedSSylwester Nawrocki 		break;
95484a15dedSSylwester Nawrocki 
95584a15dedSSylwester Nawrocki 	case V4L2_CID_POWER_LINE_FREQUENCY:
95684a15dedSSylwester Nawrocki 		ret = ov965x_set_banding_filter(ov965x, ctrl->val);
95784a15dedSSylwester Nawrocki 		break;
95884a15dedSSylwester Nawrocki 
95984a15dedSSylwester Nawrocki 	case V4L2_CID_SATURATION:
96084a15dedSSylwester Nawrocki 		ret = ov965x_set_saturation(ov965x, ctrl->val);
96184a15dedSSylwester Nawrocki 		break;
96284a15dedSSylwester Nawrocki 
96384a15dedSSylwester Nawrocki 	case V4L2_CID_SHARPNESS:
96484a15dedSSylwester Nawrocki 		ret = ov965x_set_sharpness(ov965x, ctrl->val);
96584a15dedSSylwester Nawrocki 		break;
96684a15dedSSylwester Nawrocki 
96784a15dedSSylwester Nawrocki 	case V4L2_CID_TEST_PATTERN:
96884a15dedSSylwester Nawrocki 		ret = ov965x_set_test_pattern(ov965x, ctrl->val);
96984a15dedSSylwester Nawrocki 		break;
97084a15dedSSylwester Nawrocki 	}
97184a15dedSSylwester Nawrocki 
97284a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
97384a15dedSSylwester Nawrocki 	return ret;
97484a15dedSSylwester Nawrocki }
97584a15dedSSylwester Nawrocki 
97684a15dedSSylwester Nawrocki static const struct v4l2_ctrl_ops ov965x_ctrl_ops = {
97784a15dedSSylwester Nawrocki 	.g_volatile_ctrl = ov965x_g_volatile_ctrl,
97884a15dedSSylwester Nawrocki 	.s_ctrl	= ov965x_s_ctrl,
97984a15dedSSylwester Nawrocki };
98084a15dedSSylwester Nawrocki 
98184a15dedSSylwester Nawrocki static const char * const test_pattern_menu[] = {
98284a15dedSSylwester Nawrocki 	"Disabled",
98384a15dedSSylwester Nawrocki 	"Color bars",
98484a15dedSSylwester Nawrocki };
98584a15dedSSylwester Nawrocki 
ov965x_initialize_controls(struct ov965x * ov965x)98684a15dedSSylwester Nawrocki static int ov965x_initialize_controls(struct ov965x *ov965x)
98784a15dedSSylwester Nawrocki {
98884a15dedSSylwester Nawrocki 	const struct v4l2_ctrl_ops *ops = &ov965x_ctrl_ops;
98984a15dedSSylwester Nawrocki 	struct ov965x_ctrls *ctrls = &ov965x->ctrls;
99084a15dedSSylwester Nawrocki 	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
99184a15dedSSylwester Nawrocki 	int ret;
99284a15dedSSylwester Nawrocki 
99384a15dedSSylwester Nawrocki 	ret = v4l2_ctrl_handler_init(hdl, 16);
99484a15dedSSylwester Nawrocki 	if (ret < 0)
99584a15dedSSylwester Nawrocki 		return ret;
99684a15dedSSylwester Nawrocki 
99784a15dedSSylwester Nawrocki 	/* Auto/manual white balance */
99884a15dedSSylwester Nawrocki 	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
99984a15dedSSylwester Nawrocki 					   V4L2_CID_AUTO_WHITE_BALANCE,
100084a15dedSSylwester Nawrocki 					   0, 1, 1, 1);
100184a15dedSSylwester Nawrocki 	ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
100284a15dedSSylwester Nawrocki 						0, 0xff, 1, 0x80);
100384a15dedSSylwester Nawrocki 	ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
100484a15dedSSylwester Nawrocki 					       0, 0xff, 1, 0x80);
100584a15dedSSylwester Nawrocki 	/* Auto/manual exposure */
10060a7a1345SHugues Fruchet 	ctrls->auto_exp =
10070a7a1345SHugues Fruchet 		v4l2_ctrl_new_std_menu(hdl, ops,
100884a15dedSSylwester Nawrocki 				       V4L2_CID_EXPOSURE_AUTO,
10090a7a1345SHugues Fruchet 				       V4L2_EXPOSURE_MANUAL, 0,
10100a7a1345SHugues Fruchet 				       V4L2_EXPOSURE_AUTO);
101184a15dedSSylwester Nawrocki 	/* Exposure time, in 100 us units. min/max is updated dynamically. */
101284a15dedSSylwester Nawrocki 	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops,
101384a15dedSSylwester Nawrocki 					    V4L2_CID_EXPOSURE_ABSOLUTE,
101484a15dedSSylwester Nawrocki 					    2, 1500, 1, 500);
101584a15dedSSylwester Nawrocki 	/* Auto/manual gain */
101684a15dedSSylwester Nawrocki 	ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
101784a15dedSSylwester Nawrocki 					     0, 1, 1, 1);
101884a15dedSSylwester Nawrocki 	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
101984a15dedSSylwester Nawrocki 					16, 64 * (16 + 15), 1, 64 * 16);
102084a15dedSSylwester Nawrocki 
102184a15dedSSylwester Nawrocki 	ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
102284a15dedSSylwester Nawrocki 					      -2, 2, 1, 0);
102384a15dedSSylwester Nawrocki 	ctrls->brightness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS,
102484a15dedSSylwester Nawrocki 					      -3, 3, 1, 0);
102584a15dedSSylwester Nawrocki 	ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS,
102684a15dedSSylwester Nawrocki 					     0, 32, 1, 6);
102784a15dedSSylwester Nawrocki 
102884a15dedSSylwester Nawrocki 	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
102984a15dedSSylwester Nawrocki 	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
103084a15dedSSylwester Nawrocki 
10310a7a1345SHugues Fruchet 	ctrls->light_freq =
10320a7a1345SHugues Fruchet 		v4l2_ctrl_new_std_menu(hdl, ops,
103384a15dedSSylwester Nawrocki 				       V4L2_CID_POWER_LINE_FREQUENCY,
103484a15dedSSylwester Nawrocki 				       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ~0x7,
103584a15dedSSylwester Nawrocki 				       V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
103684a15dedSSylwester Nawrocki 
103784a15dedSSylwester Nawrocki 	v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
103884a15dedSSylwester Nawrocki 				     ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
103984a15dedSSylwester Nawrocki 				     test_pattern_menu);
104084a15dedSSylwester Nawrocki 	if (hdl->error) {
104184a15dedSSylwester Nawrocki 		ret = hdl->error;
104284a15dedSSylwester Nawrocki 		v4l2_ctrl_handler_free(hdl);
104384a15dedSSylwester Nawrocki 		return ret;
104484a15dedSSylwester Nawrocki 	}
104584a15dedSSylwester Nawrocki 
104684a15dedSSylwester Nawrocki 	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
104784a15dedSSylwester Nawrocki 	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
104884a15dedSSylwester Nawrocki 
104984a15dedSSylwester Nawrocki 	v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
1050e295b3d6SGuennadi Liakhovetski 	v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
1051e295b3d6SGuennadi Liakhovetski 	v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
105284a15dedSSylwester Nawrocki 	v4l2_ctrl_cluster(2, &ctrls->hflip);
105384a15dedSSylwester Nawrocki 
105484a15dedSSylwester Nawrocki 	ov965x->sd.ctrl_handler = hdl;
105584a15dedSSylwester Nawrocki 	return 0;
105684a15dedSSylwester Nawrocki }
105784a15dedSSylwester Nawrocki 
105884a15dedSSylwester Nawrocki /*
105984a15dedSSylwester Nawrocki  * V4L2 subdev video and pad level operations
106084a15dedSSylwester Nawrocki  */
ov965x_get_default_format(struct v4l2_mbus_framefmt * mf)106184a15dedSSylwester Nawrocki static void ov965x_get_default_format(struct v4l2_mbus_framefmt *mf)
106284a15dedSSylwester Nawrocki {
106384a15dedSSylwester Nawrocki 	mf->width = ov965x_framesizes[0].width;
106484a15dedSSylwester Nawrocki 	mf->height = ov965x_framesizes[0].height;
106584a15dedSSylwester Nawrocki 	mf->colorspace = ov965x_formats[0].colorspace;
106684a15dedSSylwester Nawrocki 	mf->code = ov965x_formats[0].code;
106784a15dedSSylwester Nawrocki 	mf->field = V4L2_FIELD_NONE;
106884a15dedSSylwester Nawrocki }
106984a15dedSSylwester Nawrocki 
ov965x_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)107084a15dedSSylwester Nawrocki static int ov965x_enum_mbus_code(struct v4l2_subdev *sd,
10710d346d2aSTomi Valkeinen 				 struct v4l2_subdev_state *sd_state,
107284a15dedSSylwester Nawrocki 				 struct v4l2_subdev_mbus_code_enum *code)
107384a15dedSSylwester Nawrocki {
107484a15dedSSylwester Nawrocki 	if (code->index >= ARRAY_SIZE(ov965x_formats))
107584a15dedSSylwester Nawrocki 		return -EINVAL;
107684a15dedSSylwester Nawrocki 
107784a15dedSSylwester Nawrocki 	code->code = ov965x_formats[code->index].code;
107884a15dedSSylwester Nawrocki 	return 0;
107984a15dedSSylwester Nawrocki }
108084a15dedSSylwester Nawrocki 
ov965x_enum_frame_sizes(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)108184a15dedSSylwester Nawrocki static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd,
10820d346d2aSTomi Valkeinen 				   struct v4l2_subdev_state *sd_state,
108384a15dedSSylwester Nawrocki 				   struct v4l2_subdev_frame_size_enum *fse)
108484a15dedSSylwester Nawrocki {
108584a15dedSSylwester Nawrocki 	int i = ARRAY_SIZE(ov965x_formats);
108684a15dedSSylwester Nawrocki 
10876a4760edSDan Carpenter 	if (fse->index >= ARRAY_SIZE(ov965x_framesizes))
108884a15dedSSylwester Nawrocki 		return -EINVAL;
108984a15dedSSylwester Nawrocki 
109084a15dedSSylwester Nawrocki 	while (--i)
109184a15dedSSylwester Nawrocki 		if (fse->code == ov965x_formats[i].code)
109284a15dedSSylwester Nawrocki 			break;
109384a15dedSSylwester Nawrocki 
109484a15dedSSylwester Nawrocki 	fse->code = ov965x_formats[i].code;
109584a15dedSSylwester Nawrocki 
109684a15dedSSylwester Nawrocki 	fse->min_width  = ov965x_framesizes[fse->index].width;
109784a15dedSSylwester Nawrocki 	fse->max_width  = fse->min_width;
109884a15dedSSylwester Nawrocki 	fse->max_height = ov965x_framesizes[fse->index].height;
109984a15dedSSylwester Nawrocki 	fse->min_height = fse->max_height;
110084a15dedSSylwester Nawrocki 
110184a15dedSSylwester Nawrocki 	return 0;
110284a15dedSSylwester Nawrocki }
110384a15dedSSylwester Nawrocki 
ov965x_g_frame_interval(struct v4l2_subdev * sd,struct v4l2_subdev_frame_interval * fi)110484a15dedSSylwester Nawrocki static int ov965x_g_frame_interval(struct v4l2_subdev *sd,
110584a15dedSSylwester Nawrocki 				   struct v4l2_subdev_frame_interval *fi)
110684a15dedSSylwester Nawrocki {
110784a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
110884a15dedSSylwester Nawrocki 
110984a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
111084a15dedSSylwester Nawrocki 	fi->interval = ov965x->fiv->interval;
111184a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
111284a15dedSSylwester Nawrocki 
111384a15dedSSylwester Nawrocki 	return 0;
111484a15dedSSylwester Nawrocki }
111584a15dedSSylwester Nawrocki 
__ov965x_set_frame_interval(struct ov965x * ov965x,struct v4l2_subdev_frame_interval * fi)111684a15dedSSylwester Nawrocki static int __ov965x_set_frame_interval(struct ov965x *ov965x,
111784a15dedSSylwester Nawrocki 				       struct v4l2_subdev_frame_interval *fi)
111884a15dedSSylwester Nawrocki {
111984a15dedSSylwester Nawrocki 	struct v4l2_mbus_framefmt *mbus_fmt = &ov965x->format;
112084a15dedSSylwester Nawrocki 	const struct ov965x_interval *fiv = &ov965x_intervals[0];
112184a15dedSSylwester Nawrocki 	u64 req_int, err, min_err = ~0ULL;
112284a15dedSSylwester Nawrocki 	unsigned int i;
112384a15dedSSylwester Nawrocki 
112484a15dedSSylwester Nawrocki 	if (fi->interval.denominator == 0)
112584a15dedSSylwester Nawrocki 		return -EINVAL;
112684a15dedSSylwester Nawrocki 
112736e49ffbSGustavo A. R. Silva 	req_int = (u64)fi->interval.numerator * 10000;
112836e49ffbSGustavo A. R. Silva 	do_div(req_int, fi->interval.denominator);
112984a15dedSSylwester Nawrocki 
113084a15dedSSylwester Nawrocki 	for (i = 0; i < ARRAY_SIZE(ov965x_intervals); i++) {
113184a15dedSSylwester Nawrocki 		const struct ov965x_interval *iv = &ov965x_intervals[i];
113284a15dedSSylwester Nawrocki 
113384a15dedSSylwester Nawrocki 		if (mbus_fmt->width != iv->size.width ||
113484a15dedSSylwester Nawrocki 		    mbus_fmt->height != iv->size.height)
113584a15dedSSylwester Nawrocki 			continue;
113679211c8eSAndrew Morton 		err = abs((u64)(iv->interval.numerator * 10000) /
113784a15dedSSylwester Nawrocki 			    iv->interval.denominator - req_int);
113884a15dedSSylwester Nawrocki 		if (err < min_err) {
113984a15dedSSylwester Nawrocki 			fiv = iv;
114084a15dedSSylwester Nawrocki 			min_err = err;
114184a15dedSSylwester Nawrocki 		}
114284a15dedSSylwester Nawrocki 	}
114384a15dedSSylwester Nawrocki 	ov965x->fiv = fiv;
114484a15dedSSylwester Nawrocki 
114584a15dedSSylwester Nawrocki 	v4l2_dbg(1, debug, &ov965x->sd, "Changed frame interval to %u us\n",
114684a15dedSSylwester Nawrocki 		 fiv->interval.numerator * 1000000 / fiv->interval.denominator);
114784a15dedSSylwester Nawrocki 
114884a15dedSSylwester Nawrocki 	return 0;
114984a15dedSSylwester Nawrocki }
115084a15dedSSylwester Nawrocki 
ov965x_s_frame_interval(struct v4l2_subdev * sd,struct v4l2_subdev_frame_interval * fi)115184a15dedSSylwester Nawrocki static int ov965x_s_frame_interval(struct v4l2_subdev *sd,
115284a15dedSSylwester Nawrocki 				   struct v4l2_subdev_frame_interval *fi)
115384a15dedSSylwester Nawrocki {
115484a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
115584a15dedSSylwester Nawrocki 	int ret;
115684a15dedSSylwester Nawrocki 
115784a15dedSSylwester Nawrocki 	v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n",
115884a15dedSSylwester Nawrocki 		 fi->interval.numerator, fi->interval.denominator);
115984a15dedSSylwester Nawrocki 
116084a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
116184a15dedSSylwester Nawrocki 	ret = __ov965x_set_frame_interval(ov965x, fi);
116284a15dedSSylwester Nawrocki 	ov965x->apply_frame_fmt = 1;
116384a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
116484a15dedSSylwester Nawrocki 	return ret;
116584a15dedSSylwester Nawrocki }
116684a15dedSSylwester Nawrocki 
ov965x_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)11670a7a1345SHugues Fruchet static int ov965x_get_fmt(struct v4l2_subdev *sd,
11680d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
116984a15dedSSylwester Nawrocki 			  struct v4l2_subdev_format *fmt)
117084a15dedSSylwester Nawrocki {
117184a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
117284a15dedSSylwester Nawrocki 	struct v4l2_mbus_framefmt *mf;
117384a15dedSSylwester Nawrocki 
117484a15dedSSylwester Nawrocki 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
11750d346d2aSTomi Valkeinen 		mf = v4l2_subdev_get_try_format(sd, sd_state, 0);
117684a15dedSSylwester Nawrocki 		fmt->format = *mf;
117784a15dedSSylwester Nawrocki 		return 0;
117884a15dedSSylwester Nawrocki 	}
117984a15dedSSylwester Nawrocki 
118084a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
118184a15dedSSylwester Nawrocki 	fmt->format = ov965x->format;
118284a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
118384a15dedSSylwester Nawrocki 
118484a15dedSSylwester Nawrocki 	return 0;
118584a15dedSSylwester Nawrocki }
118684a15dedSSylwester Nawrocki 
__ov965x_try_frame_size(struct v4l2_mbus_framefmt * mf,const struct ov965x_framesize ** size)118784a15dedSSylwester Nawrocki static void __ov965x_try_frame_size(struct v4l2_mbus_framefmt *mf,
118884a15dedSSylwester Nawrocki 				    const struct ov965x_framesize **size)
118984a15dedSSylwester Nawrocki {
119084a15dedSSylwester Nawrocki 	const struct ov965x_framesize *fsize = &ov965x_framesizes[0],
119184a15dedSSylwester Nawrocki 		*match = NULL;
119284a15dedSSylwester Nawrocki 	int i = ARRAY_SIZE(ov965x_framesizes);
119384a15dedSSylwester Nawrocki 	unsigned int min_err = UINT_MAX;
119484a15dedSSylwester Nawrocki 
119584a15dedSSylwester Nawrocki 	while (i--) {
119684a15dedSSylwester Nawrocki 		int err = abs(fsize->width - mf->width)
119784a15dedSSylwester Nawrocki 				+ abs(fsize->height - mf->height);
119884a15dedSSylwester Nawrocki 		if (err < min_err) {
119984a15dedSSylwester Nawrocki 			min_err = err;
120084a15dedSSylwester Nawrocki 			match = fsize;
120184a15dedSSylwester Nawrocki 		}
120284a15dedSSylwester Nawrocki 		fsize++;
120384a15dedSSylwester Nawrocki 	}
120484a15dedSSylwester Nawrocki 	if (!match)
120584a15dedSSylwester Nawrocki 		match = &ov965x_framesizes[0];
120684a15dedSSylwester Nawrocki 	mf->width  = match->width;
120784a15dedSSylwester Nawrocki 	mf->height = match->height;
120884a15dedSSylwester Nawrocki 	if (size)
120984a15dedSSylwester Nawrocki 		*size = match;
121084a15dedSSylwester Nawrocki }
121184a15dedSSylwester Nawrocki 
ov965x_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)12120a7a1345SHugues Fruchet static int ov965x_set_fmt(struct v4l2_subdev *sd,
12130d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
121484a15dedSSylwester Nawrocki 			  struct v4l2_subdev_format *fmt)
121584a15dedSSylwester Nawrocki {
121684a15dedSSylwester Nawrocki 	unsigned int index = ARRAY_SIZE(ov965x_formats);
121784a15dedSSylwester Nawrocki 	struct v4l2_mbus_framefmt *mf = &fmt->format;
121884a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
121984a15dedSSylwester Nawrocki 	const struct ov965x_framesize *size = NULL;
122084a15dedSSylwester Nawrocki 	int ret = 0;
122184a15dedSSylwester Nawrocki 
122284a15dedSSylwester Nawrocki 	__ov965x_try_frame_size(mf, &size);
122384a15dedSSylwester Nawrocki 
122484a15dedSSylwester Nawrocki 	while (--index)
122584a15dedSSylwester Nawrocki 		if (ov965x_formats[index].code == mf->code)
122684a15dedSSylwester Nawrocki 			break;
122784a15dedSSylwester Nawrocki 
122884a15dedSSylwester Nawrocki 	mf->colorspace	= V4L2_COLORSPACE_JPEG;
122984a15dedSSylwester Nawrocki 	mf->code	= ov965x_formats[index].code;
123084a15dedSSylwester Nawrocki 	mf->field	= V4L2_FIELD_NONE;
123184a15dedSSylwester Nawrocki 
123284a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
123384a15dedSSylwester Nawrocki 
123484a15dedSSylwester Nawrocki 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
12350d346d2aSTomi Valkeinen 		if (sd_state) {
12360d346d2aSTomi Valkeinen 			mf = v4l2_subdev_get_try_format(sd, sd_state,
12370d346d2aSTomi Valkeinen 							fmt->pad);
123884a15dedSSylwester Nawrocki 			*mf = fmt->format;
123984a15dedSSylwester Nawrocki 		}
124084a15dedSSylwester Nawrocki 	} else {
124184a15dedSSylwester Nawrocki 		if (ov965x->streaming) {
124284a15dedSSylwester Nawrocki 			ret = -EBUSY;
124384a15dedSSylwester Nawrocki 		} else {
124484a15dedSSylwester Nawrocki 			ov965x->frame_size = size;
124584a15dedSSylwester Nawrocki 			ov965x->format = fmt->format;
124684a15dedSSylwester Nawrocki 			ov965x->tslb_reg = ov965x_formats[index].tslb_reg;
124784a15dedSSylwester Nawrocki 			ov965x->apply_frame_fmt = 1;
124884a15dedSSylwester Nawrocki 		}
124984a15dedSSylwester Nawrocki 	}
125084a15dedSSylwester Nawrocki 
125184a15dedSSylwester Nawrocki 	if (!ret && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
125284a15dedSSylwester Nawrocki 		struct v4l2_subdev_frame_interval fiv = {
125384a15dedSSylwester Nawrocki 			.interval = { 0, 1 }
125484a15dedSSylwester Nawrocki 		};
125584a15dedSSylwester Nawrocki 		/* Reset to minimum possible frame interval */
125684a15dedSSylwester Nawrocki 		__ov965x_set_frame_interval(ov965x, &fiv);
125784a15dedSSylwester Nawrocki 	}
125884a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
125984a15dedSSylwester Nawrocki 
126084a15dedSSylwester Nawrocki 	if (!ret)
126184a15dedSSylwester Nawrocki 		ov965x_update_exposure_ctrl(ov965x);
126284a15dedSSylwester Nawrocki 
126384a15dedSSylwester Nawrocki 	return ret;
126484a15dedSSylwester Nawrocki }
126584a15dedSSylwester Nawrocki 
ov965x_set_frame_size(struct ov965x * ov965x)126684a15dedSSylwester Nawrocki static int ov965x_set_frame_size(struct ov965x *ov965x)
126784a15dedSSylwester Nawrocki {
126884a15dedSSylwester Nawrocki 	int i, ret = 0;
126984a15dedSSylwester Nawrocki 
127084a15dedSSylwester Nawrocki 	for (i = 0; ret == 0 && i < NUM_FMT_REGS; i++)
1271361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, frame_size_reg_addr[i],
127284a15dedSSylwester Nawrocki 				   ov965x->frame_size->regs[i]);
127384a15dedSSylwester Nawrocki 	return ret;
127484a15dedSSylwester Nawrocki }
127584a15dedSSylwester Nawrocki 
__ov965x_set_params(struct ov965x * ov965x)127684a15dedSSylwester Nawrocki static int __ov965x_set_params(struct ov965x *ov965x)
127784a15dedSSylwester Nawrocki {
127884a15dedSSylwester Nawrocki 	struct ov965x_ctrls *ctrls = &ov965x->ctrls;
127984a15dedSSylwester Nawrocki 	int ret = 0;
128084a15dedSSylwester Nawrocki 	u8 reg;
128184a15dedSSylwester Nawrocki 
128284a15dedSSylwester Nawrocki 	if (ov965x->apply_frame_fmt) {
128384a15dedSSylwester Nawrocki 		reg = DEF_CLKRC + ov965x->fiv->clkrc_div;
1284361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_CLKRC, reg);
128584a15dedSSylwester Nawrocki 		if (ret < 0)
128684a15dedSSylwester Nawrocki 			return ret;
128784a15dedSSylwester Nawrocki 		ret = ov965x_set_frame_size(ov965x);
128884a15dedSSylwester Nawrocki 		if (ret < 0)
128984a15dedSSylwester Nawrocki 			return ret;
1290361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_TSLB, &reg);
129184a15dedSSylwester Nawrocki 		if (ret < 0)
129284a15dedSSylwester Nawrocki 			return ret;
129384a15dedSSylwester Nawrocki 		reg &= ~TSLB_YUYV_MASK;
129484a15dedSSylwester Nawrocki 		reg |= ov965x->tslb_reg;
1295361f3803SAkinobu Mita 		ret = ov965x_write(ov965x, REG_TSLB, reg);
129684a15dedSSylwester Nawrocki 		if (ret < 0)
129784a15dedSSylwester Nawrocki 			return ret;
129884a15dedSSylwester Nawrocki 	}
129984a15dedSSylwester Nawrocki 	ret = ov965x_set_default_gamma_curve(ov965x);
130084a15dedSSylwester Nawrocki 	if (ret < 0)
130184a15dedSSylwester Nawrocki 		return ret;
130284a15dedSSylwester Nawrocki 	ret = ov965x_set_color_matrix(ov965x);
130384a15dedSSylwester Nawrocki 	if (ret < 0)
130484a15dedSSylwester Nawrocki 		return ret;
130584a15dedSSylwester Nawrocki 	/*
130684a15dedSSylwester Nawrocki 	 * Select manual banding filter, the filter will
130784a15dedSSylwester Nawrocki 	 * be enabled further if required.
130884a15dedSSylwester Nawrocki 	 */
1309361f3803SAkinobu Mita 	ret = ov965x_read(ov965x, REG_COM11, &reg);
131084a15dedSSylwester Nawrocki 	if (!ret)
131184a15dedSSylwester Nawrocki 		reg |= COM11_BANDING;
1312361f3803SAkinobu Mita 	ret = ov965x_write(ov965x, REG_COM11, reg);
131384a15dedSSylwester Nawrocki 	if (ret < 0)
131484a15dedSSylwester Nawrocki 		return ret;
131584a15dedSSylwester Nawrocki 	/*
131684a15dedSSylwester Nawrocki 	 * Banding filter (REG_MBD value) needs to match selected
131784a15dedSSylwester Nawrocki 	 * resolution and frame rate, so it's always updated here.
131884a15dedSSylwester Nawrocki 	 */
131984a15dedSSylwester Nawrocki 	return ov965x_set_banding_filter(ov965x, ctrls->light_freq->val);
132084a15dedSSylwester Nawrocki }
132184a15dedSSylwester Nawrocki 
ov965x_s_stream(struct v4l2_subdev * sd,int on)132284a15dedSSylwester Nawrocki static int ov965x_s_stream(struct v4l2_subdev *sd, int on)
132384a15dedSSylwester Nawrocki {
132484a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
132584a15dedSSylwester Nawrocki 	struct ov965x_ctrls *ctrls = &ov965x->ctrls;
132684a15dedSSylwester Nawrocki 	int ret = 0;
132784a15dedSSylwester Nawrocki 
1328361f3803SAkinobu Mita 	v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on);
132984a15dedSSylwester Nawrocki 
133084a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
133184a15dedSSylwester Nawrocki 	if (ov965x->streaming == !on) {
133284a15dedSSylwester Nawrocki 		if (on)
133384a15dedSSylwester Nawrocki 			ret = __ov965x_set_params(ov965x);
133484a15dedSSylwester Nawrocki 
133584a15dedSSylwester Nawrocki 		if (!ret && ctrls->update) {
133684a15dedSSylwester Nawrocki 			/*
133784a15dedSSylwester Nawrocki 			 * ov965x_s_ctrl callback takes the mutex
133884a15dedSSylwester Nawrocki 			 * so it needs to be released here.
133984a15dedSSylwester Nawrocki 			 */
134084a15dedSSylwester Nawrocki 			mutex_unlock(&ov965x->lock);
134184a15dedSSylwester Nawrocki 			ret = v4l2_ctrl_handler_setup(&ctrls->handler);
134284a15dedSSylwester Nawrocki 
134384a15dedSSylwester Nawrocki 			mutex_lock(&ov965x->lock);
134484a15dedSSylwester Nawrocki 			if (!ret)
134584a15dedSSylwester Nawrocki 				ctrls->update = 0;
134684a15dedSSylwester Nawrocki 		}
134784a15dedSSylwester Nawrocki 		if (!ret)
1348361f3803SAkinobu Mita 			ret = ov965x_write(ov965x, REG_COM2,
134984a15dedSSylwester Nawrocki 					   on ? 0x01 : 0x11);
135084a15dedSSylwester Nawrocki 	}
135184a15dedSSylwester Nawrocki 	if (!ret)
135284a15dedSSylwester Nawrocki 		ov965x->streaming += on ? 1 : -1;
135384a15dedSSylwester Nawrocki 
135484a15dedSSylwester Nawrocki 	WARN_ON(ov965x->streaming < 0);
135584a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
135684a15dedSSylwester Nawrocki 
135784a15dedSSylwester Nawrocki 	return ret;
135884a15dedSSylwester Nawrocki }
135984a15dedSSylwester Nawrocki 
136084a15dedSSylwester Nawrocki /*
136184a15dedSSylwester Nawrocki  * V4L2 subdev internal operations
136284a15dedSSylwester Nawrocki  */
ov965x_open(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)136384a15dedSSylwester Nawrocki static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
136484a15dedSSylwester Nawrocki {
13650a7a1345SHugues Fruchet 	struct v4l2_mbus_framefmt *mf =
13660d346d2aSTomi Valkeinen 		v4l2_subdev_get_try_format(sd, fh->state, 0);
136784a15dedSSylwester Nawrocki 
136884a15dedSSylwester Nawrocki 	ov965x_get_default_format(mf);
136984a15dedSSylwester Nawrocki 	return 0;
137084a15dedSSylwester Nawrocki }
137184a15dedSSylwester Nawrocki 
137284a15dedSSylwester Nawrocki static const struct v4l2_subdev_pad_ops ov965x_pad_ops = {
137384a15dedSSylwester Nawrocki 	.enum_mbus_code = ov965x_enum_mbus_code,
137484a15dedSSylwester Nawrocki 	.enum_frame_size = ov965x_enum_frame_sizes,
137584a15dedSSylwester Nawrocki 	.get_fmt = ov965x_get_fmt,
137684a15dedSSylwester Nawrocki 	.set_fmt = ov965x_set_fmt,
137784a15dedSSylwester Nawrocki };
137884a15dedSSylwester Nawrocki 
137984a15dedSSylwester Nawrocki static const struct v4l2_subdev_video_ops ov965x_video_ops = {
138084a15dedSSylwester Nawrocki 	.s_stream = ov965x_s_stream,
138184a15dedSSylwester Nawrocki 	.g_frame_interval = ov965x_g_frame_interval,
138284a15dedSSylwester Nawrocki 	.s_frame_interval = ov965x_s_frame_interval,
138384a15dedSSylwester Nawrocki 
138484a15dedSSylwester Nawrocki };
138584a15dedSSylwester Nawrocki 
138684a15dedSSylwester Nawrocki static const struct v4l2_subdev_internal_ops ov965x_sd_internal_ops = {
138784a15dedSSylwester Nawrocki 	.open = ov965x_open,
138884a15dedSSylwester Nawrocki };
138984a15dedSSylwester Nawrocki 
139084a15dedSSylwester Nawrocki static const struct v4l2_subdev_core_ops ov965x_core_ops = {
139184a15dedSSylwester Nawrocki 	.s_power = ov965x_s_power,
139284a15dedSSylwester Nawrocki 	.log_status = v4l2_ctrl_subdev_log_status,
139384a15dedSSylwester Nawrocki 	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
139484a15dedSSylwester Nawrocki 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
139584a15dedSSylwester Nawrocki };
139684a15dedSSylwester Nawrocki 
139784a15dedSSylwester Nawrocki static const struct v4l2_subdev_ops ov965x_subdev_ops = {
139884a15dedSSylwester Nawrocki 	.core = &ov965x_core_ops,
139984a15dedSSylwester Nawrocki 	.pad = &ov965x_pad_ops,
140084a15dedSSylwester Nawrocki 	.video = &ov965x_video_ops,
140184a15dedSSylwester Nawrocki };
140284a15dedSSylwester Nawrocki 
ov965x_configure_gpios(struct ov965x * ov965x)1403b1f5d0aeSAkinobu Mita static int ov965x_configure_gpios(struct ov965x *ov965x)
1404b1f5d0aeSAkinobu Mita {
1405361f3803SAkinobu Mita 	struct device *dev = regmap_get_device(ov965x->regmap);
1406b1f5d0aeSAkinobu Mita 
1407b1f5d0aeSAkinobu Mita 	ov965x->gpios[GPIO_PWDN] = devm_gpiod_get_optional(dev, "powerdown",
1408b1f5d0aeSAkinobu Mita 							GPIOD_OUT_HIGH);
1409b1f5d0aeSAkinobu Mita 	if (IS_ERR(ov965x->gpios[GPIO_PWDN])) {
1410b1f5d0aeSAkinobu Mita 		dev_info(dev, "can't get %s GPIO\n", "powerdown");
1411b1f5d0aeSAkinobu Mita 		return PTR_ERR(ov965x->gpios[GPIO_PWDN]);
1412b1f5d0aeSAkinobu Mita 	}
1413b1f5d0aeSAkinobu Mita 
1414b1f5d0aeSAkinobu Mita 	ov965x->gpios[GPIO_RST] = devm_gpiod_get_optional(dev, "reset",
1415b1f5d0aeSAkinobu Mita 							GPIOD_OUT_HIGH);
1416b1f5d0aeSAkinobu Mita 	if (IS_ERR(ov965x->gpios[GPIO_RST])) {
1417b1f5d0aeSAkinobu Mita 		dev_info(dev, "can't get %s GPIO\n", "reset");
1418b1f5d0aeSAkinobu Mita 		return PTR_ERR(ov965x->gpios[GPIO_RST]);
141984a15dedSSylwester Nawrocki 	}
142084a15dedSSylwester Nawrocki 
142184a15dedSSylwester Nawrocki 	return 0;
142284a15dedSSylwester Nawrocki }
142384a15dedSSylwester Nawrocki 
ov965x_detect_sensor(struct v4l2_subdev * sd)142484a15dedSSylwester Nawrocki static int ov965x_detect_sensor(struct v4l2_subdev *sd)
142584a15dedSSylwester Nawrocki {
142684a15dedSSylwester Nawrocki 	struct ov965x *ov965x = to_ov965x(sd);
142784a15dedSSylwester Nawrocki 	u8 pid, ver;
142884a15dedSSylwester Nawrocki 	int ret;
142984a15dedSSylwester Nawrocki 
143084a15dedSSylwester Nawrocki 	mutex_lock(&ov965x->lock);
1431b1f5d0aeSAkinobu Mita 	ret = __ov965x_set_power(ov965x, 1);
1432b1f5d0aeSAkinobu Mita 	if (ret)
1433b1f5d0aeSAkinobu Mita 		goto out;
1434b1f5d0aeSAkinobu Mita 
14350df03379SNicholas Mc Guire 	msleep(25);
143684a15dedSSylwester Nawrocki 
143784a15dedSSylwester Nawrocki 	/* Check sensor revision */
1438361f3803SAkinobu Mita 	ret = ov965x_read(ov965x, REG_PID, &pid);
143984a15dedSSylwester Nawrocki 	if (!ret)
1440361f3803SAkinobu Mita 		ret = ov965x_read(ov965x, REG_VER, &ver);
144184a15dedSSylwester Nawrocki 
144284a15dedSSylwester Nawrocki 	__ov965x_set_power(ov965x, 0);
144384a15dedSSylwester Nawrocki 
144484a15dedSSylwester Nawrocki 	if (!ret) {
144584a15dedSSylwester Nawrocki 		ov965x->id = OV965X_ID(pid, ver);
144684a15dedSSylwester Nawrocki 		if (ov965x->id == OV9650_ID || ov965x->id == OV9652_ID) {
144784a15dedSSylwester Nawrocki 			v4l2_info(sd, "Found OV%04X sensor\n", ov965x->id);
144884a15dedSSylwester Nawrocki 		} else {
144992fbe032SChristophe JAILLET 			v4l2_err(sd, "Sensor detection failed (%04X)\n",
145092fbe032SChristophe JAILLET 				 ov965x->id);
145184a15dedSSylwester Nawrocki 			ret = -ENODEV;
145284a15dedSSylwester Nawrocki 		}
145384a15dedSSylwester Nawrocki 	}
1454b1f5d0aeSAkinobu Mita out:
145584a15dedSSylwester Nawrocki 	mutex_unlock(&ov965x->lock);
145684a15dedSSylwester Nawrocki 
145784a15dedSSylwester Nawrocki 	return ret;
145884a15dedSSylwester Nawrocki }
145984a15dedSSylwester Nawrocki 
ov965x_probe(struct i2c_client * client)1460e6714993SKieran Bingham static int ov965x_probe(struct i2c_client *client)
146184a15dedSSylwester Nawrocki {
146284a15dedSSylwester Nawrocki 	struct v4l2_subdev *sd;
146384a15dedSSylwester Nawrocki 	struct ov965x *ov965x;
146484a15dedSSylwester Nawrocki 	int ret;
1465361f3803SAkinobu Mita 	static const struct regmap_config ov965x_regmap_config = {
1466361f3803SAkinobu Mita 		.reg_bits = 8,
1467361f3803SAkinobu Mita 		.val_bits = 8,
1468361f3803SAkinobu Mita 		.max_register = 0xab,
1469361f3803SAkinobu Mita 	};
147084a15dedSSylwester Nawrocki 
147184a15dedSSylwester Nawrocki 	ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL);
147284a15dedSSylwester Nawrocki 	if (!ov965x)
147384a15dedSSylwester Nawrocki 		return -ENOMEM;
147484a15dedSSylwester Nawrocki 
1475361f3803SAkinobu Mita 	ov965x->regmap = devm_regmap_init_sccb(client, &ov965x_regmap_config);
1476361f3803SAkinobu Mita 	if (IS_ERR(ov965x->regmap)) {
1477361f3803SAkinobu Mita 		dev_err(&client->dev, "Failed to allocate register map\n");
1478361f3803SAkinobu Mita 		return PTR_ERR(ov965x->regmap);
1479361f3803SAkinobu Mita 	}
1480b1f5d0aeSAkinobu Mita 
148127cdfbdbSLinus Walleij 	if (dev_fwnode(&client->dev)) {
1482361f3803SAkinobu Mita 		ov965x->clk = devm_clk_get(&client->dev, NULL);
1483b1f5d0aeSAkinobu Mita 		if (IS_ERR(ov965x->clk))
1484b1f5d0aeSAkinobu Mita 			return PTR_ERR(ov965x->clk);
1485b1f5d0aeSAkinobu Mita 		ov965x->mclk_frequency = clk_get_rate(ov965x->clk);
1486b1f5d0aeSAkinobu Mita 
1487b1f5d0aeSAkinobu Mita 		ret = ov965x_configure_gpios(ov965x);
1488b1f5d0aeSAkinobu Mita 		if (ret < 0)
1489b1f5d0aeSAkinobu Mita 			return ret;
1490b1f5d0aeSAkinobu Mita 	} else {
1491b1f5d0aeSAkinobu Mita 		dev_err(&client->dev,
149227cdfbdbSLinus Walleij 			"No device properties specified\n");
1493b1f5d0aeSAkinobu Mita 
1494b1f5d0aeSAkinobu Mita 		return -EINVAL;
1495b1f5d0aeSAkinobu Mita 	}
1496b1f5d0aeSAkinobu Mita 
1497b1f5d0aeSAkinobu Mita 	mutex_init(&ov965x->lock);
1498b1f5d0aeSAkinobu Mita 
149984a15dedSSylwester Nawrocki 	sd = &ov965x->sd;
150084a15dedSSylwester Nawrocki 	v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops);
1501c0decac1SMauro Carvalho Chehab 	strscpy(sd->name, DRIVER_NAME, sizeof(sd->name));
150284a15dedSSylwester Nawrocki 
150384a15dedSSylwester Nawrocki 	sd->internal_ops = &ov965x_sd_internal_ops;
150484a15dedSSylwester Nawrocki 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
150584a15dedSSylwester Nawrocki 		     V4L2_SUBDEV_FL_HAS_EVENTS;
150684a15dedSSylwester Nawrocki 
150784a15dedSSylwester Nawrocki 	ov965x->pad.flags = MEDIA_PAD_FL_SOURCE;
15084ca72efaSMauro Carvalho Chehab 	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
1509ab22e77cSMauro Carvalho Chehab 	ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad);
151084a15dedSSylwester Nawrocki 	if (ret < 0)
1511457a1a7aSHugues Fruchet 		goto err_mutex;
151284a15dedSSylwester Nawrocki 
151384a15dedSSylwester Nawrocki 	ret = ov965x_initialize_controls(ov965x);
151484a15dedSSylwester Nawrocki 	if (ret < 0)
151584a15dedSSylwester Nawrocki 		goto err_me;
151684a15dedSSylwester Nawrocki 
151784a15dedSSylwester Nawrocki 	ov965x_get_default_format(&ov965x->format);
151884a15dedSSylwester Nawrocki 	ov965x->frame_size = &ov965x_framesizes[0];
151984a15dedSSylwester Nawrocki 	ov965x->fiv = &ov965x_intervals[0];
152084a15dedSSylwester Nawrocki 
152184a15dedSSylwester Nawrocki 	ret = ov965x_detect_sensor(sd);
152284a15dedSSylwester Nawrocki 	if (ret < 0)
152384a15dedSSylwester Nawrocki 		goto err_ctrls;
152484a15dedSSylwester Nawrocki 
152584a15dedSSylwester Nawrocki 	/* Update exposure time min/max to match frame format */
152684a15dedSSylwester Nawrocki 	ov965x_update_exposure_ctrl(ov965x);
152784a15dedSSylwester Nawrocki 
15286dca6cf0SJavier Martinez Canillas 	ret = v4l2_async_register_subdev(sd);
15296dca6cf0SJavier Martinez Canillas 	if (ret < 0)
15306dca6cf0SJavier Martinez Canillas 		goto err_ctrls;
15316dca6cf0SJavier Martinez Canillas 
153284a15dedSSylwester Nawrocki 	return 0;
153384a15dedSSylwester Nawrocki err_ctrls:
153484a15dedSSylwester Nawrocki 	v4l2_ctrl_handler_free(sd->ctrl_handler);
153584a15dedSSylwester Nawrocki err_me:
153684a15dedSSylwester Nawrocki 	media_entity_cleanup(&sd->entity);
1537457a1a7aSHugues Fruchet err_mutex:
1538457a1a7aSHugues Fruchet 	mutex_destroy(&ov965x->lock);
153984a15dedSSylwester Nawrocki 	return ret;
154084a15dedSSylwester Nawrocki }
154184a15dedSSylwester Nawrocki 
ov965x_remove(struct i2c_client * client)1542ed5c2f5fSUwe Kleine-König static void ov965x_remove(struct i2c_client *client)
154384a15dedSSylwester Nawrocki {
154484a15dedSSylwester Nawrocki 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1545457a1a7aSHugues Fruchet 	struct ov965x *ov965x = to_ov965x(sd);
154684a15dedSSylwester Nawrocki 
15476dca6cf0SJavier Martinez Canillas 	v4l2_async_unregister_subdev(sd);
154884a15dedSSylwester Nawrocki 	v4l2_ctrl_handler_free(sd->ctrl_handler);
154984a15dedSSylwester Nawrocki 	media_entity_cleanup(&sd->entity);
1550457a1a7aSHugues Fruchet 	mutex_destroy(&ov965x->lock);
155184a15dedSSylwester Nawrocki }
155284a15dedSSylwester Nawrocki 
155384a15dedSSylwester Nawrocki static const struct i2c_device_id ov965x_id[] = {
155484a15dedSSylwester Nawrocki 	{ "OV9650", 0 },
155584a15dedSSylwester Nawrocki 	{ "OV9652", 0 },
155684a15dedSSylwester Nawrocki 	{ /* sentinel */ }
155784a15dedSSylwester Nawrocki };
155884a15dedSSylwester Nawrocki MODULE_DEVICE_TABLE(i2c, ov965x_id);
155984a15dedSSylwester Nawrocki 
1560b1f5d0aeSAkinobu Mita #if IS_ENABLED(CONFIG_OF)
1561b1f5d0aeSAkinobu Mita static const struct of_device_id ov965x_of_match[] = {
1562b1f5d0aeSAkinobu Mita 	{ .compatible = "ovti,ov9650", },
1563b1f5d0aeSAkinobu Mita 	{ .compatible = "ovti,ov9652", },
1564b1f5d0aeSAkinobu Mita 	{ /* sentinel */ }
1565b1f5d0aeSAkinobu Mita };
1566b1f5d0aeSAkinobu Mita MODULE_DEVICE_TABLE(of, ov965x_of_match);
1567b1f5d0aeSAkinobu Mita #endif
1568b1f5d0aeSAkinobu Mita 
156984a15dedSSylwester Nawrocki static struct i2c_driver ov965x_i2c_driver = {
157084a15dedSSylwester Nawrocki 	.driver = {
157184a15dedSSylwester Nawrocki 		.name	= DRIVER_NAME,
1572b1f5d0aeSAkinobu Mita 		.of_match_table = of_match_ptr(ov965x_of_match),
157384a15dedSSylwester Nawrocki 	},
1574*aaeb31c0SUwe Kleine-König 	.probe		= ov965x_probe,
157584a15dedSSylwester Nawrocki 	.remove		= ov965x_remove,
157684a15dedSSylwester Nawrocki 	.id_table	= ov965x_id,
157784a15dedSSylwester Nawrocki };
157884a15dedSSylwester Nawrocki 
157984a15dedSSylwester Nawrocki module_i2c_driver(ov965x_i2c_driver);
158084a15dedSSylwester Nawrocki 
158184a15dedSSylwester Nawrocki MODULE_AUTHOR("Sylwester Nawrocki <sylvester.nawrocki@gmail.com>");
158284a15dedSSylwester Nawrocki MODULE_DESCRIPTION("OV9650/OV9652 CMOS Image Sensor driver");
158384a15dedSSylwester Nawrocki MODULE_LICENSE("GPL");
1584