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, ®);
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, ®);
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, ®);
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, ®);
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, ®);
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, ®);
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, ®0);
86984a15dedSSylwester Nawrocki if (ret < 0)
87084a15dedSSylwester Nawrocki return ret;
871361f3803SAkinobu Mita ret = ov965x_read(ov965x, REG_VREF, ®1);
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, ®0);
88329236349SMauro Carvalho Chehab if (ret < 0)
88429236349SMauro Carvalho Chehab return ret;
885361f3803SAkinobu Mita ret = ov965x_read(ov965x, REG_AECH, ®1);
88629236349SMauro Carvalho Chehab if (ret < 0)
88729236349SMauro Carvalho Chehab return ret;
888361f3803SAkinobu Mita ret = ov965x_read(ov965x, REG_AECHM, ®2);
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, ®);
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, ®);
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