1cb7a01acSMauro Carvalho Chehab /* 2cb7a01acSMauro Carvalho Chehab * A V4L2 driver for OmniVision OV7670 cameras. 3cb7a01acSMauro Carvalho Chehab * 4cb7a01acSMauro Carvalho Chehab * Copyright 2006 One Laptop Per Child Association, Inc. Written 5cb7a01acSMauro Carvalho Chehab * by Jonathan Corbet with substantial inspiration from Mark 6cb7a01acSMauro Carvalho Chehab * McClelland's ovcamchip code. 7cb7a01acSMauro Carvalho Chehab * 8cb7a01acSMauro Carvalho Chehab * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> 9cb7a01acSMauro Carvalho Chehab * 10cb7a01acSMauro Carvalho Chehab * This file may be distributed under the terms of the GNU General 11cb7a01acSMauro Carvalho Chehab * Public License, version 2. 12cb7a01acSMauro Carvalho Chehab */ 130a024d63SHans Verkuil #include <linux/clk.h> 14cb7a01acSMauro Carvalho Chehab #include <linux/init.h> 15cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 16cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 17cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 18cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 19cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 20a0c4164eSHans Verkuil #include <linux/gpio.h> 21a0c4164eSHans Verkuil #include <linux/gpio/consumer.h> 22cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 237852adf8SAkinobu Mita #include <media/v4l2-event.h> 24492959c7SJavier Martin #include <media/v4l2-ctrls.h> 2501b84448SJacopo Mondi #include <media/v4l2-fwnode.h> 26cb7a01acSMauro Carvalho Chehab #include <media/v4l2-mediabus.h> 274721b3ebSAxel Lin #include <media/v4l2-image-sizes.h> 28b5dcee22SMauro Carvalho Chehab #include <media/i2c/ov7670.h> 29cb7a01acSMauro Carvalho Chehab 30cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); 31cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); 32cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 33cb7a01acSMauro Carvalho Chehab 34cb7a01acSMauro Carvalho Chehab static bool debug; 35cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644); 36cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)"); 37cb7a01acSMauro Carvalho Chehab 38cb7a01acSMauro Carvalho Chehab /* 39cb7a01acSMauro Carvalho Chehab * The 7670 sits on i2c with ID 0x42 40cb7a01acSMauro Carvalho Chehab */ 41cb7a01acSMauro Carvalho Chehab #define OV7670_I2C_ADDR 0x42 42cb7a01acSMauro Carvalho Chehab 43f6dd927fSJavier Martin #define PLL_FACTOR 4 44f6dd927fSJavier Martin 45cb7a01acSMauro Carvalho Chehab /* Registers */ 46cb7a01acSMauro Carvalho Chehab #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ 47cb7a01acSMauro Carvalho Chehab #define REG_BLUE 0x01 /* blue gain */ 48cb7a01acSMauro Carvalho Chehab #define REG_RED 0x02 /* red gain */ 49cb7a01acSMauro Carvalho Chehab #define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ 50cb7a01acSMauro Carvalho Chehab #define REG_COM1 0x04 /* Control 1 */ 51cb7a01acSMauro Carvalho Chehab #define COM1_CCIR656 0x40 /* CCIR656 enable */ 52cb7a01acSMauro Carvalho Chehab #define REG_BAVE 0x05 /* U/B Average level */ 53cb7a01acSMauro Carvalho Chehab #define REG_GbAVE 0x06 /* Y/Gb Average level */ 54cb7a01acSMauro Carvalho Chehab #define REG_AECHH 0x07 /* AEC MS 5 bits */ 55cb7a01acSMauro Carvalho Chehab #define REG_RAVE 0x08 /* V/R Average level */ 56cb7a01acSMauro Carvalho Chehab #define REG_COM2 0x09 /* Control 2 */ 57cb7a01acSMauro Carvalho Chehab #define COM2_SSLEEP 0x10 /* Soft sleep mode */ 58cb7a01acSMauro Carvalho Chehab #define REG_PID 0x0a /* Product ID MSB */ 59cb7a01acSMauro Carvalho Chehab #define REG_VER 0x0b /* Product ID LSB */ 60cb7a01acSMauro Carvalho Chehab #define REG_COM3 0x0c /* Control 3 */ 61cb7a01acSMauro Carvalho Chehab #define COM3_SWAP 0x40 /* Byte swap */ 62cb7a01acSMauro Carvalho Chehab #define COM3_SCALEEN 0x08 /* Enable scaling */ 63cb7a01acSMauro Carvalho Chehab #define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ 64cb7a01acSMauro Carvalho Chehab #define REG_COM4 0x0d /* Control 4 */ 65cb7a01acSMauro Carvalho Chehab #define REG_COM5 0x0e /* All "reserved" */ 66cb7a01acSMauro Carvalho Chehab #define REG_COM6 0x0f /* Control 6 */ 67cb7a01acSMauro Carvalho Chehab #define REG_AECH 0x10 /* More bits of AEC value */ 68cb7a01acSMauro Carvalho Chehab #define REG_CLKRC 0x11 /* Clocl control */ 69cb7a01acSMauro Carvalho Chehab #define CLK_EXT 0x40 /* Use external clock directly */ 70cb7a01acSMauro Carvalho Chehab #define CLK_SCALE 0x3f /* Mask for internal clock scale */ 71cb7a01acSMauro Carvalho Chehab #define REG_COM7 0x12 /* Control 7 */ 72cb7a01acSMauro Carvalho Chehab #define COM7_RESET 0x80 /* Register reset */ 73cb7a01acSMauro Carvalho Chehab #define COM7_FMT_MASK 0x38 74cb7a01acSMauro Carvalho Chehab #define COM7_FMT_VGA 0x00 75cb7a01acSMauro Carvalho Chehab #define COM7_FMT_CIF 0x20 /* CIF format */ 76cb7a01acSMauro Carvalho Chehab #define COM7_FMT_QVGA 0x10 /* QVGA format */ 77cb7a01acSMauro Carvalho Chehab #define COM7_FMT_QCIF 0x08 /* QCIF format */ 78cb7a01acSMauro Carvalho Chehab #define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ 79cb7a01acSMauro Carvalho Chehab #define COM7_YUV 0x00 /* YUV */ 80cb7a01acSMauro Carvalho Chehab #define COM7_BAYER 0x01 /* Bayer format */ 81cb7a01acSMauro Carvalho Chehab #define COM7_PBAYER 0x05 /* "Processed bayer" */ 82cb7a01acSMauro Carvalho Chehab #define REG_COM8 0x13 /* Control 8 */ 83cb7a01acSMauro Carvalho Chehab #define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ 84cb7a01acSMauro Carvalho Chehab #define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ 85cb7a01acSMauro Carvalho Chehab #define COM8_BFILT 0x20 /* Band filter enable */ 86cb7a01acSMauro Carvalho Chehab #define COM8_AGC 0x04 /* Auto gain enable */ 87cb7a01acSMauro Carvalho Chehab #define COM8_AWB 0x02 /* White balance enable */ 88cb7a01acSMauro Carvalho Chehab #define COM8_AEC 0x01 /* Auto exposure enable */ 89cb7a01acSMauro Carvalho Chehab #define REG_COM9 0x14 /* Control 9 - gain ceiling */ 90cb7a01acSMauro Carvalho Chehab #define REG_COM10 0x15 /* Control 10 */ 91cb7a01acSMauro Carvalho Chehab #define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ 92cb7a01acSMauro Carvalho Chehab #define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ 93cb7a01acSMauro Carvalho Chehab #define COM10_HREF_REV 0x08 /* Reverse HREF */ 94cb7a01acSMauro Carvalho Chehab #define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ 95cb7a01acSMauro Carvalho Chehab #define COM10_VS_NEG 0x02 /* VSYNC negative */ 96cb7a01acSMauro Carvalho Chehab #define COM10_HS_NEG 0x01 /* HSYNC negative */ 97cb7a01acSMauro Carvalho Chehab #define REG_HSTART 0x17 /* Horiz start high bits */ 98cb7a01acSMauro Carvalho Chehab #define REG_HSTOP 0x18 /* Horiz stop high bits */ 99cb7a01acSMauro Carvalho Chehab #define REG_VSTART 0x19 /* Vert start high bits */ 100cb7a01acSMauro Carvalho Chehab #define REG_VSTOP 0x1a /* Vert stop high bits */ 101cb7a01acSMauro Carvalho Chehab #define REG_PSHFT 0x1b /* Pixel delay after HREF */ 102cb7a01acSMauro Carvalho Chehab #define REG_MIDH 0x1c /* Manuf. ID high */ 103cb7a01acSMauro Carvalho Chehab #define REG_MIDL 0x1d /* Manuf. ID low */ 104cb7a01acSMauro Carvalho Chehab #define REG_MVFP 0x1e /* Mirror / vflip */ 105cb7a01acSMauro Carvalho Chehab #define MVFP_MIRROR 0x20 /* Mirror image */ 106cb7a01acSMauro Carvalho Chehab #define MVFP_FLIP 0x10 /* Vertical flip */ 107cb7a01acSMauro Carvalho Chehab 108cb7a01acSMauro Carvalho Chehab #define REG_AEW 0x24 /* AGC upper limit */ 109cb7a01acSMauro Carvalho Chehab #define REG_AEB 0x25 /* AGC lower limit */ 110cb7a01acSMauro Carvalho Chehab #define REG_VPT 0x26 /* AGC/AEC fast mode op region */ 111cb7a01acSMauro Carvalho Chehab #define REG_HSYST 0x30 /* HSYNC rising edge delay */ 112cb7a01acSMauro Carvalho Chehab #define REG_HSYEN 0x31 /* HSYNC falling edge delay */ 113cb7a01acSMauro Carvalho Chehab #define REG_HREF 0x32 /* HREF pieces */ 114cb7a01acSMauro Carvalho Chehab #define REG_TSLB 0x3a /* lots of stuff */ 115cb7a01acSMauro Carvalho Chehab #define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ 116cb7a01acSMauro Carvalho Chehab #define REG_COM11 0x3b /* Control 11 */ 117cb7a01acSMauro Carvalho Chehab #define COM11_NIGHT 0x80 /* NIght mode enable */ 118cb7a01acSMauro Carvalho Chehab #define COM11_NMFR 0x60 /* Two bit NM frame rate */ 119cb7a01acSMauro Carvalho Chehab #define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ 120cb7a01acSMauro Carvalho Chehab #define COM11_50HZ 0x08 /* Manual 50Hz select */ 121cb7a01acSMauro Carvalho Chehab #define COM11_EXP 0x02 122cb7a01acSMauro Carvalho Chehab #define REG_COM12 0x3c /* Control 12 */ 123cb7a01acSMauro Carvalho Chehab #define COM12_HREF 0x80 /* HREF always */ 124cb7a01acSMauro Carvalho Chehab #define REG_COM13 0x3d /* Control 13 */ 125cb7a01acSMauro Carvalho Chehab #define COM13_GAMMA 0x80 /* Gamma enable */ 126cb7a01acSMauro Carvalho Chehab #define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ 127cb7a01acSMauro Carvalho Chehab #define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ 128cb7a01acSMauro Carvalho Chehab #define REG_COM14 0x3e /* Control 14 */ 129cb7a01acSMauro Carvalho Chehab #define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ 130cb7a01acSMauro Carvalho Chehab #define REG_EDGE 0x3f /* Edge enhancement factor */ 131cb7a01acSMauro Carvalho Chehab #define REG_COM15 0x40 /* Control 15 */ 132cb7a01acSMauro Carvalho Chehab #define COM15_R10F0 0x00 /* Data range 10 to F0 */ 133cb7a01acSMauro Carvalho Chehab #define COM15_R01FE 0x80 /* 01 to FE */ 134cb7a01acSMauro Carvalho Chehab #define COM15_R00FF 0xc0 /* 00 to FF */ 135cb7a01acSMauro Carvalho Chehab #define COM15_RGB565 0x10 /* RGB565 output */ 136cb7a01acSMauro Carvalho Chehab #define COM15_RGB555 0x30 /* RGB555 output */ 137cb7a01acSMauro Carvalho Chehab #define REG_COM16 0x41 /* Control 16 */ 138cb7a01acSMauro Carvalho Chehab #define COM16_AWBGAIN 0x08 /* AWB gain enable */ 139cb7a01acSMauro Carvalho Chehab #define REG_COM17 0x42 /* Control 17 */ 140cb7a01acSMauro Carvalho Chehab #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ 141cb7a01acSMauro Carvalho Chehab #define COM17_CBAR 0x08 /* DSP Color bar */ 142cb7a01acSMauro Carvalho Chehab 143cb7a01acSMauro Carvalho Chehab /* 144cb7a01acSMauro Carvalho Chehab * This matrix defines how the colors are generated, must be 145cb7a01acSMauro Carvalho Chehab * tweaked to adjust hue and saturation. 146cb7a01acSMauro Carvalho Chehab * 147cb7a01acSMauro Carvalho Chehab * Order: v-red, v-green, v-blue, u-red, u-green, u-blue 148cb7a01acSMauro Carvalho Chehab * 149cb7a01acSMauro Carvalho Chehab * They are nine-bit signed quantities, with the sign bit 150cb7a01acSMauro Carvalho Chehab * stored in 0x58. Sign for v-red is bit 0, and up from there. 151cb7a01acSMauro Carvalho Chehab */ 152cb7a01acSMauro Carvalho Chehab #define REG_CMATRIX_BASE 0x4f 153cb7a01acSMauro Carvalho Chehab #define CMATRIX_LEN 6 154cb7a01acSMauro Carvalho Chehab #define REG_CMATRIX_SIGN 0x58 155cb7a01acSMauro Carvalho Chehab 156cb7a01acSMauro Carvalho Chehab 157cb7a01acSMauro Carvalho Chehab #define REG_BRIGHT 0x55 /* Brightness */ 158cb7a01acSMauro Carvalho Chehab #define REG_CONTRAS 0x56 /* Contrast control */ 159cb7a01acSMauro Carvalho Chehab 160cb7a01acSMauro Carvalho Chehab #define REG_GFIX 0x69 /* Fix gain control */ 161cb7a01acSMauro Carvalho Chehab 162f6dd927fSJavier Martin #define REG_DBLV 0x6b /* PLL control an debugging */ 16361da76beSJacopo Mondi #define DBLV_BYPASS 0x0a /* Bypass PLL */ 16461da76beSJacopo Mondi #define DBLV_X4 0x4a /* clock x4 */ 16561da76beSJacopo Mondi #define DBLV_X6 0x8a /* clock x6 */ 16661da76beSJacopo Mondi #define DBLV_X8 0xca /* clock x8 */ 167f6dd927fSJavier Martin 168b48d908dSAkinobu Mita #define REG_SCALING_XSC 0x70 /* Test pattern and horizontal scale factor */ 169b48d908dSAkinobu Mita #define TEST_PATTTERN_0 0x80 170b48d908dSAkinobu Mita #define REG_SCALING_YSC 0x71 /* Test pattern and vertical scale factor */ 171b48d908dSAkinobu Mita #define TEST_PATTTERN_1 0x80 172b48d908dSAkinobu Mita 173cb7a01acSMauro Carvalho Chehab #define REG_REG76 0x76 /* OV's name */ 174cb7a01acSMauro Carvalho Chehab #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ 175cb7a01acSMauro Carvalho Chehab #define R76_WHTPCOR 0x40 /* White pixel correction enable */ 176cb7a01acSMauro Carvalho Chehab 177cb7a01acSMauro Carvalho Chehab #define REG_RGB444 0x8c /* RGB 444 control */ 178cb7a01acSMauro Carvalho Chehab #define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ 179cb7a01acSMauro Carvalho Chehab #define R444_RGBX 0x01 /* Empty nibble at end */ 180cb7a01acSMauro Carvalho Chehab 181cb7a01acSMauro Carvalho Chehab #define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ 182cb7a01acSMauro Carvalho Chehab #define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ 183cb7a01acSMauro Carvalho Chehab 184cb7a01acSMauro Carvalho Chehab #define REG_BD50MAX 0xa5 /* 50hz banding step limit */ 185cb7a01acSMauro Carvalho Chehab #define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ 186cb7a01acSMauro Carvalho Chehab #define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ 187cb7a01acSMauro Carvalho Chehab #define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ 188cb7a01acSMauro Carvalho Chehab #define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ 189cb7a01acSMauro Carvalho Chehab #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ 190cb7a01acSMauro Carvalho Chehab #define REG_BD60MAX 0xab /* 60hz banding step limit */ 191cb7a01acSMauro Carvalho Chehab 192d058e237SJavier Martin enum ov7670_model { 193d058e237SJavier Martin MODEL_OV7670 = 0, 194d058e237SJavier Martin MODEL_OV7675, 195d058e237SJavier Martin }; 196d058e237SJavier Martin 197d058e237SJavier Martin struct ov7670_win_size { 198d058e237SJavier Martin int width; 199d058e237SJavier Martin int height; 200d058e237SJavier Martin unsigned char com7_bit; 201d058e237SJavier Martin int hstart; /* Start/stop values for the camera. Note */ 202d058e237SJavier Martin int hstop; /* that they do not always make complete */ 203d058e237SJavier Martin int vstart; /* sense to humans, but evidently the sensor */ 204d058e237SJavier Martin int vstop; /* will do the right thing... */ 205d058e237SJavier Martin struct regval_list *regs; /* Regs to tweak */ 206d058e237SJavier Martin }; 207d058e237SJavier Martin 208d058e237SJavier Martin struct ov7670_devtype { 209d058e237SJavier Martin /* formats supported for each model */ 210d058e237SJavier Martin struct ov7670_win_size *win_sizes; 211d058e237SJavier Martin unsigned int n_win_sizes; 212f6dd927fSJavier Martin /* callbacks for frame rate control */ 213f6dd927fSJavier Martin int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *); 214f6dd927fSJavier Martin void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *); 215d058e237SJavier Martin }; 216cb7a01acSMauro Carvalho Chehab 217cb7a01acSMauro Carvalho Chehab /* 218cb7a01acSMauro Carvalho Chehab * Information we maintain about a known sensor. 219cb7a01acSMauro Carvalho Chehab */ 220cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct; /* coming later */ 221cb7a01acSMauro Carvalho Chehab struct ov7670_info { 222cb7a01acSMauro Carvalho Chehab struct v4l2_subdev sd; 223d94a26f0SWenyou Yang #if defined(CONFIG_MEDIA_CONTROLLER) 224d94a26f0SWenyou Yang struct media_pad pad; 225d94a26f0SWenyou Yang #endif 226492959c7SJavier Martin struct v4l2_ctrl_handler hdl; 227492959c7SJavier Martin struct { 228492959c7SJavier Martin /* gain cluster */ 229492959c7SJavier Martin struct v4l2_ctrl *auto_gain; 230492959c7SJavier Martin struct v4l2_ctrl *gain; 231492959c7SJavier Martin }; 232492959c7SJavier Martin struct { 233492959c7SJavier Martin /* exposure cluster */ 234492959c7SJavier Martin struct v4l2_ctrl *auto_exposure; 235492959c7SJavier Martin struct v4l2_ctrl *exposure; 236492959c7SJavier Martin }; 237492959c7SJavier Martin struct { 238492959c7SJavier Martin /* saturation/hue cluster */ 239492959c7SJavier Martin struct v4l2_ctrl *saturation; 240492959c7SJavier Martin struct v4l2_ctrl *hue; 241492959c7SJavier Martin }; 242c0662dd4SWenyou Yang struct v4l2_mbus_framefmt format; 243cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct *fmt; /* Current format */ 2445556ab2aSLubomir Rintel struct ov7670_win_size *wsize; 2450a024d63SHans Verkuil struct clk *clk; 246a0c4164eSHans Verkuil struct gpio_desc *resetb_gpio; 247a0c4164eSHans Verkuil struct gpio_desc *pwdn_gpio; 24801b84448SJacopo Mondi unsigned int mbus_config; /* Media bus configuration flags */ 249cb7a01acSMauro Carvalho Chehab int min_width; /* Filter out smaller sizes */ 250cb7a01acSMauro Carvalho Chehab int min_height; /* Filter out smaller sizes */ 251cb7a01acSMauro Carvalho Chehab int clock_speed; /* External clock speed (MHz) */ 252cb7a01acSMauro Carvalho Chehab u8 clkrc; /* Clock divider value */ 253cb7a01acSMauro Carvalho Chehab bool use_smbus; /* Use smbus I/O instead of I2C */ 25404ee6d92SJavier Martin bool pll_bypass; 255ee95258eSJavier Martin bool pclk_hb_disable; 256d058e237SJavier Martin const struct ov7670_devtype *devtype; /* Device specifics */ 257cb7a01acSMauro Carvalho Chehab }; 258cb7a01acSMauro Carvalho Chehab 259cb7a01acSMauro Carvalho Chehab static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) 260cb7a01acSMauro Carvalho Chehab { 261cb7a01acSMauro Carvalho Chehab return container_of(sd, struct ov7670_info, sd); 262cb7a01acSMauro Carvalho Chehab } 263cb7a01acSMauro Carvalho Chehab 264492959c7SJavier Martin static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 265492959c7SJavier Martin { 266492959c7SJavier Martin return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd; 267492959c7SJavier Martin } 268492959c7SJavier Martin 269cb7a01acSMauro Carvalho Chehab 270cb7a01acSMauro Carvalho Chehab 271cb7a01acSMauro Carvalho Chehab /* 272cb7a01acSMauro Carvalho Chehab * The default register settings, as obtained from OmniVision. There 273cb7a01acSMauro Carvalho Chehab * is really no making sense of most of these - lots of "reserved" values 274cb7a01acSMauro Carvalho Chehab * and such. 275cb7a01acSMauro Carvalho Chehab * 276cb7a01acSMauro Carvalho Chehab * These settings give VGA YUYV. 277cb7a01acSMauro Carvalho Chehab */ 278cb7a01acSMauro Carvalho Chehab 279cb7a01acSMauro Carvalho Chehab struct regval_list { 280cb7a01acSMauro Carvalho Chehab unsigned char reg_num; 281cb7a01acSMauro Carvalho Chehab unsigned char value; 282cb7a01acSMauro Carvalho Chehab }; 283cb7a01acSMauro Carvalho Chehab 284cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_default_regs[] = { 285cb7a01acSMauro Carvalho Chehab { REG_COM7, COM7_RESET }, 286cb7a01acSMauro Carvalho Chehab /* 287cb7a01acSMauro Carvalho Chehab * Clock scale: 3 = 15fps 288cb7a01acSMauro Carvalho Chehab * 2 = 20fps 289cb7a01acSMauro Carvalho Chehab * 1 = 30fps 290cb7a01acSMauro Carvalho Chehab */ 291cb7a01acSMauro Carvalho Chehab { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ 292cb7a01acSMauro Carvalho Chehab { REG_TSLB, 0x04 }, /* OV */ 293cb7a01acSMauro Carvalho Chehab { REG_COM7, 0 }, /* VGA */ 294cb7a01acSMauro Carvalho Chehab /* 295cb7a01acSMauro Carvalho Chehab * Set the hardware window. These values from OV don't entirely 296cb7a01acSMauro Carvalho Chehab * make sense - hstop is less than hstart. But they work... 297cb7a01acSMauro Carvalho Chehab */ 298cb7a01acSMauro Carvalho Chehab { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, 299cb7a01acSMauro Carvalho Chehab { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, 300cb7a01acSMauro Carvalho Chehab { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, 301cb7a01acSMauro Carvalho Chehab 302cb7a01acSMauro Carvalho Chehab { REG_COM3, 0 }, { REG_COM14, 0 }, 303cb7a01acSMauro Carvalho Chehab /* Mystery scaling numbers */ 304b48d908dSAkinobu Mita { REG_SCALING_XSC, 0x3a }, 305b48d908dSAkinobu Mita { REG_SCALING_YSC, 0x35 }, 306cb7a01acSMauro Carvalho Chehab { 0x72, 0x11 }, { 0x73, 0xf0 }, 307cb7a01acSMauro Carvalho Chehab { 0xa2, 0x02 }, { REG_COM10, 0x0 }, 308cb7a01acSMauro Carvalho Chehab 309cb7a01acSMauro Carvalho Chehab /* Gamma curve values */ 310cb7a01acSMauro Carvalho Chehab { 0x7a, 0x20 }, { 0x7b, 0x10 }, 311cb7a01acSMauro Carvalho Chehab { 0x7c, 0x1e }, { 0x7d, 0x35 }, 312cb7a01acSMauro Carvalho Chehab { 0x7e, 0x5a }, { 0x7f, 0x69 }, 313cb7a01acSMauro Carvalho Chehab { 0x80, 0x76 }, { 0x81, 0x80 }, 314cb7a01acSMauro Carvalho Chehab { 0x82, 0x88 }, { 0x83, 0x8f }, 315cb7a01acSMauro Carvalho Chehab { 0x84, 0x96 }, { 0x85, 0xa3 }, 316cb7a01acSMauro Carvalho Chehab { 0x86, 0xaf }, { 0x87, 0xc4 }, 317cb7a01acSMauro Carvalho Chehab { 0x88, 0xd7 }, { 0x89, 0xe8 }, 318cb7a01acSMauro Carvalho Chehab 319cb7a01acSMauro Carvalho Chehab /* AGC and AEC parameters. Note we start by disabling those features, 320cb7a01acSMauro Carvalho Chehab then turn them only after tweaking the values. */ 321cb7a01acSMauro Carvalho Chehab { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, 322cb7a01acSMauro Carvalho Chehab { REG_GAIN, 0 }, { REG_AECH, 0 }, 323cb7a01acSMauro Carvalho Chehab { REG_COM4, 0x40 }, /* magic reserved bit */ 324cb7a01acSMauro Carvalho Chehab { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ 325cb7a01acSMauro Carvalho Chehab { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, 326cb7a01acSMauro Carvalho Chehab { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, 327cb7a01acSMauro Carvalho Chehab { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, 328cb7a01acSMauro Carvalho Chehab { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ 329cb7a01acSMauro Carvalho Chehab { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, 330cb7a01acSMauro Carvalho Chehab { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, 331cb7a01acSMauro Carvalho Chehab { REG_HAECC7, 0x94 }, 332cb7a01acSMauro Carvalho Chehab { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, 333cb7a01acSMauro Carvalho Chehab 334cb7a01acSMauro Carvalho Chehab /* Almost all of these are magic "reserved" values. */ 335cb7a01acSMauro Carvalho Chehab { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, 336cb7a01acSMauro Carvalho Chehab { 0x16, 0x02 }, { REG_MVFP, 0x07 }, 337cb7a01acSMauro Carvalho Chehab { 0x21, 0x02 }, { 0x22, 0x91 }, 338cb7a01acSMauro Carvalho Chehab { 0x29, 0x07 }, { 0x33, 0x0b }, 339cb7a01acSMauro Carvalho Chehab { 0x35, 0x0b }, { 0x37, 0x1d }, 340cb7a01acSMauro Carvalho Chehab { 0x38, 0x71 }, { 0x39, 0x2a }, 341cb7a01acSMauro Carvalho Chehab { REG_COM12, 0x78 }, { 0x4d, 0x40 }, 342cb7a01acSMauro Carvalho Chehab { 0x4e, 0x20 }, { REG_GFIX, 0 }, 343cb7a01acSMauro Carvalho Chehab { 0x6b, 0x4a }, { 0x74, 0x10 }, 344cb7a01acSMauro Carvalho Chehab { 0x8d, 0x4f }, { 0x8e, 0 }, 345cb7a01acSMauro Carvalho Chehab { 0x8f, 0 }, { 0x90, 0 }, 346cb7a01acSMauro Carvalho Chehab { 0x91, 0 }, { 0x96, 0 }, 347cb7a01acSMauro Carvalho Chehab { 0x9a, 0 }, { 0xb0, 0x84 }, 348cb7a01acSMauro Carvalho Chehab { 0xb1, 0x0c }, { 0xb2, 0x0e }, 349cb7a01acSMauro Carvalho Chehab { 0xb3, 0x82 }, { 0xb8, 0x0a }, 350cb7a01acSMauro Carvalho Chehab 351cb7a01acSMauro Carvalho Chehab /* More reserved magic, some of which tweaks white balance */ 352cb7a01acSMauro Carvalho Chehab { 0x43, 0x0a }, { 0x44, 0xf0 }, 353cb7a01acSMauro Carvalho Chehab { 0x45, 0x34 }, { 0x46, 0x58 }, 354cb7a01acSMauro Carvalho Chehab { 0x47, 0x28 }, { 0x48, 0x3a }, 355cb7a01acSMauro Carvalho Chehab { 0x59, 0x88 }, { 0x5a, 0x88 }, 356cb7a01acSMauro Carvalho Chehab { 0x5b, 0x44 }, { 0x5c, 0x67 }, 357cb7a01acSMauro Carvalho Chehab { 0x5d, 0x49 }, { 0x5e, 0x0e }, 358cb7a01acSMauro Carvalho Chehab { 0x6c, 0x0a }, { 0x6d, 0x55 }, 359cb7a01acSMauro Carvalho Chehab { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ 360cb7a01acSMauro Carvalho Chehab { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, 361cb7a01acSMauro Carvalho Chehab { REG_RED, 0x60 }, 362cb7a01acSMauro Carvalho Chehab { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, 363cb7a01acSMauro Carvalho Chehab 364cb7a01acSMauro Carvalho Chehab /* Matrix coefficients */ 365cb7a01acSMauro Carvalho Chehab { 0x4f, 0x80 }, { 0x50, 0x80 }, 366cb7a01acSMauro Carvalho Chehab { 0x51, 0 }, { 0x52, 0x22 }, 367cb7a01acSMauro Carvalho Chehab { 0x53, 0x5e }, { 0x54, 0x80 }, 368cb7a01acSMauro Carvalho Chehab { 0x58, 0x9e }, 369cb7a01acSMauro Carvalho Chehab 370cb7a01acSMauro Carvalho Chehab { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, 371cb7a01acSMauro Carvalho Chehab { 0x75, 0x05 }, { 0x76, 0xe1 }, 372cb7a01acSMauro Carvalho Chehab { 0x4c, 0 }, { 0x77, 0x01 }, 373cb7a01acSMauro Carvalho Chehab { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, 374cb7a01acSMauro Carvalho Chehab { 0xc9, 0x60 }, { REG_COM16, 0x38 }, 375cb7a01acSMauro Carvalho Chehab { 0x56, 0x40 }, 376cb7a01acSMauro Carvalho Chehab 377cb7a01acSMauro Carvalho Chehab { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, 378cb7a01acSMauro Carvalho Chehab { 0xa4, 0x88 }, { 0x96, 0 }, 379cb7a01acSMauro Carvalho Chehab { 0x97, 0x30 }, { 0x98, 0x20 }, 380cb7a01acSMauro Carvalho Chehab { 0x99, 0x30 }, { 0x9a, 0x84 }, 381cb7a01acSMauro Carvalho Chehab { 0x9b, 0x29 }, { 0x9c, 0x03 }, 382cb7a01acSMauro Carvalho Chehab { 0x9d, 0x4c }, { 0x9e, 0x3f }, 383cb7a01acSMauro Carvalho Chehab { 0x78, 0x04 }, 384cb7a01acSMauro Carvalho Chehab 385cb7a01acSMauro Carvalho Chehab /* Extra-weird stuff. Some sort of multiplexor register */ 386cb7a01acSMauro Carvalho Chehab { 0x79, 0x01 }, { 0xc8, 0xf0 }, 387cb7a01acSMauro Carvalho Chehab { 0x79, 0x0f }, { 0xc8, 0x00 }, 388cb7a01acSMauro Carvalho Chehab { 0x79, 0x10 }, { 0xc8, 0x7e }, 389cb7a01acSMauro Carvalho Chehab { 0x79, 0x0a }, { 0xc8, 0x80 }, 390cb7a01acSMauro Carvalho Chehab { 0x79, 0x0b }, { 0xc8, 0x01 }, 391cb7a01acSMauro Carvalho Chehab { 0x79, 0x0c }, { 0xc8, 0x0f }, 392cb7a01acSMauro Carvalho Chehab { 0x79, 0x0d }, { 0xc8, 0x20 }, 393cb7a01acSMauro Carvalho Chehab { 0x79, 0x09 }, { 0xc8, 0x80 }, 394cb7a01acSMauro Carvalho Chehab { 0x79, 0x02 }, { 0xc8, 0xc0 }, 395cb7a01acSMauro Carvalho Chehab { 0x79, 0x03 }, { 0xc8, 0x40 }, 396cb7a01acSMauro Carvalho Chehab { 0x79, 0x05 }, { 0xc8, 0x30 }, 397cb7a01acSMauro Carvalho Chehab { 0x79, 0x26 }, 398cb7a01acSMauro Carvalho Chehab 399cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, /* END MARKER */ 400cb7a01acSMauro Carvalho Chehab }; 401cb7a01acSMauro Carvalho Chehab 402cb7a01acSMauro Carvalho Chehab 403cb7a01acSMauro Carvalho Chehab /* 404cb7a01acSMauro Carvalho Chehab * Here we'll try to encapsulate the changes for just the output 405cb7a01acSMauro Carvalho Chehab * video format. 406cb7a01acSMauro Carvalho Chehab * 407cb7a01acSMauro Carvalho Chehab * RGB656 and YUV422 come from OV; RGB444 is homebrewed. 408cb7a01acSMauro Carvalho Chehab * 409cb7a01acSMauro Carvalho Chehab * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. 410cb7a01acSMauro Carvalho Chehab */ 411cb7a01acSMauro Carvalho Chehab 412cb7a01acSMauro Carvalho Chehab 413cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_yuv422[] = { 414cb7a01acSMauro Carvalho Chehab { REG_COM7, 0x0 }, /* Selects YUV mode */ 415cb7a01acSMauro Carvalho Chehab { REG_RGB444, 0 }, /* No RGB444 please */ 416cb7a01acSMauro Carvalho Chehab { REG_COM1, 0 }, /* CCIR601 */ 417cb7a01acSMauro Carvalho Chehab { REG_COM15, COM15_R00FF }, 418c01b7429SJavier Martin { REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ 419cb7a01acSMauro Carvalho Chehab { 0x4f, 0x80 }, /* "matrix coefficient 1" */ 420cb7a01acSMauro Carvalho Chehab { 0x50, 0x80 }, /* "matrix coefficient 2" */ 421cb7a01acSMauro Carvalho Chehab { 0x51, 0 }, /* vb */ 422cb7a01acSMauro Carvalho Chehab { 0x52, 0x22 }, /* "matrix coefficient 4" */ 423cb7a01acSMauro Carvalho Chehab { 0x53, 0x5e }, /* "matrix coefficient 5" */ 424cb7a01acSMauro Carvalho Chehab { 0x54, 0x80 }, /* "matrix coefficient 6" */ 425cb7a01acSMauro Carvalho Chehab { REG_COM13, COM13_GAMMA|COM13_UVSAT }, 426cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 427cb7a01acSMauro Carvalho Chehab }; 428cb7a01acSMauro Carvalho Chehab 429cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb565[] = { 430cb7a01acSMauro Carvalho Chehab { REG_COM7, COM7_RGB }, /* Selects RGB mode */ 431cb7a01acSMauro Carvalho Chehab { REG_RGB444, 0 }, /* No RGB444 please */ 432cb7a01acSMauro Carvalho Chehab { REG_COM1, 0x0 }, /* CCIR601 */ 433cb7a01acSMauro Carvalho Chehab { REG_COM15, COM15_RGB565 }, 434cb7a01acSMauro Carvalho Chehab { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ 435cb7a01acSMauro Carvalho Chehab { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ 436cb7a01acSMauro Carvalho Chehab { 0x50, 0xb3 }, /* "matrix coefficient 2" */ 437cb7a01acSMauro Carvalho Chehab { 0x51, 0 }, /* vb */ 438cb7a01acSMauro Carvalho Chehab { 0x52, 0x3d }, /* "matrix coefficient 4" */ 439cb7a01acSMauro Carvalho Chehab { 0x53, 0xa7 }, /* "matrix coefficient 5" */ 440cb7a01acSMauro Carvalho Chehab { 0x54, 0xe4 }, /* "matrix coefficient 6" */ 441cb7a01acSMauro Carvalho Chehab { REG_COM13, COM13_GAMMA|COM13_UVSAT }, 442cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 443cb7a01acSMauro Carvalho Chehab }; 444cb7a01acSMauro Carvalho Chehab 445cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb444[] = { 446cb7a01acSMauro Carvalho Chehab { REG_COM7, COM7_RGB }, /* Selects RGB mode */ 447cb7a01acSMauro Carvalho Chehab { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ 448cb7a01acSMauro Carvalho Chehab { REG_COM1, 0x0 }, /* CCIR601 */ 449cb7a01acSMauro Carvalho Chehab { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ 450cb7a01acSMauro Carvalho Chehab { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ 451cb7a01acSMauro Carvalho Chehab { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ 452cb7a01acSMauro Carvalho Chehab { 0x50, 0xb3 }, /* "matrix coefficient 2" */ 453cb7a01acSMauro Carvalho Chehab { 0x51, 0 }, /* vb */ 454cb7a01acSMauro Carvalho Chehab { 0x52, 0x3d }, /* "matrix coefficient 4" */ 455cb7a01acSMauro Carvalho Chehab { 0x53, 0xa7 }, /* "matrix coefficient 5" */ 456cb7a01acSMauro Carvalho Chehab { 0x54, 0xe4 }, /* "matrix coefficient 6" */ 457cb7a01acSMauro Carvalho Chehab { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ 458cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 459cb7a01acSMauro Carvalho Chehab }; 460cb7a01acSMauro Carvalho Chehab 461cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_raw[] = { 462cb7a01acSMauro Carvalho Chehab { REG_COM7, COM7_BAYER }, 463cb7a01acSMauro Carvalho Chehab { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ 464cb7a01acSMauro Carvalho Chehab { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ 465cb7a01acSMauro Carvalho Chehab { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ 466cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 467cb7a01acSMauro Carvalho Chehab }; 468cb7a01acSMauro Carvalho Chehab 469cb7a01acSMauro Carvalho Chehab 470cb7a01acSMauro Carvalho Chehab 471cb7a01acSMauro Carvalho Chehab /* 472cb7a01acSMauro Carvalho Chehab * Low-level register I/O. 473cb7a01acSMauro Carvalho Chehab * 474cb7a01acSMauro Carvalho Chehab * Note that there are two versions of these. On the XO 1, the 475cb7a01acSMauro Carvalho Chehab * i2c controller only does SMBUS, so that's what we use. The 476cb7a01acSMauro Carvalho Chehab * ov7670 is not really an SMBUS device, though, so the communication 477cb7a01acSMauro Carvalho Chehab * is not always entirely reliable. 478cb7a01acSMauro Carvalho Chehab */ 479cb7a01acSMauro Carvalho Chehab static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, 480cb7a01acSMauro Carvalho Chehab unsigned char *value) 481cb7a01acSMauro Carvalho Chehab { 482cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 483cb7a01acSMauro Carvalho Chehab int ret; 484cb7a01acSMauro Carvalho Chehab 485cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_read_byte_data(client, reg); 486cb7a01acSMauro Carvalho Chehab if (ret >= 0) { 487cb7a01acSMauro Carvalho Chehab *value = (unsigned char)ret; 488cb7a01acSMauro Carvalho Chehab ret = 0; 489cb7a01acSMauro Carvalho Chehab } 490cb7a01acSMauro Carvalho Chehab return ret; 491cb7a01acSMauro Carvalho Chehab } 492cb7a01acSMauro Carvalho Chehab 493cb7a01acSMauro Carvalho Chehab 494cb7a01acSMauro Carvalho Chehab static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, 495cb7a01acSMauro Carvalho Chehab unsigned char value) 496cb7a01acSMauro Carvalho Chehab { 497cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 498cb7a01acSMauro Carvalho Chehab int ret = i2c_smbus_write_byte_data(client, reg, value); 499cb7a01acSMauro Carvalho Chehab 500cb7a01acSMauro Carvalho Chehab if (reg == REG_COM7 && (value & COM7_RESET)) 501cb7a01acSMauro Carvalho Chehab msleep(5); /* Wait for reset to run */ 502cb7a01acSMauro Carvalho Chehab return ret; 503cb7a01acSMauro Carvalho Chehab } 504cb7a01acSMauro Carvalho Chehab 505cb7a01acSMauro Carvalho Chehab /* 506cb7a01acSMauro Carvalho Chehab * On most platforms, we'd rather do straight i2c I/O. 507cb7a01acSMauro Carvalho Chehab */ 508cb7a01acSMauro Carvalho Chehab static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, 509cb7a01acSMauro Carvalho Chehab unsigned char *value) 510cb7a01acSMauro Carvalho Chehab { 511cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 512cb7a01acSMauro Carvalho Chehab u8 data = reg; 513cb7a01acSMauro Carvalho Chehab struct i2c_msg msg; 514cb7a01acSMauro Carvalho Chehab int ret; 515cb7a01acSMauro Carvalho Chehab 516cb7a01acSMauro Carvalho Chehab /* 517cb7a01acSMauro Carvalho Chehab * Send out the register address... 518cb7a01acSMauro Carvalho Chehab */ 519cb7a01acSMauro Carvalho Chehab msg.addr = client->addr; 520cb7a01acSMauro Carvalho Chehab msg.flags = 0; 521cb7a01acSMauro Carvalho Chehab msg.len = 1; 522cb7a01acSMauro Carvalho Chehab msg.buf = &data; 523cb7a01acSMauro Carvalho Chehab ret = i2c_transfer(client->adapter, &msg, 1); 524cb7a01acSMauro Carvalho Chehab if (ret < 0) { 525cb7a01acSMauro Carvalho Chehab printk(KERN_ERR "Error %d on register write\n", ret); 526cb7a01acSMauro Carvalho Chehab return ret; 527cb7a01acSMauro Carvalho Chehab } 528cb7a01acSMauro Carvalho Chehab /* 529cb7a01acSMauro Carvalho Chehab * ...then read back the result. 530cb7a01acSMauro Carvalho Chehab */ 531cb7a01acSMauro Carvalho Chehab msg.flags = I2C_M_RD; 532cb7a01acSMauro Carvalho Chehab ret = i2c_transfer(client->adapter, &msg, 1); 533cb7a01acSMauro Carvalho Chehab if (ret >= 0) { 534cb7a01acSMauro Carvalho Chehab *value = data; 535cb7a01acSMauro Carvalho Chehab ret = 0; 536cb7a01acSMauro Carvalho Chehab } 537cb7a01acSMauro Carvalho Chehab return ret; 538cb7a01acSMauro Carvalho Chehab } 539cb7a01acSMauro Carvalho Chehab 540cb7a01acSMauro Carvalho Chehab 541cb7a01acSMauro Carvalho Chehab static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, 542cb7a01acSMauro Carvalho Chehab unsigned char value) 543cb7a01acSMauro Carvalho Chehab { 544cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 545cb7a01acSMauro Carvalho Chehab struct i2c_msg msg; 546cb7a01acSMauro Carvalho Chehab unsigned char data[2] = { reg, value }; 547cb7a01acSMauro Carvalho Chehab int ret; 548cb7a01acSMauro Carvalho Chehab 549cb7a01acSMauro Carvalho Chehab msg.addr = client->addr; 550cb7a01acSMauro Carvalho Chehab msg.flags = 0; 551cb7a01acSMauro Carvalho Chehab msg.len = 2; 552cb7a01acSMauro Carvalho Chehab msg.buf = data; 553cb7a01acSMauro Carvalho Chehab ret = i2c_transfer(client->adapter, &msg, 1); 554cb7a01acSMauro Carvalho Chehab if (ret > 0) 555cb7a01acSMauro Carvalho Chehab ret = 0; 556cb7a01acSMauro Carvalho Chehab if (reg == REG_COM7 && (value & COM7_RESET)) 557cb7a01acSMauro Carvalho Chehab msleep(5); /* Wait for reset to run */ 558cb7a01acSMauro Carvalho Chehab return ret; 559cb7a01acSMauro Carvalho Chehab } 560cb7a01acSMauro Carvalho Chehab 561cb7a01acSMauro Carvalho Chehab static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, 562cb7a01acSMauro Carvalho Chehab unsigned char *value) 563cb7a01acSMauro Carvalho Chehab { 564cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 565cb7a01acSMauro Carvalho Chehab if (info->use_smbus) 566cb7a01acSMauro Carvalho Chehab return ov7670_read_smbus(sd, reg, value); 567cb7a01acSMauro Carvalho Chehab else 568cb7a01acSMauro Carvalho Chehab return ov7670_read_i2c(sd, reg, value); 569cb7a01acSMauro Carvalho Chehab } 570cb7a01acSMauro Carvalho Chehab 571cb7a01acSMauro Carvalho Chehab static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, 572cb7a01acSMauro Carvalho Chehab unsigned char value) 573cb7a01acSMauro Carvalho Chehab { 574cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 575cb7a01acSMauro Carvalho Chehab if (info->use_smbus) 576cb7a01acSMauro Carvalho Chehab return ov7670_write_smbus(sd, reg, value); 577cb7a01acSMauro Carvalho Chehab else 578cb7a01acSMauro Carvalho Chehab return ov7670_write_i2c(sd, reg, value); 579cb7a01acSMauro Carvalho Chehab } 580cb7a01acSMauro Carvalho Chehab 581b48d908dSAkinobu Mita static int ov7670_update_bits(struct v4l2_subdev *sd, unsigned char reg, 582b48d908dSAkinobu Mita unsigned char mask, unsigned char value) 583b48d908dSAkinobu Mita { 584b48d908dSAkinobu Mita unsigned char orig; 585b48d908dSAkinobu Mita int ret; 586b48d908dSAkinobu Mita 587b48d908dSAkinobu Mita ret = ov7670_read(sd, reg, &orig); 588b48d908dSAkinobu Mita if (ret) 589b48d908dSAkinobu Mita return ret; 590b48d908dSAkinobu Mita 591b48d908dSAkinobu Mita return ov7670_write(sd, reg, (orig & ~mask) | (value & mask)); 592b48d908dSAkinobu Mita } 593b48d908dSAkinobu Mita 594cb7a01acSMauro Carvalho Chehab /* 595cb7a01acSMauro Carvalho Chehab * Write a list of register settings; ff/ff stops the process. 596cb7a01acSMauro Carvalho Chehab */ 597cb7a01acSMauro Carvalho Chehab static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) 598cb7a01acSMauro Carvalho Chehab { 599cb7a01acSMauro Carvalho Chehab while (vals->reg_num != 0xff || vals->value != 0xff) { 600cb7a01acSMauro Carvalho Chehab int ret = ov7670_write(sd, vals->reg_num, vals->value); 601cb7a01acSMauro Carvalho Chehab if (ret < 0) 602cb7a01acSMauro Carvalho Chehab return ret; 603cb7a01acSMauro Carvalho Chehab vals++; 604cb7a01acSMauro Carvalho Chehab } 605cb7a01acSMauro Carvalho Chehab return 0; 606cb7a01acSMauro Carvalho Chehab } 607cb7a01acSMauro Carvalho Chehab 608cb7a01acSMauro Carvalho Chehab 609cb7a01acSMauro Carvalho Chehab /* 610cb7a01acSMauro Carvalho Chehab * Stuff that knows about the sensor. 611cb7a01acSMauro Carvalho Chehab */ 612cb7a01acSMauro Carvalho Chehab static int ov7670_reset(struct v4l2_subdev *sd, u32 val) 613cb7a01acSMauro Carvalho Chehab { 614cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_COM7, COM7_RESET); 615cb7a01acSMauro Carvalho Chehab msleep(1); 616cb7a01acSMauro Carvalho Chehab return 0; 617cb7a01acSMauro Carvalho Chehab } 618cb7a01acSMauro Carvalho Chehab 619cb7a01acSMauro Carvalho Chehab 620cb7a01acSMauro Carvalho Chehab static int ov7670_init(struct v4l2_subdev *sd, u32 val) 621cb7a01acSMauro Carvalho Chehab { 622cb7a01acSMauro Carvalho Chehab return ov7670_write_array(sd, ov7670_default_regs); 623cb7a01acSMauro Carvalho Chehab } 624cb7a01acSMauro Carvalho Chehab 625cb7a01acSMauro Carvalho Chehab static int ov7670_detect(struct v4l2_subdev *sd) 626cb7a01acSMauro Carvalho Chehab { 627cb7a01acSMauro Carvalho Chehab unsigned char v; 628cb7a01acSMauro Carvalho Chehab int ret; 629cb7a01acSMauro Carvalho Chehab 630cb7a01acSMauro Carvalho Chehab ret = ov7670_init(sd, 0); 631cb7a01acSMauro Carvalho Chehab if (ret < 0) 632cb7a01acSMauro Carvalho Chehab return ret; 633cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_MIDH, &v); 634cb7a01acSMauro Carvalho Chehab if (ret < 0) 635cb7a01acSMauro Carvalho Chehab return ret; 636cb7a01acSMauro Carvalho Chehab if (v != 0x7f) /* OV manuf. id. */ 637cb7a01acSMauro Carvalho Chehab return -ENODEV; 638cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_MIDL, &v); 639cb7a01acSMauro Carvalho Chehab if (ret < 0) 640cb7a01acSMauro Carvalho Chehab return ret; 641cb7a01acSMauro Carvalho Chehab if (v != 0xa2) 642cb7a01acSMauro Carvalho Chehab return -ENODEV; 643cb7a01acSMauro Carvalho Chehab /* 644cb7a01acSMauro Carvalho Chehab * OK, we know we have an OmniVision chip...but which one? 645cb7a01acSMauro Carvalho Chehab */ 646cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_PID, &v); 647cb7a01acSMauro Carvalho Chehab if (ret < 0) 648cb7a01acSMauro Carvalho Chehab return ret; 649cb7a01acSMauro Carvalho Chehab if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ 650cb7a01acSMauro Carvalho Chehab return -ENODEV; 651cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_VER, &v); 652cb7a01acSMauro Carvalho Chehab if (ret < 0) 653cb7a01acSMauro Carvalho Chehab return ret; 654cb7a01acSMauro Carvalho Chehab if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ 655cb7a01acSMauro Carvalho Chehab return -ENODEV; 656cb7a01acSMauro Carvalho Chehab return 0; 657cb7a01acSMauro Carvalho Chehab } 658cb7a01acSMauro Carvalho Chehab 659cb7a01acSMauro Carvalho Chehab 660cb7a01acSMauro Carvalho Chehab /* 661cb7a01acSMauro Carvalho Chehab * Store information about the video data format. The color matrix 662cb7a01acSMauro Carvalho Chehab * is deeply tied into the format, so keep the relevant values here. 663cb7a01acSMauro Carvalho Chehab * The magic matrix numbers come from OmniVision. 664cb7a01acSMauro Carvalho Chehab */ 665cb7a01acSMauro Carvalho Chehab static struct ov7670_format_struct { 666f5fe58fdSBoris BREZILLON u32 mbus_code; 667cb7a01acSMauro Carvalho Chehab enum v4l2_colorspace colorspace; 668cb7a01acSMauro Carvalho Chehab struct regval_list *regs; 669cb7a01acSMauro Carvalho Chehab int cmatrix[CMATRIX_LEN]; 670cb7a01acSMauro Carvalho Chehab } ov7670_formats[] = { 671cb7a01acSMauro Carvalho Chehab { 672f5fe58fdSBoris BREZILLON .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 67375eb9847SHans Verkuil .colorspace = V4L2_COLORSPACE_SRGB, 674cb7a01acSMauro Carvalho Chehab .regs = ov7670_fmt_yuv422, 675cb7a01acSMauro Carvalho Chehab .cmatrix = { 128, -128, 0, -34, -94, 128 }, 676cb7a01acSMauro Carvalho Chehab }, 677cb7a01acSMauro Carvalho Chehab { 678f5fe58fdSBoris BREZILLON .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, 679cb7a01acSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 680cb7a01acSMauro Carvalho Chehab .regs = ov7670_fmt_rgb444, 681cb7a01acSMauro Carvalho Chehab .cmatrix = { 179, -179, 0, -61, -176, 228 }, 682cb7a01acSMauro Carvalho Chehab }, 683cb7a01acSMauro Carvalho Chehab { 684f5fe58fdSBoris BREZILLON .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, 685cb7a01acSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 686cb7a01acSMauro Carvalho Chehab .regs = ov7670_fmt_rgb565, 687cb7a01acSMauro Carvalho Chehab .cmatrix = { 179, -179, 0, -61, -176, 228 }, 688cb7a01acSMauro Carvalho Chehab }, 689cb7a01acSMauro Carvalho Chehab { 690f5fe58fdSBoris BREZILLON .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, 691cb7a01acSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 692cb7a01acSMauro Carvalho Chehab .regs = ov7670_fmt_raw, 693cb7a01acSMauro Carvalho Chehab .cmatrix = { 0, 0, 0, 0, 0, 0 }, 694cb7a01acSMauro Carvalho Chehab }, 695cb7a01acSMauro Carvalho Chehab }; 696cb7a01acSMauro Carvalho Chehab #define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) 697cb7a01acSMauro Carvalho Chehab 698cb7a01acSMauro Carvalho Chehab 699cb7a01acSMauro Carvalho Chehab /* 700cb7a01acSMauro Carvalho Chehab * Then there is the issue of window sizes. Try to capture the info here. 701cb7a01acSMauro Carvalho Chehab */ 702cb7a01acSMauro Carvalho Chehab 703cb7a01acSMauro Carvalho Chehab /* 704cb7a01acSMauro Carvalho Chehab * QCIF mode is done (by OV) in a very strange way - it actually looks like 705cb7a01acSMauro Carvalho Chehab * VGA with weird scaling options - they do *not* use the canned QCIF mode 706cb7a01acSMauro Carvalho Chehab * which is allegedly provided by the sensor. So here's the weird register 707cb7a01acSMauro Carvalho Chehab * settings. 708cb7a01acSMauro Carvalho Chehab */ 709cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_qcif_regs[] = { 710cb7a01acSMauro Carvalho Chehab { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, 711cb7a01acSMauro Carvalho Chehab { REG_COM3, COM3_DCWEN }, 712cb7a01acSMauro Carvalho Chehab { REG_COM14, COM14_DCWEN | 0x01}, 713cb7a01acSMauro Carvalho Chehab { 0x73, 0xf1 }, 714cb7a01acSMauro Carvalho Chehab { 0xa2, 0x52 }, 715cb7a01acSMauro Carvalho Chehab { 0x7b, 0x1c }, 716cb7a01acSMauro Carvalho Chehab { 0x7c, 0x28 }, 717cb7a01acSMauro Carvalho Chehab { 0x7d, 0x3c }, 718cb7a01acSMauro Carvalho Chehab { 0x7f, 0x69 }, 719cb7a01acSMauro Carvalho Chehab { REG_COM9, 0x38 }, 720cb7a01acSMauro Carvalho Chehab { 0xa1, 0x0b }, 721cb7a01acSMauro Carvalho Chehab { 0x74, 0x19 }, 722cb7a01acSMauro Carvalho Chehab { 0x9a, 0x80 }, 723cb7a01acSMauro Carvalho Chehab { 0x43, 0x14 }, 724cb7a01acSMauro Carvalho Chehab { REG_COM13, 0xc0 }, 725cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 726cb7a01acSMauro Carvalho Chehab }; 727cb7a01acSMauro Carvalho Chehab 728d058e237SJavier Martin static struct ov7670_win_size ov7670_win_sizes[] = { 729cb7a01acSMauro Carvalho Chehab /* VGA */ 730cb7a01acSMauro Carvalho Chehab { 731cb7a01acSMauro Carvalho Chehab .width = VGA_WIDTH, 732cb7a01acSMauro Carvalho Chehab .height = VGA_HEIGHT, 733cb7a01acSMauro Carvalho Chehab .com7_bit = COM7_FMT_VGA, 734cb7a01acSMauro Carvalho Chehab .hstart = 158, /* These values from */ 735cb7a01acSMauro Carvalho Chehab .hstop = 14, /* Omnivision */ 736cb7a01acSMauro Carvalho Chehab .vstart = 10, 737cb7a01acSMauro Carvalho Chehab .vstop = 490, 738cb7a01acSMauro Carvalho Chehab .regs = NULL, 739cb7a01acSMauro Carvalho Chehab }, 740cb7a01acSMauro Carvalho Chehab /* CIF */ 741cb7a01acSMauro Carvalho Chehab { 742cb7a01acSMauro Carvalho Chehab .width = CIF_WIDTH, 743cb7a01acSMauro Carvalho Chehab .height = CIF_HEIGHT, 744cb7a01acSMauro Carvalho Chehab .com7_bit = COM7_FMT_CIF, 745cb7a01acSMauro Carvalho Chehab .hstart = 170, /* Empirically determined */ 746cb7a01acSMauro Carvalho Chehab .hstop = 90, 747cb7a01acSMauro Carvalho Chehab .vstart = 14, 748cb7a01acSMauro Carvalho Chehab .vstop = 494, 749cb7a01acSMauro Carvalho Chehab .regs = NULL, 750cb7a01acSMauro Carvalho Chehab }, 751cb7a01acSMauro Carvalho Chehab /* QVGA */ 752cb7a01acSMauro Carvalho Chehab { 753cb7a01acSMauro Carvalho Chehab .width = QVGA_WIDTH, 754cb7a01acSMauro Carvalho Chehab .height = QVGA_HEIGHT, 755cb7a01acSMauro Carvalho Chehab .com7_bit = COM7_FMT_QVGA, 756cb7a01acSMauro Carvalho Chehab .hstart = 168, /* Empirically determined */ 757cb7a01acSMauro Carvalho Chehab .hstop = 24, 758cb7a01acSMauro Carvalho Chehab .vstart = 12, 759cb7a01acSMauro Carvalho Chehab .vstop = 492, 760cb7a01acSMauro Carvalho Chehab .regs = NULL, 761cb7a01acSMauro Carvalho Chehab }, 762cb7a01acSMauro Carvalho Chehab /* QCIF */ 763cb7a01acSMauro Carvalho Chehab { 764cb7a01acSMauro Carvalho Chehab .width = QCIF_WIDTH, 765cb7a01acSMauro Carvalho Chehab .height = QCIF_HEIGHT, 766cb7a01acSMauro Carvalho Chehab .com7_bit = COM7_FMT_VGA, /* see comment above */ 767cb7a01acSMauro Carvalho Chehab .hstart = 456, /* Empirically determined */ 768cb7a01acSMauro Carvalho Chehab .hstop = 24, 769cb7a01acSMauro Carvalho Chehab .vstart = 14, 770cb7a01acSMauro Carvalho Chehab .vstop = 494, 771cb7a01acSMauro Carvalho Chehab .regs = ov7670_qcif_regs, 772d058e237SJavier Martin } 773cb7a01acSMauro Carvalho Chehab }; 774cb7a01acSMauro Carvalho Chehab 775d058e237SJavier Martin static struct ov7670_win_size ov7675_win_sizes[] = { 776d058e237SJavier Martin /* 777d058e237SJavier Martin * Currently, only VGA is supported. Theoretically it could be possible 778d058e237SJavier Martin * to support CIF, QVGA and QCIF too. Taking values for ov7670 as a 779d058e237SJavier Martin * base and tweak them empirically could be required. 780d058e237SJavier Martin */ 781d058e237SJavier Martin { 782d058e237SJavier Martin .width = VGA_WIDTH, 783d058e237SJavier Martin .height = VGA_HEIGHT, 784d058e237SJavier Martin .com7_bit = COM7_FMT_VGA, 785d058e237SJavier Martin .hstart = 158, /* These values from */ 786d058e237SJavier Martin .hstop = 14, /* Omnivision */ 787d058e237SJavier Martin .vstart = 14, /* Empirically determined */ 788d058e237SJavier Martin .vstop = 494, 789d058e237SJavier Martin .regs = NULL, 790d058e237SJavier Martin } 791d058e237SJavier Martin }; 792cb7a01acSMauro Carvalho Chehab 793f6dd927fSJavier Martin static void ov7675_get_framerate(struct v4l2_subdev *sd, 794f6dd927fSJavier Martin struct v4l2_fract *tpf) 795f6dd927fSJavier Martin { 796f6dd927fSJavier Martin struct ov7670_info *info = to_state(sd); 797f6dd927fSJavier Martin u32 clkrc = info->clkrc; 79804ee6d92SJavier Martin int pll_factor; 79904ee6d92SJavier Martin 80004ee6d92SJavier Martin if (info->pll_bypass) 80104ee6d92SJavier Martin pll_factor = 1; 80204ee6d92SJavier Martin else 80304ee6d92SJavier Martin pll_factor = PLL_FACTOR; 804f6dd927fSJavier Martin 805f6dd927fSJavier Martin clkrc++; 806f5fe58fdSBoris BREZILLON if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8) 807f6dd927fSJavier Martin clkrc = (clkrc >> 1); 808f6dd927fSJavier Martin 809f6dd927fSJavier Martin tpf->numerator = 1; 810f6dd927fSJavier Martin tpf->denominator = (5 * pll_factor * info->clock_speed) / 811f6dd927fSJavier Martin (4 * clkrc); 812f6dd927fSJavier Martin } 813f6dd927fSJavier Martin 814f6dd927fSJavier Martin static int ov7675_set_framerate(struct v4l2_subdev *sd, 815f6dd927fSJavier Martin struct v4l2_fract *tpf) 816f6dd927fSJavier Martin { 817f6dd927fSJavier Martin struct ov7670_info *info = to_state(sd); 818f6dd927fSJavier Martin u32 clkrc; 81904ee6d92SJavier Martin int pll_factor; 820f6dd927fSJavier Martin int ret; 821f6dd927fSJavier Martin 822f6dd927fSJavier Martin /* 823f6dd927fSJavier Martin * The formula is fps = 5/4*pixclk for YUV/RGB and 824f6dd927fSJavier Martin * fps = 5/2*pixclk for RAW. 825f6dd927fSJavier Martin * 826f6dd927fSJavier Martin * pixclk = clock_speed / (clkrc + 1) * PLLfactor 827f6dd927fSJavier Martin * 828f6dd927fSJavier Martin */ 82904ee6d92SJavier Martin if (info->pll_bypass) { 83004ee6d92SJavier Martin pll_factor = 1; 83104ee6d92SJavier Martin ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS); 83204ee6d92SJavier Martin } else { 83304ee6d92SJavier Martin pll_factor = PLL_FACTOR; 83404ee6d92SJavier Martin ret = ov7670_write(sd, REG_DBLV, DBLV_X4); 83504ee6d92SJavier Martin } 83604ee6d92SJavier Martin if (ret < 0) 83704ee6d92SJavier Martin return ret; 83804ee6d92SJavier Martin 839f6dd927fSJavier Martin if (tpf->numerator == 0 || tpf->denominator == 0) { 840f6dd927fSJavier Martin clkrc = 0; 841f6dd927fSJavier Martin } else { 842f6dd927fSJavier Martin clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / 843f6dd927fSJavier Martin (4 * tpf->denominator); 844f5fe58fdSBoris BREZILLON if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8) 845f6dd927fSJavier Martin clkrc = (clkrc << 1); 846f6dd927fSJavier Martin clkrc--; 847f6dd927fSJavier Martin } 848f6dd927fSJavier Martin 849f6dd927fSJavier Martin /* 850f6dd927fSJavier Martin * The datasheet claims that clkrc = 0 will divide the input clock by 1 851f6dd927fSJavier Martin * but we've checked with an oscilloscope that it divides by 2 instead. 852f6dd927fSJavier Martin * So, if clkrc = 0 just bypass the divider. 853f6dd927fSJavier Martin */ 854f6dd927fSJavier Martin if (clkrc <= 0) 855f6dd927fSJavier Martin clkrc = CLK_EXT; 856f6dd927fSJavier Martin else if (clkrc > CLK_SCALE) 857f6dd927fSJavier Martin clkrc = CLK_SCALE; 858f6dd927fSJavier Martin info->clkrc = clkrc; 859f6dd927fSJavier Martin 860f6dd927fSJavier Martin /* Recalculate frame rate */ 861f6dd927fSJavier Martin ov7675_get_framerate(sd, tpf); 862f6dd927fSJavier Martin 863f1fb0855SSakari Ailus return ov7670_write(sd, REG_CLKRC, info->clkrc); 864f6dd927fSJavier Martin } 865f6dd927fSJavier Martin 866f6dd927fSJavier Martin static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, 867f6dd927fSJavier Martin struct v4l2_fract *tpf) 868f6dd927fSJavier Martin { 869f6dd927fSJavier Martin struct ov7670_info *info = to_state(sd); 870f6dd927fSJavier Martin 871f6dd927fSJavier Martin tpf->numerator = 1; 872f6dd927fSJavier Martin tpf->denominator = info->clock_speed; 873f6dd927fSJavier Martin if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) 874f6dd927fSJavier Martin tpf->denominator /= (info->clkrc & CLK_SCALE); 875f6dd927fSJavier Martin } 876f6dd927fSJavier Martin 877f6dd927fSJavier Martin static int ov7670_set_framerate_legacy(struct v4l2_subdev *sd, 878f6dd927fSJavier Martin struct v4l2_fract *tpf) 879f6dd927fSJavier Martin { 880f6dd927fSJavier Martin struct ov7670_info *info = to_state(sd); 881f6dd927fSJavier Martin int div; 882f6dd927fSJavier Martin 883f6dd927fSJavier Martin if (tpf->numerator == 0 || tpf->denominator == 0) 884f6dd927fSJavier Martin div = 1; /* Reset to full rate */ 885f6dd927fSJavier Martin else 886f6dd927fSJavier Martin div = (tpf->numerator * info->clock_speed) / tpf->denominator; 887f6dd927fSJavier Martin if (div == 0) 888f6dd927fSJavier Martin div = 1; 889f6dd927fSJavier Martin else if (div > CLK_SCALE) 890f6dd927fSJavier Martin div = CLK_SCALE; 891f6dd927fSJavier Martin info->clkrc = (info->clkrc & 0x80) | div; 892f6dd927fSJavier Martin tpf->numerator = 1; 893f6dd927fSJavier Martin tpf->denominator = info->clock_speed / div; 894f6dd927fSJavier Martin return ov7670_write(sd, REG_CLKRC, info->clkrc); 895f6dd927fSJavier Martin } 896f6dd927fSJavier Martin 897cb7a01acSMauro Carvalho Chehab /* 898cb7a01acSMauro Carvalho Chehab * Store a set of start/stop values into the camera. 899cb7a01acSMauro Carvalho Chehab */ 900cb7a01acSMauro Carvalho Chehab static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, 901cb7a01acSMauro Carvalho Chehab int vstart, int vstop) 902cb7a01acSMauro Carvalho Chehab { 903cb7a01acSMauro Carvalho Chehab int ret; 904cb7a01acSMauro Carvalho Chehab unsigned char v; 905cb7a01acSMauro Carvalho Chehab /* 906cb7a01acSMauro Carvalho Chehab * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of 907cb7a01acSMauro Carvalho Chehab * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is 908cb7a01acSMauro Carvalho Chehab * a mystery "edge offset" value in the top two bits of href. 909cb7a01acSMauro Carvalho Chehab */ 910cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); 911cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); 912cb7a01acSMauro Carvalho Chehab ret += ov7670_read(sd, REG_HREF, &v); 913cb7a01acSMauro Carvalho Chehab v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); 914cb7a01acSMauro Carvalho Chehab msleep(10); 915cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_HREF, v); 916cb7a01acSMauro Carvalho Chehab /* 917cb7a01acSMauro Carvalho Chehab * Vertical: similar arrangement, but only 10 bits. 918cb7a01acSMauro Carvalho Chehab */ 919cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); 920cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); 921cb7a01acSMauro Carvalho Chehab ret += ov7670_read(sd, REG_VREF, &v); 922cb7a01acSMauro Carvalho Chehab v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); 923cb7a01acSMauro Carvalho Chehab msleep(10); 924cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_VREF, v); 925cb7a01acSMauro Carvalho Chehab return ret; 926cb7a01acSMauro Carvalho Chehab } 927cb7a01acSMauro Carvalho Chehab 928cb7a01acSMauro Carvalho Chehab 929ebcff5fcSHans Verkuil static int ov7670_enum_mbus_code(struct v4l2_subdev *sd, 930ebcff5fcSHans Verkuil struct v4l2_subdev_pad_config *cfg, 931ebcff5fcSHans Verkuil struct v4l2_subdev_mbus_code_enum *code) 932cb7a01acSMauro Carvalho Chehab { 933ebcff5fcSHans Verkuil if (code->pad || code->index >= N_OV7670_FMTS) 934cb7a01acSMauro Carvalho Chehab return -EINVAL; 935cb7a01acSMauro Carvalho Chehab 936ebcff5fcSHans Verkuil code->code = ov7670_formats[code->index].mbus_code; 937cb7a01acSMauro Carvalho Chehab return 0; 938cb7a01acSMauro Carvalho Chehab } 939cb7a01acSMauro Carvalho Chehab 940cb7a01acSMauro Carvalho Chehab static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, 941cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *fmt, 942cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct **ret_fmt, 943cb7a01acSMauro Carvalho Chehab struct ov7670_win_size **ret_wsize) 944cb7a01acSMauro Carvalho Chehab { 945f748cd3eSJavier Martin int index, i; 946cb7a01acSMauro Carvalho Chehab struct ov7670_win_size *wsize; 947d058e237SJavier Martin struct ov7670_info *info = to_state(sd); 948d058e237SJavier Martin unsigned int n_win_sizes = info->devtype->n_win_sizes; 949f748cd3eSJavier Martin unsigned int win_sizes_limit = n_win_sizes; 950cb7a01acSMauro Carvalho Chehab 951cb7a01acSMauro Carvalho Chehab for (index = 0; index < N_OV7670_FMTS; index++) 952cb7a01acSMauro Carvalho Chehab if (ov7670_formats[index].mbus_code == fmt->code) 953cb7a01acSMauro Carvalho Chehab break; 954cb7a01acSMauro Carvalho Chehab if (index >= N_OV7670_FMTS) { 955cb7a01acSMauro Carvalho Chehab /* default to first format */ 956cb7a01acSMauro Carvalho Chehab index = 0; 957cb7a01acSMauro Carvalho Chehab fmt->code = ov7670_formats[0].mbus_code; 958cb7a01acSMauro Carvalho Chehab } 959cb7a01acSMauro Carvalho Chehab if (ret_fmt != NULL) 960cb7a01acSMauro Carvalho Chehab *ret_fmt = ov7670_formats + index; 961cb7a01acSMauro Carvalho Chehab /* 962cb7a01acSMauro Carvalho Chehab * Fields: the OV devices claim to be progressive. 963cb7a01acSMauro Carvalho Chehab */ 964cb7a01acSMauro Carvalho Chehab fmt->field = V4L2_FIELD_NONE; 965f748cd3eSJavier Martin 966f748cd3eSJavier Martin /* 967f748cd3eSJavier Martin * Don't consider values that don't match min_height and min_width 968f748cd3eSJavier Martin * constraints. 969f748cd3eSJavier Martin */ 970f748cd3eSJavier Martin if (info->min_width || info->min_height) 971f748cd3eSJavier Martin for (i = 0; i < n_win_sizes; i++) { 972f748cd3eSJavier Martin wsize = info->devtype->win_sizes + i; 973f748cd3eSJavier Martin 974f748cd3eSJavier Martin if (wsize->width < info->min_width || 975f748cd3eSJavier Martin wsize->height < info->min_height) { 976f748cd3eSJavier Martin win_sizes_limit = i; 977f748cd3eSJavier Martin break; 978f748cd3eSJavier Martin } 979f748cd3eSJavier Martin } 980cb7a01acSMauro Carvalho Chehab /* 981cb7a01acSMauro Carvalho Chehab * Round requested image size down to the nearest 982cb7a01acSMauro Carvalho Chehab * we support, but not below the smallest. 983cb7a01acSMauro Carvalho Chehab */ 984d058e237SJavier Martin for (wsize = info->devtype->win_sizes; 985f748cd3eSJavier Martin wsize < info->devtype->win_sizes + win_sizes_limit; wsize++) 986cb7a01acSMauro Carvalho Chehab if (fmt->width >= wsize->width && fmt->height >= wsize->height) 987cb7a01acSMauro Carvalho Chehab break; 988f748cd3eSJavier Martin if (wsize >= info->devtype->win_sizes + win_sizes_limit) 989cb7a01acSMauro Carvalho Chehab wsize--; /* Take the smallest one */ 990cb7a01acSMauro Carvalho Chehab if (ret_wsize != NULL) 991cb7a01acSMauro Carvalho Chehab *ret_wsize = wsize; 992cb7a01acSMauro Carvalho Chehab /* 993cb7a01acSMauro Carvalho Chehab * Note the size we'll actually handle. 994cb7a01acSMauro Carvalho Chehab */ 995cb7a01acSMauro Carvalho Chehab fmt->width = wsize->width; 996cb7a01acSMauro Carvalho Chehab fmt->height = wsize->height; 997cb7a01acSMauro Carvalho Chehab fmt->colorspace = ov7670_formats[index].colorspace; 998c0662dd4SWenyou Yang 999c0662dd4SWenyou Yang info->format = *fmt; 1000c0662dd4SWenyou Yang 1001cb7a01acSMauro Carvalho Chehab return 0; 1002cb7a01acSMauro Carvalho Chehab } 1003cb7a01acSMauro Carvalho Chehab 10045556ab2aSLubomir Rintel static int ov7670_apply_fmt(struct v4l2_subdev *sd) 1005cb7a01acSMauro Carvalho Chehab { 1006cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 10075556ab2aSLubomir Rintel struct ov7670_win_size *wsize = info->wsize; 100801b84448SJacopo Mondi unsigned char com7, com10 = 0; 1009cb7a01acSMauro Carvalho Chehab int ret; 1010cb7a01acSMauro Carvalho Chehab 1011cb7a01acSMauro Carvalho Chehab /* 1012cb7a01acSMauro Carvalho Chehab * COM7 is a pain in the ass, it doesn't like to be read then 1013cb7a01acSMauro Carvalho Chehab * quickly written afterward. But we have everything we need 1014cb7a01acSMauro Carvalho Chehab * to set it absolutely here, as long as the format-specific 1015cb7a01acSMauro Carvalho Chehab * register sets list it first. 1016cb7a01acSMauro Carvalho Chehab */ 10175556ab2aSLubomir Rintel com7 = info->fmt->regs[0].value; 1018cb7a01acSMauro Carvalho Chehab com7 |= wsize->com7_bit; 101901b84448SJacopo Mondi ret = ov7670_write(sd, REG_COM7, com7); 102001b84448SJacopo Mondi if (ret) 102101b84448SJacopo Mondi return ret; 102201b84448SJacopo Mondi 102301b84448SJacopo Mondi /* 102401b84448SJacopo Mondi * Configure the media bus through COM10 register 102501b84448SJacopo Mondi */ 102601b84448SJacopo Mondi if (info->mbus_config & V4L2_MBUS_VSYNC_ACTIVE_LOW) 102701b84448SJacopo Mondi com10 |= COM10_VS_NEG; 102801b84448SJacopo Mondi if (info->mbus_config & V4L2_MBUS_HSYNC_ACTIVE_LOW) 102901b84448SJacopo Mondi com10 |= COM10_HREF_REV; 103001b84448SJacopo Mondi if (info->pclk_hb_disable) 103101b84448SJacopo Mondi com10 |= COM10_PCLK_HB; 103201b84448SJacopo Mondi ret = ov7670_write(sd, REG_COM10, com10); 103301b84448SJacopo Mondi if (ret) 103401b84448SJacopo Mondi return ret; 103501b84448SJacopo Mondi 1036cb7a01acSMauro Carvalho Chehab /* 1037cb7a01acSMauro Carvalho Chehab * Now write the rest of the array. Also store start/stops 1038cb7a01acSMauro Carvalho Chehab */ 10395556ab2aSLubomir Rintel ret = ov7670_write_array(sd, info->fmt->regs + 1); 104001b84448SJacopo Mondi if (ret) 104101b84448SJacopo Mondi return ret; 104201b84448SJacopo Mondi 104301b84448SJacopo Mondi ret = ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, 1044cb7a01acSMauro Carvalho Chehab wsize->vstop); 104501b84448SJacopo Mondi if (ret) 104601b84448SJacopo Mondi return ret; 104701b84448SJacopo Mondi 104801b84448SJacopo Mondi if (wsize->regs) { 1049cb7a01acSMauro Carvalho Chehab ret = ov7670_write_array(sd, wsize->regs); 105001b84448SJacopo Mondi if (ret) 105101b84448SJacopo Mondi return ret; 105201b84448SJacopo Mondi } 105301b84448SJacopo Mondi 1054cb7a01acSMauro Carvalho Chehab /* 1055cb7a01acSMauro Carvalho Chehab * If we're running RGB565, we must rewrite clkrc after setting 1056cb7a01acSMauro Carvalho Chehab * the other parameters or the image looks poor. If we're *not* 1057cb7a01acSMauro Carvalho Chehab * doing RGB565, we must not rewrite clkrc or the image looks 1058cb7a01acSMauro Carvalho Chehab * *really* poor. 1059cb7a01acSMauro Carvalho Chehab * 1060cb7a01acSMauro Carvalho Chehab * (Update) Now that we retain clkrc state, we should be able 1061cb7a01acSMauro Carvalho Chehab * to write it unconditionally, and that will make the frame 1062cb7a01acSMauro Carvalho Chehab * rate persistent too. 1063cb7a01acSMauro Carvalho Chehab */ 1064cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_CLKRC, info->clkrc); 106501b84448SJacopo Mondi if (ret) 106601b84448SJacopo Mondi return ret; 106701b84448SJacopo Mondi 1068cb7a01acSMauro Carvalho Chehab return 0; 1069cb7a01acSMauro Carvalho Chehab } 1070cb7a01acSMauro Carvalho Chehab 10715556ab2aSLubomir Rintel /* 10725556ab2aSLubomir Rintel * Set a format. 10735556ab2aSLubomir Rintel */ 10745556ab2aSLubomir Rintel static int ov7670_set_fmt(struct v4l2_subdev *sd, 10755556ab2aSLubomir Rintel struct v4l2_subdev_pad_config *cfg, 10765556ab2aSLubomir Rintel struct v4l2_subdev_format *format) 10775556ab2aSLubomir Rintel { 10785556ab2aSLubomir Rintel struct ov7670_info *info = to_state(sd); 10795556ab2aSLubomir Rintel #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 10805556ab2aSLubomir Rintel struct v4l2_mbus_framefmt *mbus_fmt; 10815556ab2aSLubomir Rintel #endif 10825556ab2aSLubomir Rintel int ret; 10835556ab2aSLubomir Rintel 10845556ab2aSLubomir Rintel if (format->pad) 10855556ab2aSLubomir Rintel return -EINVAL; 10865556ab2aSLubomir Rintel 10875556ab2aSLubomir Rintel if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 10885556ab2aSLubomir Rintel ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL); 10895556ab2aSLubomir Rintel if (ret) 10905556ab2aSLubomir Rintel return ret; 10915556ab2aSLubomir Rintel #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 10925556ab2aSLubomir Rintel mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); 10935556ab2aSLubomir Rintel *mbus_fmt = format->format; 10945556ab2aSLubomir Rintel return 0; 10955556ab2aSLubomir Rintel #else 10965556ab2aSLubomir Rintel return -ENOTTY; 10975556ab2aSLubomir Rintel #endif 10985556ab2aSLubomir Rintel } 10995556ab2aSLubomir Rintel 11005556ab2aSLubomir Rintel ret = ov7670_try_fmt_internal(sd, &format->format, &info->fmt, &info->wsize); 11015556ab2aSLubomir Rintel if (ret) 11025556ab2aSLubomir Rintel return ret; 11035556ab2aSLubomir Rintel 11045556ab2aSLubomir Rintel ret = ov7670_apply_fmt(sd); 11055556ab2aSLubomir Rintel if (ret) 11065556ab2aSLubomir Rintel return ret; 11075556ab2aSLubomir Rintel 11085556ab2aSLubomir Rintel return 0; 11095556ab2aSLubomir Rintel } 11105556ab2aSLubomir Rintel 1111c0662dd4SWenyou Yang static int ov7670_get_fmt(struct v4l2_subdev *sd, 1112c0662dd4SWenyou Yang struct v4l2_subdev_pad_config *cfg, 1113c0662dd4SWenyou Yang struct v4l2_subdev_format *format) 1114c0662dd4SWenyou Yang { 1115c0662dd4SWenyou Yang struct ov7670_info *info = to_state(sd); 1116c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 1117c0662dd4SWenyou Yang struct v4l2_mbus_framefmt *mbus_fmt; 1118c0662dd4SWenyou Yang #endif 1119c0662dd4SWenyou Yang 1120c0662dd4SWenyou Yang if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 1121c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 1122c0662dd4SWenyou Yang mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, 0); 1123c0662dd4SWenyou Yang format->format = *mbus_fmt; 1124c0662dd4SWenyou Yang return 0; 1125c0662dd4SWenyou Yang #else 1126c0662dd4SWenyou Yang return -ENOTTY; 1127c0662dd4SWenyou Yang #endif 1128c0662dd4SWenyou Yang } else { 1129c0662dd4SWenyou Yang format->format = info->format; 1130c0662dd4SWenyou Yang } 1131c0662dd4SWenyou Yang 1132c0662dd4SWenyou Yang return 0; 1133c0662dd4SWenyou Yang } 1134c0662dd4SWenyou Yang 1135cb7a01acSMauro Carvalho Chehab /* 1136cb7a01acSMauro Carvalho Chehab * Implement G/S_PARM. There is a "high quality" mode we could try 1137cb7a01acSMauro Carvalho Chehab * to do someday; for now, we just do the frame rate tweak. 1138cb7a01acSMauro Carvalho Chehab */ 11394471109eSHans Verkuil static int ov7670_g_frame_interval(struct v4l2_subdev *sd, 11404471109eSHans Verkuil struct v4l2_subdev_frame_interval *ival) 1141cb7a01acSMauro Carvalho Chehab { 1142cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 1143cb7a01acSMauro Carvalho Chehab 1144cb7a01acSMauro Carvalho Chehab 11454471109eSHans Verkuil info->devtype->get_framerate(sd, &ival->interval); 1146f6dd927fSJavier Martin 1147cb7a01acSMauro Carvalho Chehab return 0; 1148cb7a01acSMauro Carvalho Chehab } 1149cb7a01acSMauro Carvalho Chehab 11504471109eSHans Verkuil static int ov7670_s_frame_interval(struct v4l2_subdev *sd, 11514471109eSHans Verkuil struct v4l2_subdev_frame_interval *ival) 1152cb7a01acSMauro Carvalho Chehab { 11534471109eSHans Verkuil struct v4l2_fract *tpf = &ival->interval; 1154cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 1155cb7a01acSMauro Carvalho Chehab 1156cb7a01acSMauro Carvalho Chehab 1157f6dd927fSJavier Martin return info->devtype->set_framerate(sd, tpf); 1158cb7a01acSMauro Carvalho Chehab } 1159cb7a01acSMauro Carvalho Chehab 1160cb7a01acSMauro Carvalho Chehab 1161cb7a01acSMauro Carvalho Chehab /* 1162cb7a01acSMauro Carvalho Chehab * Frame intervals. Since frame rates are controlled with the clock 1163cb7a01acSMauro Carvalho Chehab * divider, we can only do 30/n for integer n values. So no continuous 1164cb7a01acSMauro Carvalho Chehab * or stepwise options. Here we just pick a handful of logical values. 1165cb7a01acSMauro Carvalho Chehab */ 1166cb7a01acSMauro Carvalho Chehab 1167cb7a01acSMauro Carvalho Chehab static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; 1168cb7a01acSMauro Carvalho Chehab 116917bef885SHans Verkuil static int ov7670_enum_frame_interval(struct v4l2_subdev *sd, 117017bef885SHans Verkuil struct v4l2_subdev_pad_config *cfg, 117117bef885SHans Verkuil struct v4l2_subdev_frame_interval_enum *fie) 1172cb7a01acSMauro Carvalho Chehab { 1173b8cc79fdSHans Verkuil struct ov7670_info *info = to_state(sd); 1174b8cc79fdSHans Verkuil unsigned int n_win_sizes = info->devtype->n_win_sizes; 1175b8cc79fdSHans Verkuil int i; 1176b8cc79fdSHans Verkuil 117717bef885SHans Verkuil if (fie->pad) 1178cb7a01acSMauro Carvalho Chehab return -EINVAL; 117917bef885SHans Verkuil if (fie->index >= ARRAY_SIZE(ov7670_frame_rates)) 118017bef885SHans Verkuil return -EINVAL; 1181b8cc79fdSHans Verkuil 1182b8cc79fdSHans Verkuil /* 1183b8cc79fdSHans Verkuil * Check if the width/height is valid. 1184b8cc79fdSHans Verkuil * 1185b8cc79fdSHans Verkuil * If a minimum width/height was requested, filter out the capture 1186b8cc79fdSHans Verkuil * windows that fall outside that. 1187b8cc79fdSHans Verkuil */ 1188b8cc79fdSHans Verkuil for (i = 0; i < n_win_sizes; i++) { 1189b8cc79fdSHans Verkuil struct ov7670_win_size *win = &info->devtype->win_sizes[i]; 1190b8cc79fdSHans Verkuil 1191b8cc79fdSHans Verkuil if (info->min_width && win->width < info->min_width) 1192b8cc79fdSHans Verkuil continue; 1193b8cc79fdSHans Verkuil if (info->min_height && win->height < info->min_height) 1194b8cc79fdSHans Verkuil continue; 1195b8cc79fdSHans Verkuil if (fie->width == win->width && fie->height == win->height) 1196b8cc79fdSHans Verkuil break; 1197b8cc79fdSHans Verkuil } 1198b8cc79fdSHans Verkuil if (i == n_win_sizes) 1199b8cc79fdSHans Verkuil return -EINVAL; 120017bef885SHans Verkuil fie->interval.numerator = 1; 120117bef885SHans Verkuil fie->interval.denominator = ov7670_frame_rates[fie->index]; 1202cb7a01acSMauro Carvalho Chehab return 0; 1203cb7a01acSMauro Carvalho Chehab } 1204cb7a01acSMauro Carvalho Chehab 1205cb7a01acSMauro Carvalho Chehab /* 1206cb7a01acSMauro Carvalho Chehab * Frame size enumeration 1207cb7a01acSMauro Carvalho Chehab */ 120817bef885SHans Verkuil static int ov7670_enum_frame_size(struct v4l2_subdev *sd, 120917bef885SHans Verkuil struct v4l2_subdev_pad_config *cfg, 121017bef885SHans Verkuil struct v4l2_subdev_frame_size_enum *fse) 1211cb7a01acSMauro Carvalho Chehab { 1212cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 1213cb7a01acSMauro Carvalho Chehab int i; 1214cb7a01acSMauro Carvalho Chehab int num_valid = -1; 121517bef885SHans Verkuil __u32 index = fse->index; 1216d058e237SJavier Martin unsigned int n_win_sizes = info->devtype->n_win_sizes; 1217cb7a01acSMauro Carvalho Chehab 121817bef885SHans Verkuil if (fse->pad) 121917bef885SHans Verkuil return -EINVAL; 122017bef885SHans Verkuil 1221cb7a01acSMauro Carvalho Chehab /* 1222cb7a01acSMauro Carvalho Chehab * If a minimum width/height was requested, filter out the capture 1223cb7a01acSMauro Carvalho Chehab * windows that fall outside that. 1224cb7a01acSMauro Carvalho Chehab */ 1225d058e237SJavier Martin for (i = 0; i < n_win_sizes; i++) { 1226322e6d19SGuennadi Liakhovetski struct ov7670_win_size *win = &info->devtype->win_sizes[i]; 1227cb7a01acSMauro Carvalho Chehab if (info->min_width && win->width < info->min_width) 1228cb7a01acSMauro Carvalho Chehab continue; 1229cb7a01acSMauro Carvalho Chehab if (info->min_height && win->height < info->min_height) 1230cb7a01acSMauro Carvalho Chehab continue; 1231cb7a01acSMauro Carvalho Chehab if (index == ++num_valid) { 123217bef885SHans Verkuil fse->min_width = fse->max_width = win->width; 123317bef885SHans Verkuil fse->min_height = fse->max_height = win->height; 1234cb7a01acSMauro Carvalho Chehab return 0; 1235cb7a01acSMauro Carvalho Chehab } 1236cb7a01acSMauro Carvalho Chehab } 1237cb7a01acSMauro Carvalho Chehab 1238cb7a01acSMauro Carvalho Chehab return -EINVAL; 1239cb7a01acSMauro Carvalho Chehab } 1240cb7a01acSMauro Carvalho Chehab 1241cb7a01acSMauro Carvalho Chehab /* 1242cb7a01acSMauro Carvalho Chehab * Code for dealing with controls. 1243cb7a01acSMauro Carvalho Chehab */ 1244cb7a01acSMauro Carvalho Chehab 1245cb7a01acSMauro Carvalho Chehab static int ov7670_store_cmatrix(struct v4l2_subdev *sd, 1246cb7a01acSMauro Carvalho Chehab int matrix[CMATRIX_LEN]) 1247cb7a01acSMauro Carvalho Chehab { 1248cb7a01acSMauro Carvalho Chehab int i, ret; 1249cb7a01acSMauro Carvalho Chehab unsigned char signbits = 0; 1250cb7a01acSMauro Carvalho Chehab 1251cb7a01acSMauro Carvalho Chehab /* 1252cb7a01acSMauro Carvalho Chehab * Weird crap seems to exist in the upper part of 1253cb7a01acSMauro Carvalho Chehab * the sign bits register, so let's preserve it. 1254cb7a01acSMauro Carvalho Chehab */ 1255cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); 1256cb7a01acSMauro Carvalho Chehab signbits &= 0xc0; 1257cb7a01acSMauro Carvalho Chehab 1258cb7a01acSMauro Carvalho Chehab for (i = 0; i < CMATRIX_LEN; i++) { 1259cb7a01acSMauro Carvalho Chehab unsigned char raw; 1260cb7a01acSMauro Carvalho Chehab 1261cb7a01acSMauro Carvalho Chehab if (matrix[i] < 0) { 1262cb7a01acSMauro Carvalho Chehab signbits |= (1 << i); 1263cb7a01acSMauro Carvalho Chehab if (matrix[i] < -255) 1264cb7a01acSMauro Carvalho Chehab raw = 0xff; 1265cb7a01acSMauro Carvalho Chehab else 1266cb7a01acSMauro Carvalho Chehab raw = (-1 * matrix[i]) & 0xff; 1267cb7a01acSMauro Carvalho Chehab } 1268cb7a01acSMauro Carvalho Chehab else { 1269cb7a01acSMauro Carvalho Chehab if (matrix[i] > 255) 1270cb7a01acSMauro Carvalho Chehab raw = 0xff; 1271cb7a01acSMauro Carvalho Chehab else 1272cb7a01acSMauro Carvalho Chehab raw = matrix[i] & 0xff; 1273cb7a01acSMauro Carvalho Chehab } 1274cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); 1275cb7a01acSMauro Carvalho Chehab } 1276cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); 1277cb7a01acSMauro Carvalho Chehab return ret; 1278cb7a01acSMauro Carvalho Chehab } 1279cb7a01acSMauro Carvalho Chehab 1280cb7a01acSMauro Carvalho Chehab 1281cb7a01acSMauro Carvalho Chehab /* 1282cb7a01acSMauro Carvalho Chehab * Hue also requires messing with the color matrix. It also requires 1283cb7a01acSMauro Carvalho Chehab * trig functions, which tend not to be well supported in the kernel. 1284cb7a01acSMauro Carvalho Chehab * So here is a simple table of sine values, 0-90 degrees, in steps 1285cb7a01acSMauro Carvalho Chehab * of five degrees. Values are multiplied by 1000. 1286cb7a01acSMauro Carvalho Chehab * 1287cb7a01acSMauro Carvalho Chehab * The following naive approximate trig functions require an argument 1288cb7a01acSMauro Carvalho Chehab * carefully limited to -180 <= theta <= 180. 1289cb7a01acSMauro Carvalho Chehab */ 1290cb7a01acSMauro Carvalho Chehab #define SIN_STEP 5 1291cb7a01acSMauro Carvalho Chehab static const int ov7670_sin_table[] = { 1292cb7a01acSMauro Carvalho Chehab 0, 87, 173, 258, 342, 422, 1293cb7a01acSMauro Carvalho Chehab 499, 573, 642, 707, 766, 819, 1294cb7a01acSMauro Carvalho Chehab 866, 906, 939, 965, 984, 996, 1295cb7a01acSMauro Carvalho Chehab 1000 1296cb7a01acSMauro Carvalho Chehab }; 1297cb7a01acSMauro Carvalho Chehab 1298cb7a01acSMauro Carvalho Chehab static int ov7670_sine(int theta) 1299cb7a01acSMauro Carvalho Chehab { 1300cb7a01acSMauro Carvalho Chehab int chs = 1; 1301cb7a01acSMauro Carvalho Chehab int sine; 1302cb7a01acSMauro Carvalho Chehab 1303cb7a01acSMauro Carvalho Chehab if (theta < 0) { 1304cb7a01acSMauro Carvalho Chehab theta = -theta; 1305cb7a01acSMauro Carvalho Chehab chs = -1; 1306cb7a01acSMauro Carvalho Chehab } 1307cb7a01acSMauro Carvalho Chehab if (theta <= 90) 1308cb7a01acSMauro Carvalho Chehab sine = ov7670_sin_table[theta/SIN_STEP]; 1309cb7a01acSMauro Carvalho Chehab else { 1310cb7a01acSMauro Carvalho Chehab theta -= 90; 1311cb7a01acSMauro Carvalho Chehab sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; 1312cb7a01acSMauro Carvalho Chehab } 1313cb7a01acSMauro Carvalho Chehab return sine*chs; 1314cb7a01acSMauro Carvalho Chehab } 1315cb7a01acSMauro Carvalho Chehab 1316cb7a01acSMauro Carvalho Chehab static int ov7670_cosine(int theta) 1317cb7a01acSMauro Carvalho Chehab { 1318cb7a01acSMauro Carvalho Chehab theta = 90 - theta; 1319cb7a01acSMauro Carvalho Chehab if (theta > 180) 1320cb7a01acSMauro Carvalho Chehab theta -= 360; 1321cb7a01acSMauro Carvalho Chehab else if (theta < -180) 1322cb7a01acSMauro Carvalho Chehab theta += 360; 1323cb7a01acSMauro Carvalho Chehab return ov7670_sine(theta); 1324cb7a01acSMauro Carvalho Chehab } 1325cb7a01acSMauro Carvalho Chehab 1326cb7a01acSMauro Carvalho Chehab 1327cb7a01acSMauro Carvalho Chehab 1328cb7a01acSMauro Carvalho Chehab 1329cb7a01acSMauro Carvalho Chehab static void ov7670_calc_cmatrix(struct ov7670_info *info, 1330492959c7SJavier Martin int matrix[CMATRIX_LEN], int sat, int hue) 1331cb7a01acSMauro Carvalho Chehab { 1332cb7a01acSMauro Carvalho Chehab int i; 1333cb7a01acSMauro Carvalho Chehab /* 1334cb7a01acSMauro Carvalho Chehab * Apply the current saturation setting first. 1335cb7a01acSMauro Carvalho Chehab */ 1336cb7a01acSMauro Carvalho Chehab for (i = 0; i < CMATRIX_LEN; i++) 1337492959c7SJavier Martin matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7; 1338cb7a01acSMauro Carvalho Chehab /* 1339cb7a01acSMauro Carvalho Chehab * Then, if need be, rotate the hue value. 1340cb7a01acSMauro Carvalho Chehab */ 1341492959c7SJavier Martin if (hue != 0) { 1342cb7a01acSMauro Carvalho Chehab int sinth, costh, tmpmatrix[CMATRIX_LEN]; 1343cb7a01acSMauro Carvalho Chehab 1344cb7a01acSMauro Carvalho Chehab memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); 1345492959c7SJavier Martin sinth = ov7670_sine(hue); 1346492959c7SJavier Martin costh = ov7670_cosine(hue); 1347cb7a01acSMauro Carvalho Chehab 1348cb7a01acSMauro Carvalho Chehab matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; 1349cb7a01acSMauro Carvalho Chehab matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; 1350cb7a01acSMauro Carvalho Chehab matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; 1351cb7a01acSMauro Carvalho Chehab matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; 1352cb7a01acSMauro Carvalho Chehab matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; 1353cb7a01acSMauro Carvalho Chehab matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; 1354cb7a01acSMauro Carvalho Chehab } 1355cb7a01acSMauro Carvalho Chehab } 1356cb7a01acSMauro Carvalho Chehab 1357cb7a01acSMauro Carvalho Chehab 1358cb7a01acSMauro Carvalho Chehab 1359492959c7SJavier Martin static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue) 1360cb7a01acSMauro Carvalho Chehab { 1361cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 1362cb7a01acSMauro Carvalho Chehab int matrix[CMATRIX_LEN]; 1363cb7a01acSMauro Carvalho Chehab int ret; 1364cb7a01acSMauro Carvalho Chehab 1365492959c7SJavier Martin ov7670_calc_cmatrix(info, matrix, sat, hue); 1366cb7a01acSMauro Carvalho Chehab ret = ov7670_store_cmatrix(sd, matrix); 1367cb7a01acSMauro Carvalho Chehab return ret; 1368cb7a01acSMauro Carvalho Chehab } 1369cb7a01acSMauro Carvalho Chehab 1370cb7a01acSMauro Carvalho Chehab 1371cb7a01acSMauro Carvalho Chehab /* 1372cb7a01acSMauro Carvalho Chehab * Some weird registers seem to store values in a sign/magnitude format! 1373cb7a01acSMauro Carvalho Chehab */ 1374cb7a01acSMauro Carvalho Chehab 1375cb7a01acSMauro Carvalho Chehab static unsigned char ov7670_abs_to_sm(unsigned char v) 1376cb7a01acSMauro Carvalho Chehab { 1377cb7a01acSMauro Carvalho Chehab if (v > 127) 1378cb7a01acSMauro Carvalho Chehab return v & 0x7f; 1379cb7a01acSMauro Carvalho Chehab return (128 - v) | 0x80; 1380cb7a01acSMauro Carvalho Chehab } 1381cb7a01acSMauro Carvalho Chehab 1382cb7a01acSMauro Carvalho Chehab static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) 1383cb7a01acSMauro Carvalho Chehab { 1384cb7a01acSMauro Carvalho Chehab unsigned char com8 = 0, v; 1385cb7a01acSMauro Carvalho Chehab int ret; 1386cb7a01acSMauro Carvalho Chehab 1387cb7a01acSMauro Carvalho Chehab ov7670_read(sd, REG_COM8, &com8); 1388cb7a01acSMauro Carvalho Chehab com8 &= ~COM8_AEC; 1389cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_COM8, com8); 1390cb7a01acSMauro Carvalho Chehab v = ov7670_abs_to_sm(value); 1391cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_BRIGHT, v); 1392cb7a01acSMauro Carvalho Chehab return ret; 1393cb7a01acSMauro Carvalho Chehab } 1394cb7a01acSMauro Carvalho Chehab 1395cb7a01acSMauro Carvalho Chehab static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) 1396cb7a01acSMauro Carvalho Chehab { 1397cb7a01acSMauro Carvalho Chehab return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); 1398cb7a01acSMauro Carvalho Chehab } 1399cb7a01acSMauro Carvalho Chehab 1400cb7a01acSMauro Carvalho Chehab static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) 1401cb7a01acSMauro Carvalho Chehab { 1402cb7a01acSMauro Carvalho Chehab unsigned char v = 0; 1403cb7a01acSMauro Carvalho Chehab int ret; 1404cb7a01acSMauro Carvalho Chehab 1405cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_MVFP, &v); 1406cb7a01acSMauro Carvalho Chehab if (value) 1407cb7a01acSMauro Carvalho Chehab v |= MVFP_MIRROR; 1408cb7a01acSMauro Carvalho Chehab else 1409cb7a01acSMauro Carvalho Chehab v &= ~MVFP_MIRROR; 1410cb7a01acSMauro Carvalho Chehab msleep(10); /* FIXME */ 1411cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_MVFP, v); 1412cb7a01acSMauro Carvalho Chehab return ret; 1413cb7a01acSMauro Carvalho Chehab } 1414cb7a01acSMauro Carvalho Chehab 1415cb7a01acSMauro Carvalho Chehab static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) 1416cb7a01acSMauro Carvalho Chehab { 1417cb7a01acSMauro Carvalho Chehab unsigned char v = 0; 1418cb7a01acSMauro Carvalho Chehab int ret; 1419cb7a01acSMauro Carvalho Chehab 1420cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_MVFP, &v); 1421cb7a01acSMauro Carvalho Chehab if (value) 1422cb7a01acSMauro Carvalho Chehab v |= MVFP_FLIP; 1423cb7a01acSMauro Carvalho Chehab else 1424cb7a01acSMauro Carvalho Chehab v &= ~MVFP_FLIP; 1425cb7a01acSMauro Carvalho Chehab msleep(10); /* FIXME */ 1426cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_MVFP, v); 1427cb7a01acSMauro Carvalho Chehab return ret; 1428cb7a01acSMauro Carvalho Chehab } 1429cb7a01acSMauro Carvalho Chehab 1430cb7a01acSMauro Carvalho Chehab /* 1431cb7a01acSMauro Carvalho Chehab * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes 1432cb7a01acSMauro Carvalho Chehab * the data sheet, the VREF parts should be the most significant, but 1433cb7a01acSMauro Carvalho Chehab * experience shows otherwise. There seems to be little value in 1434cb7a01acSMauro Carvalho Chehab * messing with the VREF bits, so we leave them alone. 1435cb7a01acSMauro Carvalho Chehab */ 1436cb7a01acSMauro Carvalho Chehab static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) 1437cb7a01acSMauro Carvalho Chehab { 1438cb7a01acSMauro Carvalho Chehab int ret; 1439cb7a01acSMauro Carvalho Chehab unsigned char gain; 1440cb7a01acSMauro Carvalho Chehab 1441cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_GAIN, &gain); 1442cb7a01acSMauro Carvalho Chehab *value = gain; 1443cb7a01acSMauro Carvalho Chehab return ret; 1444cb7a01acSMauro Carvalho Chehab } 1445cb7a01acSMauro Carvalho Chehab 1446cb7a01acSMauro Carvalho Chehab static int ov7670_s_gain(struct v4l2_subdev *sd, int value) 1447cb7a01acSMauro Carvalho Chehab { 1448cb7a01acSMauro Carvalho Chehab int ret; 1449cb7a01acSMauro Carvalho Chehab unsigned char com8; 1450cb7a01acSMauro Carvalho Chehab 1451cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_GAIN, value & 0xff); 1452cb7a01acSMauro Carvalho Chehab /* Have to turn off AGC as well */ 1453cb7a01acSMauro Carvalho Chehab if (ret == 0) { 1454cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_COM8, &com8); 1455cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); 1456cb7a01acSMauro Carvalho Chehab } 1457cb7a01acSMauro Carvalho Chehab return ret; 1458cb7a01acSMauro Carvalho Chehab } 1459cb7a01acSMauro Carvalho Chehab 1460cb7a01acSMauro Carvalho Chehab /* 1461cb7a01acSMauro Carvalho Chehab * Tweak autogain. 1462cb7a01acSMauro Carvalho Chehab */ 1463cb7a01acSMauro Carvalho Chehab static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) 1464cb7a01acSMauro Carvalho Chehab { 1465cb7a01acSMauro Carvalho Chehab int ret; 1466cb7a01acSMauro Carvalho Chehab unsigned char com8; 1467cb7a01acSMauro Carvalho Chehab 1468cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_COM8, &com8); 1469cb7a01acSMauro Carvalho Chehab if (ret == 0) { 1470cb7a01acSMauro Carvalho Chehab if (value) 1471cb7a01acSMauro Carvalho Chehab com8 |= COM8_AGC; 1472cb7a01acSMauro Carvalho Chehab else 1473cb7a01acSMauro Carvalho Chehab com8 &= ~COM8_AGC; 1474cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM8, com8); 1475cb7a01acSMauro Carvalho Chehab } 1476cb7a01acSMauro Carvalho Chehab return ret; 1477cb7a01acSMauro Carvalho Chehab } 1478cb7a01acSMauro Carvalho Chehab 1479cb7a01acSMauro Carvalho Chehab static int ov7670_s_exp(struct v4l2_subdev *sd, int value) 1480cb7a01acSMauro Carvalho Chehab { 1481cb7a01acSMauro Carvalho Chehab int ret; 1482cb7a01acSMauro Carvalho Chehab unsigned char com1, com8, aech, aechh; 1483cb7a01acSMauro Carvalho Chehab 1484cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_COM1, &com1) + 1485d487df9eSMauro Carvalho Chehab ov7670_read(sd, REG_COM8, &com8) + 1486cb7a01acSMauro Carvalho Chehab ov7670_read(sd, REG_AECHH, &aechh); 1487cb7a01acSMauro Carvalho Chehab if (ret) 1488cb7a01acSMauro Carvalho Chehab return ret; 1489cb7a01acSMauro Carvalho Chehab 1490cb7a01acSMauro Carvalho Chehab com1 = (com1 & 0xfc) | (value & 0x03); 1491cb7a01acSMauro Carvalho Chehab aech = (value >> 2) & 0xff; 1492cb7a01acSMauro Carvalho Chehab aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); 1493cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM1, com1) + 1494cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_AECH, aech) + 1495cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_AECHH, aechh); 1496cb7a01acSMauro Carvalho Chehab /* Have to turn off AEC as well */ 1497cb7a01acSMauro Carvalho Chehab if (ret == 0) 1498cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); 1499cb7a01acSMauro Carvalho Chehab return ret; 1500cb7a01acSMauro Carvalho Chehab } 1501cb7a01acSMauro Carvalho Chehab 1502cb7a01acSMauro Carvalho Chehab /* 1503cb7a01acSMauro Carvalho Chehab * Tweak autoexposure. 1504cb7a01acSMauro Carvalho Chehab */ 1505cb7a01acSMauro Carvalho Chehab static int ov7670_s_autoexp(struct v4l2_subdev *sd, 1506cb7a01acSMauro Carvalho Chehab enum v4l2_exposure_auto_type value) 1507cb7a01acSMauro Carvalho Chehab { 1508cb7a01acSMauro Carvalho Chehab int ret; 1509cb7a01acSMauro Carvalho Chehab unsigned char com8; 1510cb7a01acSMauro Carvalho Chehab 1511cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_COM8, &com8); 1512cb7a01acSMauro Carvalho Chehab if (ret == 0) { 1513cb7a01acSMauro Carvalho Chehab if (value == V4L2_EXPOSURE_AUTO) 1514cb7a01acSMauro Carvalho Chehab com8 |= COM8_AEC; 1515cb7a01acSMauro Carvalho Chehab else 1516cb7a01acSMauro Carvalho Chehab com8 &= ~COM8_AEC; 1517cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM8, com8); 1518cb7a01acSMauro Carvalho Chehab } 1519cb7a01acSMauro Carvalho Chehab return ret; 1520cb7a01acSMauro Carvalho Chehab } 1521cb7a01acSMauro Carvalho Chehab 1522b48d908dSAkinobu Mita static const char * const ov7670_test_pattern_menu[] = { 1523b48d908dSAkinobu Mita "No test output", 1524b48d908dSAkinobu Mita "Shifting \"1\"", 1525b48d908dSAkinobu Mita "8-bar color bar", 1526b48d908dSAkinobu Mita "Fade to gray color bar", 1527b48d908dSAkinobu Mita }; 1528b48d908dSAkinobu Mita 1529b48d908dSAkinobu Mita static int ov7670_s_test_pattern(struct v4l2_subdev *sd, int value) 1530b48d908dSAkinobu Mita { 1531b48d908dSAkinobu Mita int ret; 1532b48d908dSAkinobu Mita 1533b48d908dSAkinobu Mita ret = ov7670_update_bits(sd, REG_SCALING_XSC, TEST_PATTTERN_0, 1534b48d908dSAkinobu Mita value & BIT(0) ? TEST_PATTTERN_0 : 0); 1535b48d908dSAkinobu Mita if (ret) 1536b48d908dSAkinobu Mita return ret; 1537b48d908dSAkinobu Mita 1538b48d908dSAkinobu Mita return ov7670_update_bits(sd, REG_SCALING_YSC, TEST_PATTTERN_1, 1539b48d908dSAkinobu Mita value & BIT(1) ? TEST_PATTTERN_1 : 0); 1540b48d908dSAkinobu Mita } 1541cb7a01acSMauro Carvalho Chehab 1542492959c7SJavier Martin static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 1543cb7a01acSMauro Carvalho Chehab { 1544492959c7SJavier Martin struct v4l2_subdev *sd = to_sd(ctrl); 1545492959c7SJavier Martin struct ov7670_info *info = to_state(sd); 1546492959c7SJavier Martin 1547492959c7SJavier Martin switch (ctrl->id) { 1548cb7a01acSMauro Carvalho Chehab case V4L2_CID_AUTOGAIN: 1549492959c7SJavier Martin return ov7670_g_gain(sd, &info->gain->val); 1550cb7a01acSMauro Carvalho Chehab } 1551cb7a01acSMauro Carvalho Chehab return -EINVAL; 1552cb7a01acSMauro Carvalho Chehab } 1553cb7a01acSMauro Carvalho Chehab 1554492959c7SJavier Martin static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl) 1555cb7a01acSMauro Carvalho Chehab { 1556492959c7SJavier Martin struct v4l2_subdev *sd = to_sd(ctrl); 1557492959c7SJavier Martin struct ov7670_info *info = to_state(sd); 1558492959c7SJavier Martin 1559cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 1560cb7a01acSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 1561492959c7SJavier Martin return ov7670_s_brightness(sd, ctrl->val); 1562cb7a01acSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 1563492959c7SJavier Martin return ov7670_s_contrast(sd, ctrl->val); 1564cb7a01acSMauro Carvalho Chehab case V4L2_CID_SATURATION: 1565492959c7SJavier Martin return ov7670_s_sat_hue(sd, 1566492959c7SJavier Martin info->saturation->val, info->hue->val); 1567cb7a01acSMauro Carvalho Chehab case V4L2_CID_VFLIP: 1568492959c7SJavier Martin return ov7670_s_vflip(sd, ctrl->val); 1569cb7a01acSMauro Carvalho Chehab case V4L2_CID_HFLIP: 1570492959c7SJavier Martin return ov7670_s_hflip(sd, ctrl->val); 1571cb7a01acSMauro Carvalho Chehab case V4L2_CID_AUTOGAIN: 1572492959c7SJavier Martin /* Only set manual gain if auto gain is not explicitly 1573492959c7SJavier Martin turned on. */ 1574492959c7SJavier Martin if (!ctrl->val) { 1575492959c7SJavier Martin /* ov7670_s_gain turns off auto gain */ 1576492959c7SJavier Martin return ov7670_s_gain(sd, info->gain->val); 1577492959c7SJavier Martin } 1578492959c7SJavier Martin return ov7670_s_autogain(sd, ctrl->val); 1579cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE_AUTO: 1580492959c7SJavier Martin /* Only set manual exposure if auto exposure is not explicitly 1581492959c7SJavier Martin turned on. */ 1582492959c7SJavier Martin if (ctrl->val == V4L2_EXPOSURE_MANUAL) { 1583492959c7SJavier Martin /* ov7670_s_exp turns off auto exposure */ 1584492959c7SJavier Martin return ov7670_s_exp(sd, info->exposure->val); 1585492959c7SJavier Martin } 1586492959c7SJavier Martin return ov7670_s_autoexp(sd, ctrl->val); 1587b48d908dSAkinobu Mita case V4L2_CID_TEST_PATTERN: 1588b48d908dSAkinobu Mita return ov7670_s_test_pattern(sd, ctrl->val); 1589cb7a01acSMauro Carvalho Chehab } 1590cb7a01acSMauro Carvalho Chehab return -EINVAL; 1591cb7a01acSMauro Carvalho Chehab } 1592cb7a01acSMauro Carvalho Chehab 1593492959c7SJavier Martin static const struct v4l2_ctrl_ops ov7670_ctrl_ops = { 1594492959c7SJavier Martin .s_ctrl = ov7670_s_ctrl, 1595492959c7SJavier Martin .g_volatile_ctrl = ov7670_g_volatile_ctrl, 1596492959c7SJavier Martin }; 1597cb7a01acSMauro Carvalho Chehab 1598cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG 1599cb7a01acSMauro Carvalho Chehab static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 1600cb7a01acSMauro Carvalho Chehab { 1601cb7a01acSMauro Carvalho Chehab unsigned char val = 0; 1602cb7a01acSMauro Carvalho Chehab int ret; 1603cb7a01acSMauro Carvalho Chehab 1604cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, reg->reg & 0xff, &val); 1605cb7a01acSMauro Carvalho Chehab reg->val = val; 1606cb7a01acSMauro Carvalho Chehab reg->size = 1; 1607cb7a01acSMauro Carvalho Chehab return ret; 1608cb7a01acSMauro Carvalho Chehab } 1609cb7a01acSMauro Carvalho Chehab 1610977ba3b1SHans Verkuil static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) 1611cb7a01acSMauro Carvalho Chehab { 1612cb7a01acSMauro Carvalho Chehab ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); 1613cb7a01acSMauro Carvalho Chehab return 0; 1614cb7a01acSMauro Carvalho Chehab } 1615cb7a01acSMauro Carvalho Chehab #endif 1616cb7a01acSMauro Carvalho Chehab 161771862f63SWenyou Yang static int ov7670_s_power(struct v4l2_subdev *sd, int on) 161871862f63SWenyou Yang { 161971862f63SWenyou Yang struct ov7670_info *info = to_state(sd); 162071862f63SWenyou Yang 162171862f63SWenyou Yang if (info->pwdn_gpio) 162271862f63SWenyou Yang gpiod_set_value(info->pwdn_gpio, !on); 162371862f63SWenyou Yang if (on && info->resetb_gpio) { 162471862f63SWenyou Yang gpiod_set_value(info->resetb_gpio, 1); 162571862f63SWenyou Yang usleep_range(500, 1000); 162671862f63SWenyou Yang gpiod_set_value(info->resetb_gpio, 0); 162771862f63SWenyou Yang usleep_range(3000, 5000); 162871862f63SWenyou Yang } 162971862f63SWenyou Yang 163071862f63SWenyou Yang return 0; 163171862f63SWenyou Yang } 163271862f63SWenyou Yang 1633c0662dd4SWenyou Yang static void ov7670_get_default_format(struct v4l2_subdev *sd, 1634c0662dd4SWenyou Yang struct v4l2_mbus_framefmt *format) 1635c0662dd4SWenyou Yang { 1636c0662dd4SWenyou Yang struct ov7670_info *info = to_state(sd); 1637c0662dd4SWenyou Yang 1638c0662dd4SWenyou Yang format->width = info->devtype->win_sizes[0].width; 1639c0662dd4SWenyou Yang format->height = info->devtype->win_sizes[0].height; 1640c0662dd4SWenyou Yang format->colorspace = info->fmt->colorspace; 1641c0662dd4SWenyou Yang format->code = info->fmt->mbus_code; 1642c0662dd4SWenyou Yang format->field = V4L2_FIELD_NONE; 1643c0662dd4SWenyou Yang } 1644c0662dd4SWenyou Yang 1645c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 1646c0662dd4SWenyou Yang static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 1647c0662dd4SWenyou Yang { 1648c0662dd4SWenyou Yang struct v4l2_mbus_framefmt *format = 1649c0662dd4SWenyou Yang v4l2_subdev_get_try_format(sd, fh->pad, 0); 1650c0662dd4SWenyou Yang 1651c0662dd4SWenyou Yang ov7670_get_default_format(sd, format); 1652c0662dd4SWenyou Yang 1653c0662dd4SWenyou Yang return 0; 1654c0662dd4SWenyou Yang } 1655c0662dd4SWenyou Yang #endif 1656c0662dd4SWenyou Yang 1657cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 1658cb7a01acSMauro Carvalho Chehab 1659cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops ov7670_core_ops = { 1660cb7a01acSMauro Carvalho Chehab .reset = ov7670_reset, 1661cb7a01acSMauro Carvalho Chehab .init = ov7670_init, 16627852adf8SAkinobu Mita .log_status = v4l2_ctrl_subdev_log_status, 16637852adf8SAkinobu Mita .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 16647852adf8SAkinobu Mita .unsubscribe_event = v4l2_event_subdev_unsubscribe, 1665cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG 1666cb7a01acSMauro Carvalho Chehab .g_register = ov7670_g_register, 1667cb7a01acSMauro Carvalho Chehab .s_register = ov7670_s_register, 1668cb7a01acSMauro Carvalho Chehab #endif 1669cb7a01acSMauro Carvalho Chehab }; 1670cb7a01acSMauro Carvalho Chehab 1671cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops ov7670_video_ops = { 16724471109eSHans Verkuil .s_frame_interval = ov7670_s_frame_interval, 16734471109eSHans Verkuil .g_frame_interval = ov7670_g_frame_interval, 167417bef885SHans Verkuil }; 167517bef885SHans Verkuil 167617bef885SHans Verkuil static const struct v4l2_subdev_pad_ops ov7670_pad_ops = { 167717bef885SHans Verkuil .enum_frame_interval = ov7670_enum_frame_interval, 167817bef885SHans Verkuil .enum_frame_size = ov7670_enum_frame_size, 1679ebcff5fcSHans Verkuil .enum_mbus_code = ov7670_enum_mbus_code, 1680c0662dd4SWenyou Yang .get_fmt = ov7670_get_fmt, 1681717fd5b4SHans Verkuil .set_fmt = ov7670_set_fmt, 1682cb7a01acSMauro Carvalho Chehab }; 1683cb7a01acSMauro Carvalho Chehab 1684cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops ov7670_ops = { 1685cb7a01acSMauro Carvalho Chehab .core = &ov7670_core_ops, 1686cb7a01acSMauro Carvalho Chehab .video = &ov7670_video_ops, 168717bef885SHans Verkuil .pad = &ov7670_pad_ops, 1688cb7a01acSMauro Carvalho Chehab }; 1689cb7a01acSMauro Carvalho Chehab 1690c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 1691c0662dd4SWenyou Yang static const struct v4l2_subdev_internal_ops ov7670_subdev_internal_ops = { 1692c0662dd4SWenyou Yang .open = ov7670_open, 1693c0662dd4SWenyou Yang }; 1694c0662dd4SWenyou Yang #endif 1695c0662dd4SWenyou Yang 1696cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 1697cb7a01acSMauro Carvalho Chehab 1698d058e237SJavier Martin static const struct ov7670_devtype ov7670_devdata[] = { 1699d058e237SJavier Martin [MODEL_OV7670] = { 1700d058e237SJavier Martin .win_sizes = ov7670_win_sizes, 1701d058e237SJavier Martin .n_win_sizes = ARRAY_SIZE(ov7670_win_sizes), 1702f6dd927fSJavier Martin .set_framerate = ov7670_set_framerate_legacy, 1703f6dd927fSJavier Martin .get_framerate = ov7670_get_framerate_legacy, 1704d058e237SJavier Martin }, 1705d058e237SJavier Martin [MODEL_OV7675] = { 1706d058e237SJavier Martin .win_sizes = ov7675_win_sizes, 1707d058e237SJavier Martin .n_win_sizes = ARRAY_SIZE(ov7675_win_sizes), 1708f6dd927fSJavier Martin .set_framerate = ov7675_set_framerate, 1709f6dd927fSJavier Martin .get_framerate = ov7675_get_framerate, 1710d058e237SJavier Martin }, 1711d058e237SJavier Martin }; 1712d058e237SJavier Martin 1713a0c4164eSHans Verkuil static int ov7670_init_gpio(struct i2c_client *client, struct ov7670_info *info) 1714a0c4164eSHans Verkuil { 1715a0c4164eSHans Verkuil info->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", 1716a0c4164eSHans Verkuil GPIOD_OUT_LOW); 1717a0c4164eSHans Verkuil if (IS_ERR(info->pwdn_gpio)) { 1718a0c4164eSHans Verkuil dev_info(&client->dev, "can't get %s GPIO\n", "powerdown"); 1719a0c4164eSHans Verkuil return PTR_ERR(info->pwdn_gpio); 1720a0c4164eSHans Verkuil } 1721a0c4164eSHans Verkuil 1722a0c4164eSHans Verkuil info->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset", 1723a0c4164eSHans Verkuil GPIOD_OUT_LOW); 1724a0c4164eSHans Verkuil if (IS_ERR(info->resetb_gpio)) { 1725a0c4164eSHans Verkuil dev_info(&client->dev, "can't get %s GPIO\n", "reset"); 1726a0c4164eSHans Verkuil return PTR_ERR(info->resetb_gpio); 1727a0c4164eSHans Verkuil } 1728a0c4164eSHans Verkuil 1729a0c4164eSHans Verkuil usleep_range(3000, 5000); 1730a0c4164eSHans Verkuil 1731a0c4164eSHans Verkuil return 0; 1732a0c4164eSHans Verkuil } 1733a0c4164eSHans Verkuil 173401b84448SJacopo Mondi /* 173501b84448SJacopo Mondi * ov7670_parse_dt() - Parse device tree to collect mbus configuration 173601b84448SJacopo Mondi * properties 173701b84448SJacopo Mondi */ 173801b84448SJacopo Mondi static int ov7670_parse_dt(struct device *dev, 173901b84448SJacopo Mondi struct ov7670_info *info) 174001b84448SJacopo Mondi { 174101b84448SJacopo Mondi struct fwnode_handle *fwnode = dev_fwnode(dev); 174260359a28SSakari Ailus struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; 174301b84448SJacopo Mondi struct fwnode_handle *ep; 174401b84448SJacopo Mondi int ret; 174501b84448SJacopo Mondi 174601b84448SJacopo Mondi if (!fwnode) 174701b84448SJacopo Mondi return -EINVAL; 174801b84448SJacopo Mondi 174901b84448SJacopo Mondi info->pclk_hb_disable = false; 175001b84448SJacopo Mondi if (fwnode_property_present(fwnode, "ov7670,pclk-hb-disable")) 175101b84448SJacopo Mondi info->pclk_hb_disable = true; 175201b84448SJacopo Mondi 175301b84448SJacopo Mondi ep = fwnode_graph_get_next_endpoint(fwnode, NULL); 175401b84448SJacopo Mondi if (!ep) 175501b84448SJacopo Mondi return -EINVAL; 175601b84448SJacopo Mondi 175701b84448SJacopo Mondi ret = v4l2_fwnode_endpoint_parse(ep, &bus_cfg); 175801b84448SJacopo Mondi fwnode_handle_put(ep); 175909a48f74SJacopo Mondi if (ret) 176001b84448SJacopo Mondi return ret; 176101b84448SJacopo Mondi 176201b84448SJacopo Mondi if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) { 176301b84448SJacopo Mondi dev_err(dev, "Unsupported media bus type\n"); 176401b84448SJacopo Mondi return ret; 176501b84448SJacopo Mondi } 176601b84448SJacopo Mondi info->mbus_config = bus_cfg.bus.parallel.flags; 176701b84448SJacopo Mondi 176801b84448SJacopo Mondi return 0; 176901b84448SJacopo Mondi } 177001b84448SJacopo Mondi 1771cb7a01acSMauro Carvalho Chehab static int ov7670_probe(struct i2c_client *client, 1772cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *id) 1773cb7a01acSMauro Carvalho Chehab { 1774f6dd927fSJavier Martin struct v4l2_fract tpf; 1775cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd; 1776cb7a01acSMauro Carvalho Chehab struct ov7670_info *info; 1777cb7a01acSMauro Carvalho Chehab int ret; 1778cb7a01acSMauro Carvalho Chehab 1779c02b211dSLaurent Pinchart info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); 1780cb7a01acSMauro Carvalho Chehab if (info == NULL) 1781cb7a01acSMauro Carvalho Chehab return -ENOMEM; 1782cb7a01acSMauro Carvalho Chehab sd = &info->sd; 1783cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(sd, client, &ov7670_ops); 1784cb7a01acSMauro Carvalho Chehab 1785c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API 1786c0662dd4SWenyou Yang sd->internal_ops = &ov7670_subdev_internal_ops; 17877852adf8SAkinobu Mita sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 1788c0662dd4SWenyou Yang #endif 1789c0662dd4SWenyou Yang 1790cb7a01acSMauro Carvalho Chehab info->clock_speed = 30; /* default: a guess */ 179101b84448SJacopo Mondi 179201b84448SJacopo Mondi if (dev_fwnode(&client->dev)) { 179301b84448SJacopo Mondi ret = ov7670_parse_dt(&client->dev, info); 179401b84448SJacopo Mondi if (ret) 179501b84448SJacopo Mondi return ret; 179601b84448SJacopo Mondi 179701b84448SJacopo Mondi } else if (client->dev.platform_data) { 1798cb7a01acSMauro Carvalho Chehab struct ov7670_config *config = client->dev.platform_data; 1799cb7a01acSMauro Carvalho Chehab 1800cb7a01acSMauro Carvalho Chehab /* 1801cb7a01acSMauro Carvalho Chehab * Must apply configuration before initializing device, because it 1802cb7a01acSMauro Carvalho Chehab * selects I/O method. 1803cb7a01acSMauro Carvalho Chehab */ 1804cb7a01acSMauro Carvalho Chehab info->min_width = config->min_width; 1805cb7a01acSMauro Carvalho Chehab info->min_height = config->min_height; 1806cb7a01acSMauro Carvalho Chehab info->use_smbus = config->use_smbus; 1807cb7a01acSMauro Carvalho Chehab 1808cb7a01acSMauro Carvalho Chehab if (config->clock_speed) 1809cb7a01acSMauro Carvalho Chehab info->clock_speed = config->clock_speed; 181004ee6d92SJavier Martin 181161da76beSJacopo Mondi if (config->pll_bypass) 181204ee6d92SJavier Martin info->pll_bypass = true; 1813ee95258eSJavier Martin 1814ee95258eSJavier Martin if (config->pclk_hb_disable) 1815ee95258eSJavier Martin info->pclk_hb_disable = true; 1816cb7a01acSMauro Carvalho Chehab } 1817cb7a01acSMauro Carvalho Chehab 1818786fa584SLubomir Rintel info->clk = devm_clk_get(&client->dev, "xclk"); /* optional */ 1819786fa584SLubomir Rintel if (IS_ERR(info->clk)) { 1820786fa584SLubomir Rintel ret = PTR_ERR(info->clk); 1821786fa584SLubomir Rintel if (ret == -ENOENT) 1822786fa584SLubomir Rintel info->clk = NULL; 1823786fa584SLubomir Rintel else 1824786fa584SLubomir Rintel return ret; 1825786fa584SLubomir Rintel } 1826786fa584SLubomir Rintel if (info->clk) { 18271fc86ad0SFabio Estevam ret = clk_prepare_enable(info->clk); 18281fc86ad0SFabio Estevam if (ret) 18291fc86ad0SFabio Estevam return ret; 18300a024d63SHans Verkuil 18310a024d63SHans Verkuil info->clock_speed = clk_get_rate(info->clk) / 1000000; 18320a024d63SHans Verkuil if (info->clock_speed < 10 || info->clock_speed > 48) { 18330a024d63SHans Verkuil ret = -EINVAL; 18340a024d63SHans Verkuil goto clk_disable; 18350a024d63SHans Verkuil } 1836786fa584SLubomir Rintel } 18370a024d63SHans Verkuil 183871862f63SWenyou Yang ret = ov7670_init_gpio(client, info); 183971862f63SWenyou Yang if (ret) 184071862f63SWenyou Yang goto clk_disable; 184171862f63SWenyou Yang 184271862f63SWenyou Yang ov7670_s_power(sd, 1); 184371862f63SWenyou Yang 1844cb7a01acSMauro Carvalho Chehab /* Make sure it's an ov7670 */ 1845cb7a01acSMauro Carvalho Chehab ret = ov7670_detect(sd); 1846cb7a01acSMauro Carvalho Chehab if (ret) { 1847cb7a01acSMauro Carvalho Chehab v4l_dbg(1, debug, client, 1848cb7a01acSMauro Carvalho Chehab "chip found @ 0x%x (%s) is not an ov7670 chip.\n", 1849cb7a01acSMauro Carvalho Chehab client->addr << 1, client->adapter->name); 185071862f63SWenyou Yang goto power_off; 1851cb7a01acSMauro Carvalho Chehab } 1852cb7a01acSMauro Carvalho Chehab v4l_info(client, "chip found @ 0x%02x (%s)\n", 1853cb7a01acSMauro Carvalho Chehab client->addr << 1, client->adapter->name); 1854cb7a01acSMauro Carvalho Chehab 1855d058e237SJavier Martin info->devtype = &ov7670_devdata[id->driver_data]; 1856cb7a01acSMauro Carvalho Chehab info->fmt = &ov7670_formats[0]; 18575556ab2aSLubomir Rintel info->wsize = &info->devtype->win_sizes[0]; 1858c0662dd4SWenyou Yang 1859c0662dd4SWenyou Yang ov7670_get_default_format(sd, &info->format); 1860c0662dd4SWenyou Yang 1861f6dd927fSJavier Martin info->clkrc = 0; 1862f6dd927fSJavier Martin 1863f6dd927fSJavier Martin /* Set default frame rate to 30 fps */ 1864f6dd927fSJavier Martin tpf.numerator = 1; 1865f6dd927fSJavier Martin tpf.denominator = 30; 1866f6dd927fSJavier Martin info->devtype->set_framerate(sd, &tpf); 1867f6dd927fSJavier Martin 1868492959c7SJavier Martin v4l2_ctrl_handler_init(&info->hdl, 10); 1869492959c7SJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1870492959c7SJavier Martin V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); 1871492959c7SJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1872492959c7SJavier Martin V4L2_CID_CONTRAST, 0, 127, 1, 64); 1873492959c7SJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1874492959c7SJavier Martin V4L2_CID_VFLIP, 0, 1, 1, 0); 1875492959c7SJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1876492959c7SJavier Martin V4L2_CID_HFLIP, 0, 1, 1, 0); 1877492959c7SJavier Martin info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1878492959c7SJavier Martin V4L2_CID_SATURATION, 0, 256, 1, 128); 1879492959c7SJavier Martin info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1880492959c7SJavier Martin V4L2_CID_HUE, -180, 180, 5, 0); 1881492959c7SJavier Martin info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1882492959c7SJavier Martin V4L2_CID_GAIN, 0, 255, 1, 128); 1883492959c7SJavier Martin info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1884492959c7SJavier Martin V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 1885492959c7SJavier Martin info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1886492959c7SJavier Martin V4L2_CID_EXPOSURE, 0, 65535, 1, 500); 1887492959c7SJavier Martin info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops, 1888492959c7SJavier Martin V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, 1889492959c7SJavier Martin V4L2_EXPOSURE_AUTO); 1890b48d908dSAkinobu Mita v4l2_ctrl_new_std_menu_items(&info->hdl, &ov7670_ctrl_ops, 1891b48d908dSAkinobu Mita V4L2_CID_TEST_PATTERN, 1892b48d908dSAkinobu Mita ARRAY_SIZE(ov7670_test_pattern_menu) - 1, 0, 0, 1893b48d908dSAkinobu Mita ov7670_test_pattern_menu); 1894492959c7SJavier Martin sd->ctrl_handler = &info->hdl; 1895492959c7SJavier Martin if (info->hdl.error) { 18967d1b8619SHans Verkuil ret = info->hdl.error; 1897492959c7SJavier Martin 18987d1b8619SHans Verkuil goto hdl_free; 1899492959c7SJavier Martin } 1900492959c7SJavier Martin /* 1901492959c7SJavier Martin * We have checked empirically that hw allows to read back the gain 1902492959c7SJavier Martin * value chosen by auto gain but that's not the case for auto exposure. 1903492959c7SJavier Martin */ 1904492959c7SJavier Martin v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true); 1905492959c7SJavier Martin v4l2_ctrl_auto_cluster(2, &info->auto_exposure, 1906492959c7SJavier Martin V4L2_EXPOSURE_MANUAL, false); 1907492959c7SJavier Martin v4l2_ctrl_cluster(2, &info->saturation); 1908d94a26f0SWenyou Yang 1909d94a26f0SWenyou Yang #if defined(CONFIG_MEDIA_CONTROLLER) 1910d94a26f0SWenyou Yang info->pad.flags = MEDIA_PAD_FL_SOURCE; 1911d94a26f0SWenyou Yang info->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; 1912d94a26f0SWenyou Yang ret = media_entity_pads_init(&info->sd.entity, 1, &info->pad); 1913d94a26f0SWenyou Yang if (ret < 0) 1914d94a26f0SWenyou Yang goto hdl_free; 1915d94a26f0SWenyou Yang #endif 1916d94a26f0SWenyou Yang 1917492959c7SJavier Martin v4l2_ctrl_handler_setup(&info->hdl); 1918492959c7SJavier Martin 19197d1b8619SHans Verkuil ret = v4l2_async_register_subdev(&info->sd); 19207d1b8619SHans Verkuil if (ret < 0) 1921d94a26f0SWenyou Yang goto entity_cleanup; 19227d1b8619SHans Verkuil 1923cb7a01acSMauro Carvalho Chehab return 0; 19247d1b8619SHans Verkuil 1925d94a26f0SWenyou Yang entity_cleanup: 1926d94a26f0SWenyou Yang media_entity_cleanup(&info->sd.entity); 19277d1b8619SHans Verkuil hdl_free: 19287d1b8619SHans Verkuil v4l2_ctrl_handler_free(&info->hdl); 192971862f63SWenyou Yang power_off: 193071862f63SWenyou Yang ov7670_s_power(sd, 0); 19310a024d63SHans Verkuil clk_disable: 19320a024d63SHans Verkuil clk_disable_unprepare(info->clk); 19337d1b8619SHans Verkuil return ret; 1934cb7a01acSMauro Carvalho Chehab } 1935cb7a01acSMauro Carvalho Chehab 1936cb7a01acSMauro Carvalho Chehab 1937cb7a01acSMauro Carvalho Chehab static int ov7670_remove(struct i2c_client *client) 1938cb7a01acSMauro Carvalho Chehab { 1939cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 1940492959c7SJavier Martin struct ov7670_info *info = to_state(sd); 1941cb7a01acSMauro Carvalho Chehab 1942344aa836SAkinobu Mita v4l2_async_unregister_subdev(sd); 1943492959c7SJavier Martin v4l2_ctrl_handler_free(&info->hdl); 19440a024d63SHans Verkuil clk_disable_unprepare(info->clk); 1945d94a26f0SWenyou Yang media_entity_cleanup(&info->sd.entity); 194671862f63SWenyou Yang ov7670_s_power(sd, 0); 1947cb7a01acSMauro Carvalho Chehab return 0; 1948cb7a01acSMauro Carvalho Chehab } 1949cb7a01acSMauro Carvalho Chehab 1950cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id ov7670_id[] = { 1951d058e237SJavier Martin { "ov7670", MODEL_OV7670 }, 1952d058e237SJavier Martin { "ov7675", MODEL_OV7675 }, 1953cb7a01acSMauro Carvalho Chehab { } 1954cb7a01acSMauro Carvalho Chehab }; 1955cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, ov7670_id); 1956cb7a01acSMauro Carvalho Chehab 1957a0c4164eSHans Verkuil #if IS_ENABLED(CONFIG_OF) 1958a0c4164eSHans Verkuil static const struct of_device_id ov7670_of_match[] = { 1959a0c4164eSHans Verkuil { .compatible = "ovti,ov7670", }, 1960a0c4164eSHans Verkuil { /* sentinel */ }, 1961a0c4164eSHans Verkuil }; 1962a0c4164eSHans Verkuil MODULE_DEVICE_TABLE(of, ov7670_of_match); 1963a0c4164eSHans Verkuil #endif 1964a0c4164eSHans Verkuil 1965cb7a01acSMauro Carvalho Chehab static struct i2c_driver ov7670_driver = { 1966cb7a01acSMauro Carvalho Chehab .driver = { 1967cb7a01acSMauro Carvalho Chehab .name = "ov7670", 1968a0c4164eSHans Verkuil .of_match_table = of_match_ptr(ov7670_of_match), 1969cb7a01acSMauro Carvalho Chehab }, 1970cb7a01acSMauro Carvalho Chehab .probe = ov7670_probe, 1971cb7a01acSMauro Carvalho Chehab .remove = ov7670_remove, 1972cb7a01acSMauro Carvalho Chehab .id_table = ov7670_id, 1973cb7a01acSMauro Carvalho Chehab }; 1974cb7a01acSMauro Carvalho Chehab 1975cb7a01acSMauro Carvalho Chehab module_i2c_driver(ov7670_driver); 1976