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 */ 13cb7a01acSMauro Carvalho Chehab #include <linux/init.h> 14cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 15cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 16cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 17cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 18cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 19cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 20492959c7SJavier Martin #include <media/v4l2-ctrls.h> 21cb7a01acSMauro Carvalho Chehab #include <media/v4l2-mediabus.h> 224721b3ebSAxel Lin #include <media/v4l2-image-sizes.h> 23cb7a01acSMauro Carvalho Chehab #include <media/ov7670.h> 24cb7a01acSMauro Carvalho Chehab 25cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); 26cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); 27cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 28cb7a01acSMauro Carvalho Chehab 29cb7a01acSMauro Carvalho Chehab static bool debug; 30cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644); 31cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)"); 32cb7a01acSMauro Carvalho Chehab 33cb7a01acSMauro Carvalho Chehab /* 34cb7a01acSMauro Carvalho Chehab * The 7670 sits on i2c with ID 0x42 35cb7a01acSMauro Carvalho Chehab */ 36cb7a01acSMauro Carvalho Chehab #define OV7670_I2C_ADDR 0x42 37cb7a01acSMauro Carvalho Chehab 38f6dd927fSJavier Martin #define PLL_FACTOR 4 39f6dd927fSJavier Martin 40cb7a01acSMauro Carvalho Chehab /* Registers */ 41cb7a01acSMauro Carvalho Chehab #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ 42cb7a01acSMauro Carvalho Chehab #define REG_BLUE 0x01 /* blue gain */ 43cb7a01acSMauro Carvalho Chehab #define REG_RED 0x02 /* red gain */ 44cb7a01acSMauro Carvalho Chehab #define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ 45cb7a01acSMauro Carvalho Chehab #define REG_COM1 0x04 /* Control 1 */ 46cb7a01acSMauro Carvalho Chehab #define COM1_CCIR656 0x40 /* CCIR656 enable */ 47cb7a01acSMauro Carvalho Chehab #define REG_BAVE 0x05 /* U/B Average level */ 48cb7a01acSMauro Carvalho Chehab #define REG_GbAVE 0x06 /* Y/Gb Average level */ 49cb7a01acSMauro Carvalho Chehab #define REG_AECHH 0x07 /* AEC MS 5 bits */ 50cb7a01acSMauro Carvalho Chehab #define REG_RAVE 0x08 /* V/R Average level */ 51cb7a01acSMauro Carvalho Chehab #define REG_COM2 0x09 /* Control 2 */ 52cb7a01acSMauro Carvalho Chehab #define COM2_SSLEEP 0x10 /* Soft sleep mode */ 53cb7a01acSMauro Carvalho Chehab #define REG_PID 0x0a /* Product ID MSB */ 54cb7a01acSMauro Carvalho Chehab #define REG_VER 0x0b /* Product ID LSB */ 55cb7a01acSMauro Carvalho Chehab #define REG_COM3 0x0c /* Control 3 */ 56cb7a01acSMauro Carvalho Chehab #define COM3_SWAP 0x40 /* Byte swap */ 57cb7a01acSMauro Carvalho Chehab #define COM3_SCALEEN 0x08 /* Enable scaling */ 58cb7a01acSMauro Carvalho Chehab #define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ 59cb7a01acSMauro Carvalho Chehab #define REG_COM4 0x0d /* Control 4 */ 60cb7a01acSMauro Carvalho Chehab #define REG_COM5 0x0e /* All "reserved" */ 61cb7a01acSMauro Carvalho Chehab #define REG_COM6 0x0f /* Control 6 */ 62cb7a01acSMauro Carvalho Chehab #define REG_AECH 0x10 /* More bits of AEC value */ 63cb7a01acSMauro Carvalho Chehab #define REG_CLKRC 0x11 /* Clocl control */ 64cb7a01acSMauro Carvalho Chehab #define CLK_EXT 0x40 /* Use external clock directly */ 65cb7a01acSMauro Carvalho Chehab #define CLK_SCALE 0x3f /* Mask for internal clock scale */ 66cb7a01acSMauro Carvalho Chehab #define REG_COM7 0x12 /* Control 7 */ 67cb7a01acSMauro Carvalho Chehab #define COM7_RESET 0x80 /* Register reset */ 68cb7a01acSMauro Carvalho Chehab #define COM7_FMT_MASK 0x38 69cb7a01acSMauro Carvalho Chehab #define COM7_FMT_VGA 0x00 70cb7a01acSMauro Carvalho Chehab #define COM7_FMT_CIF 0x20 /* CIF format */ 71cb7a01acSMauro Carvalho Chehab #define COM7_FMT_QVGA 0x10 /* QVGA format */ 72cb7a01acSMauro Carvalho Chehab #define COM7_FMT_QCIF 0x08 /* QCIF format */ 73cb7a01acSMauro Carvalho Chehab #define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ 74cb7a01acSMauro Carvalho Chehab #define COM7_YUV 0x00 /* YUV */ 75cb7a01acSMauro Carvalho Chehab #define COM7_BAYER 0x01 /* Bayer format */ 76cb7a01acSMauro Carvalho Chehab #define COM7_PBAYER 0x05 /* "Processed bayer" */ 77cb7a01acSMauro Carvalho Chehab #define REG_COM8 0x13 /* Control 8 */ 78cb7a01acSMauro Carvalho Chehab #define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ 79cb7a01acSMauro Carvalho Chehab #define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ 80cb7a01acSMauro Carvalho Chehab #define COM8_BFILT 0x20 /* Band filter enable */ 81cb7a01acSMauro Carvalho Chehab #define COM8_AGC 0x04 /* Auto gain enable */ 82cb7a01acSMauro Carvalho Chehab #define COM8_AWB 0x02 /* White balance enable */ 83cb7a01acSMauro Carvalho Chehab #define COM8_AEC 0x01 /* Auto exposure enable */ 84cb7a01acSMauro Carvalho Chehab #define REG_COM9 0x14 /* Control 9 - gain ceiling */ 85cb7a01acSMauro Carvalho Chehab #define REG_COM10 0x15 /* Control 10 */ 86cb7a01acSMauro Carvalho Chehab #define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ 87cb7a01acSMauro Carvalho Chehab #define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ 88cb7a01acSMauro Carvalho Chehab #define COM10_HREF_REV 0x08 /* Reverse HREF */ 89cb7a01acSMauro Carvalho Chehab #define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ 90cb7a01acSMauro Carvalho Chehab #define COM10_VS_NEG 0x02 /* VSYNC negative */ 91cb7a01acSMauro Carvalho Chehab #define COM10_HS_NEG 0x01 /* HSYNC negative */ 92cb7a01acSMauro Carvalho Chehab #define REG_HSTART 0x17 /* Horiz start high bits */ 93cb7a01acSMauro Carvalho Chehab #define REG_HSTOP 0x18 /* Horiz stop high bits */ 94cb7a01acSMauro Carvalho Chehab #define REG_VSTART 0x19 /* Vert start high bits */ 95cb7a01acSMauro Carvalho Chehab #define REG_VSTOP 0x1a /* Vert stop high bits */ 96cb7a01acSMauro Carvalho Chehab #define REG_PSHFT 0x1b /* Pixel delay after HREF */ 97cb7a01acSMauro Carvalho Chehab #define REG_MIDH 0x1c /* Manuf. ID high */ 98cb7a01acSMauro Carvalho Chehab #define REG_MIDL 0x1d /* Manuf. ID low */ 99cb7a01acSMauro Carvalho Chehab #define REG_MVFP 0x1e /* Mirror / vflip */ 100cb7a01acSMauro Carvalho Chehab #define MVFP_MIRROR 0x20 /* Mirror image */ 101cb7a01acSMauro Carvalho Chehab #define MVFP_FLIP 0x10 /* Vertical flip */ 102cb7a01acSMauro Carvalho Chehab 103cb7a01acSMauro Carvalho Chehab #define REG_AEW 0x24 /* AGC upper limit */ 104cb7a01acSMauro Carvalho Chehab #define REG_AEB 0x25 /* AGC lower limit */ 105cb7a01acSMauro Carvalho Chehab #define REG_VPT 0x26 /* AGC/AEC fast mode op region */ 106cb7a01acSMauro Carvalho Chehab #define REG_HSYST 0x30 /* HSYNC rising edge delay */ 107cb7a01acSMauro Carvalho Chehab #define REG_HSYEN 0x31 /* HSYNC falling edge delay */ 108cb7a01acSMauro Carvalho Chehab #define REG_HREF 0x32 /* HREF pieces */ 109cb7a01acSMauro Carvalho Chehab #define REG_TSLB 0x3a /* lots of stuff */ 110cb7a01acSMauro Carvalho Chehab #define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ 111cb7a01acSMauro Carvalho Chehab #define REG_COM11 0x3b /* Control 11 */ 112cb7a01acSMauro Carvalho Chehab #define COM11_NIGHT 0x80 /* NIght mode enable */ 113cb7a01acSMauro Carvalho Chehab #define COM11_NMFR 0x60 /* Two bit NM frame rate */ 114cb7a01acSMauro Carvalho Chehab #define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ 115cb7a01acSMauro Carvalho Chehab #define COM11_50HZ 0x08 /* Manual 50Hz select */ 116cb7a01acSMauro Carvalho Chehab #define COM11_EXP 0x02 117cb7a01acSMauro Carvalho Chehab #define REG_COM12 0x3c /* Control 12 */ 118cb7a01acSMauro Carvalho Chehab #define COM12_HREF 0x80 /* HREF always */ 119cb7a01acSMauro Carvalho Chehab #define REG_COM13 0x3d /* Control 13 */ 120cb7a01acSMauro Carvalho Chehab #define COM13_GAMMA 0x80 /* Gamma enable */ 121cb7a01acSMauro Carvalho Chehab #define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ 122cb7a01acSMauro Carvalho Chehab #define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ 123cb7a01acSMauro Carvalho Chehab #define REG_COM14 0x3e /* Control 14 */ 124cb7a01acSMauro Carvalho Chehab #define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ 125cb7a01acSMauro Carvalho Chehab #define REG_EDGE 0x3f /* Edge enhancement factor */ 126cb7a01acSMauro Carvalho Chehab #define REG_COM15 0x40 /* Control 15 */ 127cb7a01acSMauro Carvalho Chehab #define COM15_R10F0 0x00 /* Data range 10 to F0 */ 128cb7a01acSMauro Carvalho Chehab #define COM15_R01FE 0x80 /* 01 to FE */ 129cb7a01acSMauro Carvalho Chehab #define COM15_R00FF 0xc0 /* 00 to FF */ 130cb7a01acSMauro Carvalho Chehab #define COM15_RGB565 0x10 /* RGB565 output */ 131cb7a01acSMauro Carvalho Chehab #define COM15_RGB555 0x30 /* RGB555 output */ 132cb7a01acSMauro Carvalho Chehab #define REG_COM16 0x41 /* Control 16 */ 133cb7a01acSMauro Carvalho Chehab #define COM16_AWBGAIN 0x08 /* AWB gain enable */ 134cb7a01acSMauro Carvalho Chehab #define REG_COM17 0x42 /* Control 17 */ 135cb7a01acSMauro Carvalho Chehab #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ 136cb7a01acSMauro Carvalho Chehab #define COM17_CBAR 0x08 /* DSP Color bar */ 137cb7a01acSMauro Carvalho Chehab 138cb7a01acSMauro Carvalho Chehab /* 139cb7a01acSMauro Carvalho Chehab * This matrix defines how the colors are generated, must be 140cb7a01acSMauro Carvalho Chehab * tweaked to adjust hue and saturation. 141cb7a01acSMauro Carvalho Chehab * 142cb7a01acSMauro Carvalho Chehab * Order: v-red, v-green, v-blue, u-red, u-green, u-blue 143cb7a01acSMauro Carvalho Chehab * 144cb7a01acSMauro Carvalho Chehab * They are nine-bit signed quantities, with the sign bit 145cb7a01acSMauro Carvalho Chehab * stored in 0x58. Sign for v-red is bit 0, and up from there. 146cb7a01acSMauro Carvalho Chehab */ 147cb7a01acSMauro Carvalho Chehab #define REG_CMATRIX_BASE 0x4f 148cb7a01acSMauro Carvalho Chehab #define CMATRIX_LEN 6 149cb7a01acSMauro Carvalho Chehab #define REG_CMATRIX_SIGN 0x58 150cb7a01acSMauro Carvalho Chehab 151cb7a01acSMauro Carvalho Chehab 152cb7a01acSMauro Carvalho Chehab #define REG_BRIGHT 0x55 /* Brightness */ 153cb7a01acSMauro Carvalho Chehab #define REG_CONTRAS 0x56 /* Contrast control */ 154cb7a01acSMauro Carvalho Chehab 155cb7a01acSMauro Carvalho Chehab #define REG_GFIX 0x69 /* Fix gain control */ 156cb7a01acSMauro Carvalho Chehab 157f6dd927fSJavier Martin #define REG_DBLV 0x6b /* PLL control an debugging */ 158f6dd927fSJavier Martin #define DBLV_BYPASS 0x00 /* Bypass PLL */ 159f6dd927fSJavier Martin #define DBLV_X4 0x01 /* clock x4 */ 160f6dd927fSJavier Martin #define DBLV_X6 0x10 /* clock x6 */ 161f6dd927fSJavier Martin #define DBLV_X8 0x11 /* clock x8 */ 162f6dd927fSJavier Martin 163cb7a01acSMauro Carvalho Chehab #define REG_REG76 0x76 /* OV's name */ 164cb7a01acSMauro Carvalho Chehab #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ 165cb7a01acSMauro Carvalho Chehab #define R76_WHTPCOR 0x40 /* White pixel correction enable */ 166cb7a01acSMauro Carvalho Chehab 167cb7a01acSMauro Carvalho Chehab #define REG_RGB444 0x8c /* RGB 444 control */ 168cb7a01acSMauro Carvalho Chehab #define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ 169cb7a01acSMauro Carvalho Chehab #define R444_RGBX 0x01 /* Empty nibble at end */ 170cb7a01acSMauro Carvalho Chehab 171cb7a01acSMauro Carvalho Chehab #define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ 172cb7a01acSMauro Carvalho Chehab #define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ 173cb7a01acSMauro Carvalho Chehab 174cb7a01acSMauro Carvalho Chehab #define REG_BD50MAX 0xa5 /* 50hz banding step limit */ 175cb7a01acSMauro Carvalho Chehab #define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ 176cb7a01acSMauro Carvalho Chehab #define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ 177cb7a01acSMauro Carvalho Chehab #define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ 178cb7a01acSMauro Carvalho Chehab #define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ 179cb7a01acSMauro Carvalho Chehab #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ 180cb7a01acSMauro Carvalho Chehab #define REG_BD60MAX 0xab /* 60hz banding step limit */ 181cb7a01acSMauro Carvalho Chehab 182d058e237SJavier Martin enum ov7670_model { 183d058e237SJavier Martin MODEL_OV7670 = 0, 184d058e237SJavier Martin MODEL_OV7675, 185d058e237SJavier Martin }; 186d058e237SJavier Martin 187d058e237SJavier Martin struct ov7670_win_size { 188d058e237SJavier Martin int width; 189d058e237SJavier Martin int height; 190d058e237SJavier Martin unsigned char com7_bit; 191d058e237SJavier Martin int hstart; /* Start/stop values for the camera. Note */ 192d058e237SJavier Martin int hstop; /* that they do not always make complete */ 193d058e237SJavier Martin int vstart; /* sense to humans, but evidently the sensor */ 194d058e237SJavier Martin int vstop; /* will do the right thing... */ 195d058e237SJavier Martin struct regval_list *regs; /* Regs to tweak */ 196d058e237SJavier Martin }; 197d058e237SJavier Martin 198d058e237SJavier Martin struct ov7670_devtype { 199d058e237SJavier Martin /* formats supported for each model */ 200d058e237SJavier Martin struct ov7670_win_size *win_sizes; 201d058e237SJavier Martin unsigned int n_win_sizes; 202f6dd927fSJavier Martin /* callbacks for frame rate control */ 203f6dd927fSJavier Martin int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *); 204f6dd927fSJavier Martin void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *); 205d058e237SJavier Martin }; 206cb7a01acSMauro Carvalho Chehab 207cb7a01acSMauro Carvalho Chehab /* 208cb7a01acSMauro Carvalho Chehab * Information we maintain about a known sensor. 209cb7a01acSMauro Carvalho Chehab */ 210cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct; /* coming later */ 211cb7a01acSMauro Carvalho Chehab struct ov7670_info { 212cb7a01acSMauro Carvalho Chehab struct v4l2_subdev sd; 213492959c7SJavier Martin struct v4l2_ctrl_handler hdl; 214492959c7SJavier Martin struct { 215492959c7SJavier Martin /* gain cluster */ 216492959c7SJavier Martin struct v4l2_ctrl *auto_gain; 217492959c7SJavier Martin struct v4l2_ctrl *gain; 218492959c7SJavier Martin }; 219492959c7SJavier Martin struct { 220492959c7SJavier Martin /* exposure cluster */ 221492959c7SJavier Martin struct v4l2_ctrl *auto_exposure; 222492959c7SJavier Martin struct v4l2_ctrl *exposure; 223492959c7SJavier Martin }; 224492959c7SJavier Martin struct { 225492959c7SJavier Martin /* saturation/hue cluster */ 226492959c7SJavier Martin struct v4l2_ctrl *saturation; 227492959c7SJavier Martin struct v4l2_ctrl *hue; 228492959c7SJavier Martin }; 229cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct *fmt; /* Current format */ 230cb7a01acSMauro Carvalho Chehab int min_width; /* Filter out smaller sizes */ 231cb7a01acSMauro Carvalho Chehab int min_height; /* Filter out smaller sizes */ 232cb7a01acSMauro Carvalho Chehab int clock_speed; /* External clock speed (MHz) */ 233cb7a01acSMauro Carvalho Chehab u8 clkrc; /* Clock divider value */ 234cb7a01acSMauro Carvalho Chehab bool use_smbus; /* Use smbus I/O instead of I2C */ 23504ee6d92SJavier Martin bool pll_bypass; 236ee95258eSJavier Martin bool pclk_hb_disable; 237d058e237SJavier Martin const struct ov7670_devtype *devtype; /* Device specifics */ 238cb7a01acSMauro Carvalho Chehab }; 239cb7a01acSMauro Carvalho Chehab 240cb7a01acSMauro Carvalho Chehab static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) 241cb7a01acSMauro Carvalho Chehab { 242cb7a01acSMauro Carvalho Chehab return container_of(sd, struct ov7670_info, sd); 243cb7a01acSMauro Carvalho Chehab } 244cb7a01acSMauro Carvalho Chehab 245492959c7SJavier Martin static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 246492959c7SJavier Martin { 247492959c7SJavier Martin return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd; 248492959c7SJavier Martin } 249492959c7SJavier Martin 250cb7a01acSMauro Carvalho Chehab 251cb7a01acSMauro Carvalho Chehab 252cb7a01acSMauro Carvalho Chehab /* 253cb7a01acSMauro Carvalho Chehab * The default register settings, as obtained from OmniVision. There 254cb7a01acSMauro Carvalho Chehab * is really no making sense of most of these - lots of "reserved" values 255cb7a01acSMauro Carvalho Chehab * and such. 256cb7a01acSMauro Carvalho Chehab * 257cb7a01acSMauro Carvalho Chehab * These settings give VGA YUYV. 258cb7a01acSMauro Carvalho Chehab */ 259cb7a01acSMauro Carvalho Chehab 260cb7a01acSMauro Carvalho Chehab struct regval_list { 261cb7a01acSMauro Carvalho Chehab unsigned char reg_num; 262cb7a01acSMauro Carvalho Chehab unsigned char value; 263cb7a01acSMauro Carvalho Chehab }; 264cb7a01acSMauro Carvalho Chehab 265cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_default_regs[] = { 266cb7a01acSMauro Carvalho Chehab { REG_COM7, COM7_RESET }, 267cb7a01acSMauro Carvalho Chehab /* 268cb7a01acSMauro Carvalho Chehab * Clock scale: 3 = 15fps 269cb7a01acSMauro Carvalho Chehab * 2 = 20fps 270cb7a01acSMauro Carvalho Chehab * 1 = 30fps 271cb7a01acSMauro Carvalho Chehab */ 272cb7a01acSMauro Carvalho Chehab { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ 273cb7a01acSMauro Carvalho Chehab { REG_TSLB, 0x04 }, /* OV */ 274cb7a01acSMauro Carvalho Chehab { REG_COM7, 0 }, /* VGA */ 275cb7a01acSMauro Carvalho Chehab /* 276cb7a01acSMauro Carvalho Chehab * Set the hardware window. These values from OV don't entirely 277cb7a01acSMauro Carvalho Chehab * make sense - hstop is less than hstart. But they work... 278cb7a01acSMauro Carvalho Chehab */ 279cb7a01acSMauro Carvalho Chehab { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, 280cb7a01acSMauro Carvalho Chehab { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, 281cb7a01acSMauro Carvalho Chehab { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, 282cb7a01acSMauro Carvalho Chehab 283cb7a01acSMauro Carvalho Chehab { REG_COM3, 0 }, { REG_COM14, 0 }, 284cb7a01acSMauro Carvalho Chehab /* Mystery scaling numbers */ 285cb7a01acSMauro Carvalho Chehab { 0x70, 0x3a }, { 0x71, 0x35 }, 286cb7a01acSMauro Carvalho Chehab { 0x72, 0x11 }, { 0x73, 0xf0 }, 287cb7a01acSMauro Carvalho Chehab { 0xa2, 0x02 }, { REG_COM10, 0x0 }, 288cb7a01acSMauro Carvalho Chehab 289cb7a01acSMauro Carvalho Chehab /* Gamma curve values */ 290cb7a01acSMauro Carvalho Chehab { 0x7a, 0x20 }, { 0x7b, 0x10 }, 291cb7a01acSMauro Carvalho Chehab { 0x7c, 0x1e }, { 0x7d, 0x35 }, 292cb7a01acSMauro Carvalho Chehab { 0x7e, 0x5a }, { 0x7f, 0x69 }, 293cb7a01acSMauro Carvalho Chehab { 0x80, 0x76 }, { 0x81, 0x80 }, 294cb7a01acSMauro Carvalho Chehab { 0x82, 0x88 }, { 0x83, 0x8f }, 295cb7a01acSMauro Carvalho Chehab { 0x84, 0x96 }, { 0x85, 0xa3 }, 296cb7a01acSMauro Carvalho Chehab { 0x86, 0xaf }, { 0x87, 0xc4 }, 297cb7a01acSMauro Carvalho Chehab { 0x88, 0xd7 }, { 0x89, 0xe8 }, 298cb7a01acSMauro Carvalho Chehab 299cb7a01acSMauro Carvalho Chehab /* AGC and AEC parameters. Note we start by disabling those features, 300cb7a01acSMauro Carvalho Chehab then turn them only after tweaking the values. */ 301cb7a01acSMauro Carvalho Chehab { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, 302cb7a01acSMauro Carvalho Chehab { REG_GAIN, 0 }, { REG_AECH, 0 }, 303cb7a01acSMauro Carvalho Chehab { REG_COM4, 0x40 }, /* magic reserved bit */ 304cb7a01acSMauro Carvalho Chehab { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ 305cb7a01acSMauro Carvalho Chehab { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, 306cb7a01acSMauro Carvalho Chehab { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, 307cb7a01acSMauro Carvalho Chehab { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, 308cb7a01acSMauro Carvalho Chehab { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ 309cb7a01acSMauro Carvalho Chehab { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, 310cb7a01acSMauro Carvalho Chehab { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, 311cb7a01acSMauro Carvalho Chehab { REG_HAECC7, 0x94 }, 312cb7a01acSMauro Carvalho Chehab { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, 313cb7a01acSMauro Carvalho Chehab 314cb7a01acSMauro Carvalho Chehab /* Almost all of these are magic "reserved" values. */ 315cb7a01acSMauro Carvalho Chehab { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, 316cb7a01acSMauro Carvalho Chehab { 0x16, 0x02 }, { REG_MVFP, 0x07 }, 317cb7a01acSMauro Carvalho Chehab { 0x21, 0x02 }, { 0x22, 0x91 }, 318cb7a01acSMauro Carvalho Chehab { 0x29, 0x07 }, { 0x33, 0x0b }, 319cb7a01acSMauro Carvalho Chehab { 0x35, 0x0b }, { 0x37, 0x1d }, 320cb7a01acSMauro Carvalho Chehab { 0x38, 0x71 }, { 0x39, 0x2a }, 321cb7a01acSMauro Carvalho Chehab { REG_COM12, 0x78 }, { 0x4d, 0x40 }, 322cb7a01acSMauro Carvalho Chehab { 0x4e, 0x20 }, { REG_GFIX, 0 }, 323cb7a01acSMauro Carvalho Chehab { 0x6b, 0x4a }, { 0x74, 0x10 }, 324cb7a01acSMauro Carvalho Chehab { 0x8d, 0x4f }, { 0x8e, 0 }, 325cb7a01acSMauro Carvalho Chehab { 0x8f, 0 }, { 0x90, 0 }, 326cb7a01acSMauro Carvalho Chehab { 0x91, 0 }, { 0x96, 0 }, 327cb7a01acSMauro Carvalho Chehab { 0x9a, 0 }, { 0xb0, 0x84 }, 328cb7a01acSMauro Carvalho Chehab { 0xb1, 0x0c }, { 0xb2, 0x0e }, 329cb7a01acSMauro Carvalho Chehab { 0xb3, 0x82 }, { 0xb8, 0x0a }, 330cb7a01acSMauro Carvalho Chehab 331cb7a01acSMauro Carvalho Chehab /* More reserved magic, some of which tweaks white balance */ 332cb7a01acSMauro Carvalho Chehab { 0x43, 0x0a }, { 0x44, 0xf0 }, 333cb7a01acSMauro Carvalho Chehab { 0x45, 0x34 }, { 0x46, 0x58 }, 334cb7a01acSMauro Carvalho Chehab { 0x47, 0x28 }, { 0x48, 0x3a }, 335cb7a01acSMauro Carvalho Chehab { 0x59, 0x88 }, { 0x5a, 0x88 }, 336cb7a01acSMauro Carvalho Chehab { 0x5b, 0x44 }, { 0x5c, 0x67 }, 337cb7a01acSMauro Carvalho Chehab { 0x5d, 0x49 }, { 0x5e, 0x0e }, 338cb7a01acSMauro Carvalho Chehab { 0x6c, 0x0a }, { 0x6d, 0x55 }, 339cb7a01acSMauro Carvalho Chehab { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ 340cb7a01acSMauro Carvalho Chehab { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, 341cb7a01acSMauro Carvalho Chehab { REG_RED, 0x60 }, 342cb7a01acSMauro Carvalho Chehab { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, 343cb7a01acSMauro Carvalho Chehab 344cb7a01acSMauro Carvalho Chehab /* Matrix coefficients */ 345cb7a01acSMauro Carvalho Chehab { 0x4f, 0x80 }, { 0x50, 0x80 }, 346cb7a01acSMauro Carvalho Chehab { 0x51, 0 }, { 0x52, 0x22 }, 347cb7a01acSMauro Carvalho Chehab { 0x53, 0x5e }, { 0x54, 0x80 }, 348cb7a01acSMauro Carvalho Chehab { 0x58, 0x9e }, 349cb7a01acSMauro Carvalho Chehab 350cb7a01acSMauro Carvalho Chehab { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, 351cb7a01acSMauro Carvalho Chehab { 0x75, 0x05 }, { 0x76, 0xe1 }, 352cb7a01acSMauro Carvalho Chehab { 0x4c, 0 }, { 0x77, 0x01 }, 353cb7a01acSMauro Carvalho Chehab { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, 354cb7a01acSMauro Carvalho Chehab { 0xc9, 0x60 }, { REG_COM16, 0x38 }, 355cb7a01acSMauro Carvalho Chehab { 0x56, 0x40 }, 356cb7a01acSMauro Carvalho Chehab 357cb7a01acSMauro Carvalho Chehab { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, 358cb7a01acSMauro Carvalho Chehab { 0xa4, 0x88 }, { 0x96, 0 }, 359cb7a01acSMauro Carvalho Chehab { 0x97, 0x30 }, { 0x98, 0x20 }, 360cb7a01acSMauro Carvalho Chehab { 0x99, 0x30 }, { 0x9a, 0x84 }, 361cb7a01acSMauro Carvalho Chehab { 0x9b, 0x29 }, { 0x9c, 0x03 }, 362cb7a01acSMauro Carvalho Chehab { 0x9d, 0x4c }, { 0x9e, 0x3f }, 363cb7a01acSMauro Carvalho Chehab { 0x78, 0x04 }, 364cb7a01acSMauro Carvalho Chehab 365cb7a01acSMauro Carvalho Chehab /* Extra-weird stuff. Some sort of multiplexor register */ 366cb7a01acSMauro Carvalho Chehab { 0x79, 0x01 }, { 0xc8, 0xf0 }, 367cb7a01acSMauro Carvalho Chehab { 0x79, 0x0f }, { 0xc8, 0x00 }, 368cb7a01acSMauro Carvalho Chehab { 0x79, 0x10 }, { 0xc8, 0x7e }, 369cb7a01acSMauro Carvalho Chehab { 0x79, 0x0a }, { 0xc8, 0x80 }, 370cb7a01acSMauro Carvalho Chehab { 0x79, 0x0b }, { 0xc8, 0x01 }, 371cb7a01acSMauro Carvalho Chehab { 0x79, 0x0c }, { 0xc8, 0x0f }, 372cb7a01acSMauro Carvalho Chehab { 0x79, 0x0d }, { 0xc8, 0x20 }, 373cb7a01acSMauro Carvalho Chehab { 0x79, 0x09 }, { 0xc8, 0x80 }, 374cb7a01acSMauro Carvalho Chehab { 0x79, 0x02 }, { 0xc8, 0xc0 }, 375cb7a01acSMauro Carvalho Chehab { 0x79, 0x03 }, { 0xc8, 0x40 }, 376cb7a01acSMauro Carvalho Chehab { 0x79, 0x05 }, { 0xc8, 0x30 }, 377cb7a01acSMauro Carvalho Chehab { 0x79, 0x26 }, 378cb7a01acSMauro Carvalho Chehab 379cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, /* END MARKER */ 380cb7a01acSMauro Carvalho Chehab }; 381cb7a01acSMauro Carvalho Chehab 382cb7a01acSMauro Carvalho Chehab 383cb7a01acSMauro Carvalho Chehab /* 384cb7a01acSMauro Carvalho Chehab * Here we'll try to encapsulate the changes for just the output 385cb7a01acSMauro Carvalho Chehab * video format. 386cb7a01acSMauro Carvalho Chehab * 387cb7a01acSMauro Carvalho Chehab * RGB656 and YUV422 come from OV; RGB444 is homebrewed. 388cb7a01acSMauro Carvalho Chehab * 389cb7a01acSMauro Carvalho Chehab * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. 390cb7a01acSMauro Carvalho Chehab */ 391cb7a01acSMauro Carvalho Chehab 392cb7a01acSMauro Carvalho Chehab 393cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_yuv422[] = { 394cb7a01acSMauro Carvalho Chehab { REG_COM7, 0x0 }, /* Selects YUV mode */ 395cb7a01acSMauro Carvalho Chehab { REG_RGB444, 0 }, /* No RGB444 please */ 396cb7a01acSMauro Carvalho Chehab { REG_COM1, 0 }, /* CCIR601 */ 397cb7a01acSMauro Carvalho Chehab { REG_COM15, COM15_R00FF }, 398c01b7429SJavier Martin { REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ 399cb7a01acSMauro Carvalho Chehab { 0x4f, 0x80 }, /* "matrix coefficient 1" */ 400cb7a01acSMauro Carvalho Chehab { 0x50, 0x80 }, /* "matrix coefficient 2" */ 401cb7a01acSMauro Carvalho Chehab { 0x51, 0 }, /* vb */ 402cb7a01acSMauro Carvalho Chehab { 0x52, 0x22 }, /* "matrix coefficient 4" */ 403cb7a01acSMauro Carvalho Chehab { 0x53, 0x5e }, /* "matrix coefficient 5" */ 404cb7a01acSMauro Carvalho Chehab { 0x54, 0x80 }, /* "matrix coefficient 6" */ 405cb7a01acSMauro Carvalho Chehab { REG_COM13, COM13_GAMMA|COM13_UVSAT }, 406cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 407cb7a01acSMauro Carvalho Chehab }; 408cb7a01acSMauro Carvalho Chehab 409cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb565[] = { 410cb7a01acSMauro Carvalho Chehab { REG_COM7, COM7_RGB }, /* Selects RGB mode */ 411cb7a01acSMauro Carvalho Chehab { REG_RGB444, 0 }, /* No RGB444 please */ 412cb7a01acSMauro Carvalho Chehab { REG_COM1, 0x0 }, /* CCIR601 */ 413cb7a01acSMauro Carvalho Chehab { REG_COM15, COM15_RGB565 }, 414cb7a01acSMauro Carvalho Chehab { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ 415cb7a01acSMauro Carvalho Chehab { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ 416cb7a01acSMauro Carvalho Chehab { 0x50, 0xb3 }, /* "matrix coefficient 2" */ 417cb7a01acSMauro Carvalho Chehab { 0x51, 0 }, /* vb */ 418cb7a01acSMauro Carvalho Chehab { 0x52, 0x3d }, /* "matrix coefficient 4" */ 419cb7a01acSMauro Carvalho Chehab { 0x53, 0xa7 }, /* "matrix coefficient 5" */ 420cb7a01acSMauro Carvalho Chehab { 0x54, 0xe4 }, /* "matrix coefficient 6" */ 421cb7a01acSMauro Carvalho Chehab { REG_COM13, COM13_GAMMA|COM13_UVSAT }, 422cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 423cb7a01acSMauro Carvalho Chehab }; 424cb7a01acSMauro Carvalho Chehab 425cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb444[] = { 426cb7a01acSMauro Carvalho Chehab { REG_COM7, COM7_RGB }, /* Selects RGB mode */ 427cb7a01acSMauro Carvalho Chehab { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ 428cb7a01acSMauro Carvalho Chehab { REG_COM1, 0x0 }, /* CCIR601 */ 429cb7a01acSMauro Carvalho Chehab { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ 430cb7a01acSMauro Carvalho Chehab { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ 431cb7a01acSMauro Carvalho Chehab { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ 432cb7a01acSMauro Carvalho Chehab { 0x50, 0xb3 }, /* "matrix coefficient 2" */ 433cb7a01acSMauro Carvalho Chehab { 0x51, 0 }, /* vb */ 434cb7a01acSMauro Carvalho Chehab { 0x52, 0x3d }, /* "matrix coefficient 4" */ 435cb7a01acSMauro Carvalho Chehab { 0x53, 0xa7 }, /* "matrix coefficient 5" */ 436cb7a01acSMauro Carvalho Chehab { 0x54, 0xe4 }, /* "matrix coefficient 6" */ 437cb7a01acSMauro Carvalho Chehab { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ 438cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 439cb7a01acSMauro Carvalho Chehab }; 440cb7a01acSMauro Carvalho Chehab 441cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_raw[] = { 442cb7a01acSMauro Carvalho Chehab { REG_COM7, COM7_BAYER }, 443cb7a01acSMauro Carvalho Chehab { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ 444cb7a01acSMauro Carvalho Chehab { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ 445cb7a01acSMauro Carvalho Chehab { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ 446cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 447cb7a01acSMauro Carvalho Chehab }; 448cb7a01acSMauro Carvalho Chehab 449cb7a01acSMauro Carvalho Chehab 450cb7a01acSMauro Carvalho Chehab 451cb7a01acSMauro Carvalho Chehab /* 452cb7a01acSMauro Carvalho Chehab * Low-level register I/O. 453cb7a01acSMauro Carvalho Chehab * 454cb7a01acSMauro Carvalho Chehab * Note that there are two versions of these. On the XO 1, the 455cb7a01acSMauro Carvalho Chehab * i2c controller only does SMBUS, so that's what we use. The 456cb7a01acSMauro Carvalho Chehab * ov7670 is not really an SMBUS device, though, so the communication 457cb7a01acSMauro Carvalho Chehab * is not always entirely reliable. 458cb7a01acSMauro Carvalho Chehab */ 459cb7a01acSMauro Carvalho Chehab static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, 460cb7a01acSMauro Carvalho Chehab unsigned char *value) 461cb7a01acSMauro Carvalho Chehab { 462cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 463cb7a01acSMauro Carvalho Chehab int ret; 464cb7a01acSMauro Carvalho Chehab 465cb7a01acSMauro Carvalho Chehab ret = i2c_smbus_read_byte_data(client, reg); 466cb7a01acSMauro Carvalho Chehab if (ret >= 0) { 467cb7a01acSMauro Carvalho Chehab *value = (unsigned char)ret; 468cb7a01acSMauro Carvalho Chehab ret = 0; 469cb7a01acSMauro Carvalho Chehab } 470cb7a01acSMauro Carvalho Chehab return ret; 471cb7a01acSMauro Carvalho Chehab } 472cb7a01acSMauro Carvalho Chehab 473cb7a01acSMauro Carvalho Chehab 474cb7a01acSMauro Carvalho Chehab static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, 475cb7a01acSMauro Carvalho Chehab unsigned char value) 476cb7a01acSMauro Carvalho Chehab { 477cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 478cb7a01acSMauro Carvalho Chehab int ret = i2c_smbus_write_byte_data(client, reg, value); 479cb7a01acSMauro Carvalho Chehab 480cb7a01acSMauro Carvalho Chehab if (reg == REG_COM7 && (value & COM7_RESET)) 481cb7a01acSMauro Carvalho Chehab msleep(5); /* Wait for reset to run */ 482cb7a01acSMauro Carvalho Chehab return ret; 483cb7a01acSMauro Carvalho Chehab } 484cb7a01acSMauro Carvalho Chehab 485cb7a01acSMauro Carvalho Chehab /* 486cb7a01acSMauro Carvalho Chehab * On most platforms, we'd rather do straight i2c I/O. 487cb7a01acSMauro Carvalho Chehab */ 488cb7a01acSMauro Carvalho Chehab static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, 489cb7a01acSMauro Carvalho Chehab unsigned char *value) 490cb7a01acSMauro Carvalho Chehab { 491cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 492cb7a01acSMauro Carvalho Chehab u8 data = reg; 493cb7a01acSMauro Carvalho Chehab struct i2c_msg msg; 494cb7a01acSMauro Carvalho Chehab int ret; 495cb7a01acSMauro Carvalho Chehab 496cb7a01acSMauro Carvalho Chehab /* 497cb7a01acSMauro Carvalho Chehab * Send out the register address... 498cb7a01acSMauro Carvalho Chehab */ 499cb7a01acSMauro Carvalho Chehab msg.addr = client->addr; 500cb7a01acSMauro Carvalho Chehab msg.flags = 0; 501cb7a01acSMauro Carvalho Chehab msg.len = 1; 502cb7a01acSMauro Carvalho Chehab msg.buf = &data; 503cb7a01acSMauro Carvalho Chehab ret = i2c_transfer(client->adapter, &msg, 1); 504cb7a01acSMauro Carvalho Chehab if (ret < 0) { 505cb7a01acSMauro Carvalho Chehab printk(KERN_ERR "Error %d on register write\n", ret); 506cb7a01acSMauro Carvalho Chehab return ret; 507cb7a01acSMauro Carvalho Chehab } 508cb7a01acSMauro Carvalho Chehab /* 509cb7a01acSMauro Carvalho Chehab * ...then read back the result. 510cb7a01acSMauro Carvalho Chehab */ 511cb7a01acSMauro Carvalho Chehab msg.flags = I2C_M_RD; 512cb7a01acSMauro Carvalho Chehab ret = i2c_transfer(client->adapter, &msg, 1); 513cb7a01acSMauro Carvalho Chehab if (ret >= 0) { 514cb7a01acSMauro Carvalho Chehab *value = data; 515cb7a01acSMauro Carvalho Chehab ret = 0; 516cb7a01acSMauro Carvalho Chehab } 517cb7a01acSMauro Carvalho Chehab return ret; 518cb7a01acSMauro Carvalho Chehab } 519cb7a01acSMauro Carvalho Chehab 520cb7a01acSMauro Carvalho Chehab 521cb7a01acSMauro Carvalho Chehab static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, 522cb7a01acSMauro Carvalho Chehab unsigned char value) 523cb7a01acSMauro Carvalho Chehab { 524cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 525cb7a01acSMauro Carvalho Chehab struct i2c_msg msg; 526cb7a01acSMauro Carvalho Chehab unsigned char data[2] = { reg, value }; 527cb7a01acSMauro Carvalho Chehab int ret; 528cb7a01acSMauro Carvalho Chehab 529cb7a01acSMauro Carvalho Chehab msg.addr = client->addr; 530cb7a01acSMauro Carvalho Chehab msg.flags = 0; 531cb7a01acSMauro Carvalho Chehab msg.len = 2; 532cb7a01acSMauro Carvalho Chehab msg.buf = data; 533cb7a01acSMauro Carvalho Chehab ret = i2c_transfer(client->adapter, &msg, 1); 534cb7a01acSMauro Carvalho Chehab if (ret > 0) 535cb7a01acSMauro Carvalho Chehab ret = 0; 536cb7a01acSMauro Carvalho Chehab if (reg == REG_COM7 && (value & COM7_RESET)) 537cb7a01acSMauro Carvalho Chehab msleep(5); /* Wait for reset to run */ 538cb7a01acSMauro Carvalho Chehab return ret; 539cb7a01acSMauro Carvalho Chehab } 540cb7a01acSMauro Carvalho Chehab 541cb7a01acSMauro Carvalho Chehab static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, 542cb7a01acSMauro Carvalho Chehab unsigned char *value) 543cb7a01acSMauro Carvalho Chehab { 544cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 545cb7a01acSMauro Carvalho Chehab if (info->use_smbus) 546cb7a01acSMauro Carvalho Chehab return ov7670_read_smbus(sd, reg, value); 547cb7a01acSMauro Carvalho Chehab else 548cb7a01acSMauro Carvalho Chehab return ov7670_read_i2c(sd, reg, value); 549cb7a01acSMauro Carvalho Chehab } 550cb7a01acSMauro Carvalho Chehab 551cb7a01acSMauro Carvalho Chehab static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, 552cb7a01acSMauro Carvalho Chehab unsigned char value) 553cb7a01acSMauro Carvalho Chehab { 554cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 555cb7a01acSMauro Carvalho Chehab if (info->use_smbus) 556cb7a01acSMauro Carvalho Chehab return ov7670_write_smbus(sd, reg, value); 557cb7a01acSMauro Carvalho Chehab else 558cb7a01acSMauro Carvalho Chehab return ov7670_write_i2c(sd, reg, value); 559cb7a01acSMauro Carvalho Chehab } 560cb7a01acSMauro Carvalho Chehab 561cb7a01acSMauro Carvalho Chehab /* 562cb7a01acSMauro Carvalho Chehab * Write a list of register settings; ff/ff stops the process. 563cb7a01acSMauro Carvalho Chehab */ 564cb7a01acSMauro Carvalho Chehab static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) 565cb7a01acSMauro Carvalho Chehab { 566cb7a01acSMauro Carvalho Chehab while (vals->reg_num != 0xff || vals->value != 0xff) { 567cb7a01acSMauro Carvalho Chehab int ret = ov7670_write(sd, vals->reg_num, vals->value); 568cb7a01acSMauro Carvalho Chehab if (ret < 0) 569cb7a01acSMauro Carvalho Chehab return ret; 570cb7a01acSMauro Carvalho Chehab vals++; 571cb7a01acSMauro Carvalho Chehab } 572cb7a01acSMauro Carvalho Chehab return 0; 573cb7a01acSMauro Carvalho Chehab } 574cb7a01acSMauro Carvalho Chehab 575cb7a01acSMauro Carvalho Chehab 576cb7a01acSMauro Carvalho Chehab /* 577cb7a01acSMauro Carvalho Chehab * Stuff that knows about the sensor. 578cb7a01acSMauro Carvalho Chehab */ 579cb7a01acSMauro Carvalho Chehab static int ov7670_reset(struct v4l2_subdev *sd, u32 val) 580cb7a01acSMauro Carvalho Chehab { 581cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_COM7, COM7_RESET); 582cb7a01acSMauro Carvalho Chehab msleep(1); 583cb7a01acSMauro Carvalho Chehab return 0; 584cb7a01acSMauro Carvalho Chehab } 585cb7a01acSMauro Carvalho Chehab 586cb7a01acSMauro Carvalho Chehab 587cb7a01acSMauro Carvalho Chehab static int ov7670_init(struct v4l2_subdev *sd, u32 val) 588cb7a01acSMauro Carvalho Chehab { 589cb7a01acSMauro Carvalho Chehab return ov7670_write_array(sd, ov7670_default_regs); 590cb7a01acSMauro Carvalho Chehab } 591cb7a01acSMauro Carvalho Chehab 592cb7a01acSMauro Carvalho Chehab 593cb7a01acSMauro Carvalho Chehab 594cb7a01acSMauro Carvalho Chehab static int ov7670_detect(struct v4l2_subdev *sd) 595cb7a01acSMauro Carvalho Chehab { 596cb7a01acSMauro Carvalho Chehab unsigned char v; 597cb7a01acSMauro Carvalho Chehab int ret; 598cb7a01acSMauro Carvalho Chehab 599cb7a01acSMauro Carvalho Chehab ret = ov7670_init(sd, 0); 600cb7a01acSMauro Carvalho Chehab if (ret < 0) 601cb7a01acSMauro Carvalho Chehab return ret; 602cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_MIDH, &v); 603cb7a01acSMauro Carvalho Chehab if (ret < 0) 604cb7a01acSMauro Carvalho Chehab return ret; 605cb7a01acSMauro Carvalho Chehab if (v != 0x7f) /* OV manuf. id. */ 606cb7a01acSMauro Carvalho Chehab return -ENODEV; 607cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_MIDL, &v); 608cb7a01acSMauro Carvalho Chehab if (ret < 0) 609cb7a01acSMauro Carvalho Chehab return ret; 610cb7a01acSMauro Carvalho Chehab if (v != 0xa2) 611cb7a01acSMauro Carvalho Chehab return -ENODEV; 612cb7a01acSMauro Carvalho Chehab /* 613cb7a01acSMauro Carvalho Chehab * OK, we know we have an OmniVision chip...but which one? 614cb7a01acSMauro Carvalho Chehab */ 615cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_PID, &v); 616cb7a01acSMauro Carvalho Chehab if (ret < 0) 617cb7a01acSMauro Carvalho Chehab return ret; 618cb7a01acSMauro Carvalho Chehab if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ 619cb7a01acSMauro Carvalho Chehab return -ENODEV; 620cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_VER, &v); 621cb7a01acSMauro Carvalho Chehab if (ret < 0) 622cb7a01acSMauro Carvalho Chehab return ret; 623cb7a01acSMauro Carvalho Chehab if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ 624cb7a01acSMauro Carvalho Chehab return -ENODEV; 625cb7a01acSMauro Carvalho Chehab return 0; 626cb7a01acSMauro Carvalho Chehab } 627cb7a01acSMauro Carvalho Chehab 628cb7a01acSMauro Carvalho Chehab 629cb7a01acSMauro Carvalho Chehab /* 630cb7a01acSMauro Carvalho Chehab * Store information about the video data format. The color matrix 631cb7a01acSMauro Carvalho Chehab * is deeply tied into the format, so keep the relevant values here. 632cb7a01acSMauro Carvalho Chehab * The magic matrix numbers come from OmniVision. 633cb7a01acSMauro Carvalho Chehab */ 634cb7a01acSMauro Carvalho Chehab static struct ov7670_format_struct { 635f5fe58fdSBoris BREZILLON u32 mbus_code; 636cb7a01acSMauro Carvalho Chehab enum v4l2_colorspace colorspace; 637cb7a01acSMauro Carvalho Chehab struct regval_list *regs; 638cb7a01acSMauro Carvalho Chehab int cmatrix[CMATRIX_LEN]; 639cb7a01acSMauro Carvalho Chehab } ov7670_formats[] = { 640cb7a01acSMauro Carvalho Chehab { 641f5fe58fdSBoris BREZILLON .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, 642cb7a01acSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_JPEG, 643cb7a01acSMauro Carvalho Chehab .regs = ov7670_fmt_yuv422, 644cb7a01acSMauro Carvalho Chehab .cmatrix = { 128, -128, 0, -34, -94, 128 }, 645cb7a01acSMauro Carvalho Chehab }, 646cb7a01acSMauro Carvalho Chehab { 647f5fe58fdSBoris BREZILLON .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, 648cb7a01acSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 649cb7a01acSMauro Carvalho Chehab .regs = ov7670_fmt_rgb444, 650cb7a01acSMauro Carvalho Chehab .cmatrix = { 179, -179, 0, -61, -176, 228 }, 651cb7a01acSMauro Carvalho Chehab }, 652cb7a01acSMauro Carvalho Chehab { 653f5fe58fdSBoris BREZILLON .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, 654cb7a01acSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 655cb7a01acSMauro Carvalho Chehab .regs = ov7670_fmt_rgb565, 656cb7a01acSMauro Carvalho Chehab .cmatrix = { 179, -179, 0, -61, -176, 228 }, 657cb7a01acSMauro Carvalho Chehab }, 658cb7a01acSMauro Carvalho Chehab { 659f5fe58fdSBoris BREZILLON .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, 660cb7a01acSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB, 661cb7a01acSMauro Carvalho Chehab .regs = ov7670_fmt_raw, 662cb7a01acSMauro Carvalho Chehab .cmatrix = { 0, 0, 0, 0, 0, 0 }, 663cb7a01acSMauro Carvalho Chehab }, 664cb7a01acSMauro Carvalho Chehab }; 665cb7a01acSMauro Carvalho Chehab #define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) 666cb7a01acSMauro Carvalho Chehab 667cb7a01acSMauro Carvalho Chehab 668cb7a01acSMauro Carvalho Chehab /* 669cb7a01acSMauro Carvalho Chehab * Then there is the issue of window sizes. Try to capture the info here. 670cb7a01acSMauro Carvalho Chehab */ 671cb7a01acSMauro Carvalho Chehab 672cb7a01acSMauro Carvalho Chehab /* 673cb7a01acSMauro Carvalho Chehab * QCIF mode is done (by OV) in a very strange way - it actually looks like 674cb7a01acSMauro Carvalho Chehab * VGA with weird scaling options - they do *not* use the canned QCIF mode 675cb7a01acSMauro Carvalho Chehab * which is allegedly provided by the sensor. So here's the weird register 676cb7a01acSMauro Carvalho Chehab * settings. 677cb7a01acSMauro Carvalho Chehab */ 678cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_qcif_regs[] = { 679cb7a01acSMauro Carvalho Chehab { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, 680cb7a01acSMauro Carvalho Chehab { REG_COM3, COM3_DCWEN }, 681cb7a01acSMauro Carvalho Chehab { REG_COM14, COM14_DCWEN | 0x01}, 682cb7a01acSMauro Carvalho Chehab { 0x73, 0xf1 }, 683cb7a01acSMauro Carvalho Chehab { 0xa2, 0x52 }, 684cb7a01acSMauro Carvalho Chehab { 0x7b, 0x1c }, 685cb7a01acSMauro Carvalho Chehab { 0x7c, 0x28 }, 686cb7a01acSMauro Carvalho Chehab { 0x7d, 0x3c }, 687cb7a01acSMauro Carvalho Chehab { 0x7f, 0x69 }, 688cb7a01acSMauro Carvalho Chehab { REG_COM9, 0x38 }, 689cb7a01acSMauro Carvalho Chehab { 0xa1, 0x0b }, 690cb7a01acSMauro Carvalho Chehab { 0x74, 0x19 }, 691cb7a01acSMauro Carvalho Chehab { 0x9a, 0x80 }, 692cb7a01acSMauro Carvalho Chehab { 0x43, 0x14 }, 693cb7a01acSMauro Carvalho Chehab { REG_COM13, 0xc0 }, 694cb7a01acSMauro Carvalho Chehab { 0xff, 0xff }, 695cb7a01acSMauro Carvalho Chehab }; 696cb7a01acSMauro Carvalho Chehab 697d058e237SJavier Martin static struct ov7670_win_size ov7670_win_sizes[] = { 698cb7a01acSMauro Carvalho Chehab /* VGA */ 699cb7a01acSMauro Carvalho Chehab { 700cb7a01acSMauro Carvalho Chehab .width = VGA_WIDTH, 701cb7a01acSMauro Carvalho Chehab .height = VGA_HEIGHT, 702cb7a01acSMauro Carvalho Chehab .com7_bit = COM7_FMT_VGA, 703cb7a01acSMauro Carvalho Chehab .hstart = 158, /* These values from */ 704cb7a01acSMauro Carvalho Chehab .hstop = 14, /* Omnivision */ 705cb7a01acSMauro Carvalho Chehab .vstart = 10, 706cb7a01acSMauro Carvalho Chehab .vstop = 490, 707cb7a01acSMauro Carvalho Chehab .regs = NULL, 708cb7a01acSMauro Carvalho Chehab }, 709cb7a01acSMauro Carvalho Chehab /* CIF */ 710cb7a01acSMauro Carvalho Chehab { 711cb7a01acSMauro Carvalho Chehab .width = CIF_WIDTH, 712cb7a01acSMauro Carvalho Chehab .height = CIF_HEIGHT, 713cb7a01acSMauro Carvalho Chehab .com7_bit = COM7_FMT_CIF, 714cb7a01acSMauro Carvalho Chehab .hstart = 170, /* Empirically determined */ 715cb7a01acSMauro Carvalho Chehab .hstop = 90, 716cb7a01acSMauro Carvalho Chehab .vstart = 14, 717cb7a01acSMauro Carvalho Chehab .vstop = 494, 718cb7a01acSMauro Carvalho Chehab .regs = NULL, 719cb7a01acSMauro Carvalho Chehab }, 720cb7a01acSMauro Carvalho Chehab /* QVGA */ 721cb7a01acSMauro Carvalho Chehab { 722cb7a01acSMauro Carvalho Chehab .width = QVGA_WIDTH, 723cb7a01acSMauro Carvalho Chehab .height = QVGA_HEIGHT, 724cb7a01acSMauro Carvalho Chehab .com7_bit = COM7_FMT_QVGA, 725cb7a01acSMauro Carvalho Chehab .hstart = 168, /* Empirically determined */ 726cb7a01acSMauro Carvalho Chehab .hstop = 24, 727cb7a01acSMauro Carvalho Chehab .vstart = 12, 728cb7a01acSMauro Carvalho Chehab .vstop = 492, 729cb7a01acSMauro Carvalho Chehab .regs = NULL, 730cb7a01acSMauro Carvalho Chehab }, 731cb7a01acSMauro Carvalho Chehab /* QCIF */ 732cb7a01acSMauro Carvalho Chehab { 733cb7a01acSMauro Carvalho Chehab .width = QCIF_WIDTH, 734cb7a01acSMauro Carvalho Chehab .height = QCIF_HEIGHT, 735cb7a01acSMauro Carvalho Chehab .com7_bit = COM7_FMT_VGA, /* see comment above */ 736cb7a01acSMauro Carvalho Chehab .hstart = 456, /* Empirically determined */ 737cb7a01acSMauro Carvalho Chehab .hstop = 24, 738cb7a01acSMauro Carvalho Chehab .vstart = 14, 739cb7a01acSMauro Carvalho Chehab .vstop = 494, 740cb7a01acSMauro Carvalho Chehab .regs = ov7670_qcif_regs, 741d058e237SJavier Martin } 742cb7a01acSMauro Carvalho Chehab }; 743cb7a01acSMauro Carvalho Chehab 744d058e237SJavier Martin static struct ov7670_win_size ov7675_win_sizes[] = { 745d058e237SJavier Martin /* 746d058e237SJavier Martin * Currently, only VGA is supported. Theoretically it could be possible 747d058e237SJavier Martin * to support CIF, QVGA and QCIF too. Taking values for ov7670 as a 748d058e237SJavier Martin * base and tweak them empirically could be required. 749d058e237SJavier Martin */ 750d058e237SJavier Martin { 751d058e237SJavier Martin .width = VGA_WIDTH, 752d058e237SJavier Martin .height = VGA_HEIGHT, 753d058e237SJavier Martin .com7_bit = COM7_FMT_VGA, 754d058e237SJavier Martin .hstart = 158, /* These values from */ 755d058e237SJavier Martin .hstop = 14, /* Omnivision */ 756d058e237SJavier Martin .vstart = 14, /* Empirically determined */ 757d058e237SJavier Martin .vstop = 494, 758d058e237SJavier Martin .regs = NULL, 759d058e237SJavier Martin } 760d058e237SJavier Martin }; 761cb7a01acSMauro Carvalho Chehab 762f6dd927fSJavier Martin static void ov7675_get_framerate(struct v4l2_subdev *sd, 763f6dd927fSJavier Martin struct v4l2_fract *tpf) 764f6dd927fSJavier Martin { 765f6dd927fSJavier Martin struct ov7670_info *info = to_state(sd); 766f6dd927fSJavier Martin u32 clkrc = info->clkrc; 76704ee6d92SJavier Martin int pll_factor; 76804ee6d92SJavier Martin 76904ee6d92SJavier Martin if (info->pll_bypass) 77004ee6d92SJavier Martin pll_factor = 1; 77104ee6d92SJavier Martin else 77204ee6d92SJavier Martin pll_factor = PLL_FACTOR; 773f6dd927fSJavier Martin 774f6dd927fSJavier Martin clkrc++; 775f5fe58fdSBoris BREZILLON if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8) 776f6dd927fSJavier Martin clkrc = (clkrc >> 1); 777f6dd927fSJavier Martin 778f6dd927fSJavier Martin tpf->numerator = 1; 779f6dd927fSJavier Martin tpf->denominator = (5 * pll_factor * info->clock_speed) / 780f6dd927fSJavier Martin (4 * clkrc); 781f6dd927fSJavier Martin } 782f6dd927fSJavier Martin 783f6dd927fSJavier Martin static int ov7675_set_framerate(struct v4l2_subdev *sd, 784f6dd927fSJavier Martin struct v4l2_fract *tpf) 785f6dd927fSJavier Martin { 786f6dd927fSJavier Martin struct ov7670_info *info = to_state(sd); 787f6dd927fSJavier Martin u32 clkrc; 78804ee6d92SJavier Martin int pll_factor; 789f6dd927fSJavier Martin int ret; 790f6dd927fSJavier Martin 791f6dd927fSJavier Martin /* 792f6dd927fSJavier Martin * The formula is fps = 5/4*pixclk for YUV/RGB and 793f6dd927fSJavier Martin * fps = 5/2*pixclk for RAW. 794f6dd927fSJavier Martin * 795f6dd927fSJavier Martin * pixclk = clock_speed / (clkrc + 1) * PLLfactor 796f6dd927fSJavier Martin * 797f6dd927fSJavier Martin */ 79804ee6d92SJavier Martin if (info->pll_bypass) { 79904ee6d92SJavier Martin pll_factor = 1; 80004ee6d92SJavier Martin ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS); 80104ee6d92SJavier Martin } else { 80204ee6d92SJavier Martin pll_factor = PLL_FACTOR; 80304ee6d92SJavier Martin ret = ov7670_write(sd, REG_DBLV, DBLV_X4); 80404ee6d92SJavier Martin } 80504ee6d92SJavier Martin if (ret < 0) 80604ee6d92SJavier Martin return ret; 80704ee6d92SJavier Martin 808f6dd927fSJavier Martin if (tpf->numerator == 0 || tpf->denominator == 0) { 809f6dd927fSJavier Martin clkrc = 0; 810f6dd927fSJavier Martin } else { 811f6dd927fSJavier Martin clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / 812f6dd927fSJavier Martin (4 * tpf->denominator); 813f5fe58fdSBoris BREZILLON if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8) 814f6dd927fSJavier Martin clkrc = (clkrc << 1); 815f6dd927fSJavier Martin clkrc--; 816f6dd927fSJavier Martin } 817f6dd927fSJavier Martin 818f6dd927fSJavier Martin /* 819f6dd927fSJavier Martin * The datasheet claims that clkrc = 0 will divide the input clock by 1 820f6dd927fSJavier Martin * but we've checked with an oscilloscope that it divides by 2 instead. 821f6dd927fSJavier Martin * So, if clkrc = 0 just bypass the divider. 822f6dd927fSJavier Martin */ 823f6dd927fSJavier Martin if (clkrc <= 0) 824f6dd927fSJavier Martin clkrc = CLK_EXT; 825f6dd927fSJavier Martin else if (clkrc > CLK_SCALE) 826f6dd927fSJavier Martin clkrc = CLK_SCALE; 827f6dd927fSJavier Martin info->clkrc = clkrc; 828f6dd927fSJavier Martin 829f6dd927fSJavier Martin /* Recalculate frame rate */ 830f6dd927fSJavier Martin ov7675_get_framerate(sd, tpf); 831f6dd927fSJavier Martin 832f6dd927fSJavier Martin ret = ov7670_write(sd, REG_CLKRC, info->clkrc); 833f6dd927fSJavier Martin if (ret < 0) 834f6dd927fSJavier Martin return ret; 83504ee6d92SJavier Martin 836f6dd927fSJavier Martin return ov7670_write(sd, REG_DBLV, DBLV_X4); 837f6dd927fSJavier Martin } 838f6dd927fSJavier Martin 839f6dd927fSJavier Martin static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, 840f6dd927fSJavier Martin struct v4l2_fract *tpf) 841f6dd927fSJavier Martin { 842f6dd927fSJavier Martin struct ov7670_info *info = to_state(sd); 843f6dd927fSJavier Martin 844f6dd927fSJavier Martin tpf->numerator = 1; 845f6dd927fSJavier Martin tpf->denominator = info->clock_speed; 846f6dd927fSJavier Martin if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) 847f6dd927fSJavier Martin tpf->denominator /= (info->clkrc & CLK_SCALE); 848f6dd927fSJavier Martin } 849f6dd927fSJavier Martin 850f6dd927fSJavier Martin static int ov7670_set_framerate_legacy(struct v4l2_subdev *sd, 851f6dd927fSJavier Martin struct v4l2_fract *tpf) 852f6dd927fSJavier Martin { 853f6dd927fSJavier Martin struct ov7670_info *info = to_state(sd); 854f6dd927fSJavier Martin int div; 855f6dd927fSJavier Martin 856f6dd927fSJavier Martin if (tpf->numerator == 0 || tpf->denominator == 0) 857f6dd927fSJavier Martin div = 1; /* Reset to full rate */ 858f6dd927fSJavier Martin else 859f6dd927fSJavier Martin div = (tpf->numerator * info->clock_speed) / tpf->denominator; 860f6dd927fSJavier Martin if (div == 0) 861f6dd927fSJavier Martin div = 1; 862f6dd927fSJavier Martin else if (div > CLK_SCALE) 863f6dd927fSJavier Martin div = CLK_SCALE; 864f6dd927fSJavier Martin info->clkrc = (info->clkrc & 0x80) | div; 865f6dd927fSJavier Martin tpf->numerator = 1; 866f6dd927fSJavier Martin tpf->denominator = info->clock_speed / div; 867f6dd927fSJavier Martin return ov7670_write(sd, REG_CLKRC, info->clkrc); 868f6dd927fSJavier Martin } 869f6dd927fSJavier Martin 870cb7a01acSMauro Carvalho Chehab /* 871cb7a01acSMauro Carvalho Chehab * Store a set of start/stop values into the camera. 872cb7a01acSMauro Carvalho Chehab */ 873cb7a01acSMauro Carvalho Chehab static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, 874cb7a01acSMauro Carvalho Chehab int vstart, int vstop) 875cb7a01acSMauro Carvalho Chehab { 876cb7a01acSMauro Carvalho Chehab int ret; 877cb7a01acSMauro Carvalho Chehab unsigned char v; 878cb7a01acSMauro Carvalho Chehab /* 879cb7a01acSMauro Carvalho Chehab * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of 880cb7a01acSMauro Carvalho Chehab * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is 881cb7a01acSMauro Carvalho Chehab * a mystery "edge offset" value in the top two bits of href. 882cb7a01acSMauro Carvalho Chehab */ 883cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); 884cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); 885cb7a01acSMauro Carvalho Chehab ret += ov7670_read(sd, REG_HREF, &v); 886cb7a01acSMauro Carvalho Chehab v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); 887cb7a01acSMauro Carvalho Chehab msleep(10); 888cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_HREF, v); 889cb7a01acSMauro Carvalho Chehab /* 890cb7a01acSMauro Carvalho Chehab * Vertical: similar arrangement, but only 10 bits. 891cb7a01acSMauro Carvalho Chehab */ 892cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); 893cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); 894cb7a01acSMauro Carvalho Chehab ret += ov7670_read(sd, REG_VREF, &v); 895cb7a01acSMauro Carvalho Chehab v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); 896cb7a01acSMauro Carvalho Chehab msleep(10); 897cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_VREF, v); 898cb7a01acSMauro Carvalho Chehab return ret; 899cb7a01acSMauro Carvalho Chehab } 900cb7a01acSMauro Carvalho Chehab 901cb7a01acSMauro Carvalho Chehab 902cb7a01acSMauro Carvalho Chehab static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, 903f5fe58fdSBoris BREZILLON u32 *code) 904cb7a01acSMauro Carvalho Chehab { 905cb7a01acSMauro Carvalho Chehab if (index >= N_OV7670_FMTS) 906cb7a01acSMauro Carvalho Chehab return -EINVAL; 907cb7a01acSMauro Carvalho Chehab 908cb7a01acSMauro Carvalho Chehab *code = ov7670_formats[index].mbus_code; 909cb7a01acSMauro Carvalho Chehab return 0; 910cb7a01acSMauro Carvalho Chehab } 911cb7a01acSMauro Carvalho Chehab 912cb7a01acSMauro Carvalho Chehab static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, 913cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *fmt, 914cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct **ret_fmt, 915cb7a01acSMauro Carvalho Chehab struct ov7670_win_size **ret_wsize) 916cb7a01acSMauro Carvalho Chehab { 917f748cd3eSJavier Martin int index, i; 918cb7a01acSMauro Carvalho Chehab struct ov7670_win_size *wsize; 919d058e237SJavier Martin struct ov7670_info *info = to_state(sd); 920d058e237SJavier Martin unsigned int n_win_sizes = info->devtype->n_win_sizes; 921f748cd3eSJavier Martin unsigned int win_sizes_limit = n_win_sizes; 922cb7a01acSMauro Carvalho Chehab 923cb7a01acSMauro Carvalho Chehab for (index = 0; index < N_OV7670_FMTS; index++) 924cb7a01acSMauro Carvalho Chehab if (ov7670_formats[index].mbus_code == fmt->code) 925cb7a01acSMauro Carvalho Chehab break; 926cb7a01acSMauro Carvalho Chehab if (index >= N_OV7670_FMTS) { 927cb7a01acSMauro Carvalho Chehab /* default to first format */ 928cb7a01acSMauro Carvalho Chehab index = 0; 929cb7a01acSMauro Carvalho Chehab fmt->code = ov7670_formats[0].mbus_code; 930cb7a01acSMauro Carvalho Chehab } 931cb7a01acSMauro Carvalho Chehab if (ret_fmt != NULL) 932cb7a01acSMauro Carvalho Chehab *ret_fmt = ov7670_formats + index; 933cb7a01acSMauro Carvalho Chehab /* 934cb7a01acSMauro Carvalho Chehab * Fields: the OV devices claim to be progressive. 935cb7a01acSMauro Carvalho Chehab */ 936cb7a01acSMauro Carvalho Chehab fmt->field = V4L2_FIELD_NONE; 937f748cd3eSJavier Martin 938f748cd3eSJavier Martin /* 939f748cd3eSJavier Martin * Don't consider values that don't match min_height and min_width 940f748cd3eSJavier Martin * constraints. 941f748cd3eSJavier Martin */ 942f748cd3eSJavier Martin if (info->min_width || info->min_height) 943f748cd3eSJavier Martin for (i = 0; i < n_win_sizes; i++) { 944f748cd3eSJavier Martin wsize = info->devtype->win_sizes + i; 945f748cd3eSJavier Martin 946f748cd3eSJavier Martin if (wsize->width < info->min_width || 947f748cd3eSJavier Martin wsize->height < info->min_height) { 948f748cd3eSJavier Martin win_sizes_limit = i; 949f748cd3eSJavier Martin break; 950f748cd3eSJavier Martin } 951f748cd3eSJavier Martin } 952cb7a01acSMauro Carvalho Chehab /* 953cb7a01acSMauro Carvalho Chehab * Round requested image size down to the nearest 954cb7a01acSMauro Carvalho Chehab * we support, but not below the smallest. 955cb7a01acSMauro Carvalho Chehab */ 956d058e237SJavier Martin for (wsize = info->devtype->win_sizes; 957f748cd3eSJavier Martin wsize < info->devtype->win_sizes + win_sizes_limit; wsize++) 958cb7a01acSMauro Carvalho Chehab if (fmt->width >= wsize->width && fmt->height >= wsize->height) 959cb7a01acSMauro Carvalho Chehab break; 960f748cd3eSJavier Martin if (wsize >= info->devtype->win_sizes + win_sizes_limit) 961cb7a01acSMauro Carvalho Chehab wsize--; /* Take the smallest one */ 962cb7a01acSMauro Carvalho Chehab if (ret_wsize != NULL) 963cb7a01acSMauro Carvalho Chehab *ret_wsize = wsize; 964cb7a01acSMauro Carvalho Chehab /* 965cb7a01acSMauro Carvalho Chehab * Note the size we'll actually handle. 966cb7a01acSMauro Carvalho Chehab */ 967cb7a01acSMauro Carvalho Chehab fmt->width = wsize->width; 968cb7a01acSMauro Carvalho Chehab fmt->height = wsize->height; 969cb7a01acSMauro Carvalho Chehab fmt->colorspace = ov7670_formats[index].colorspace; 970cb7a01acSMauro Carvalho Chehab return 0; 971cb7a01acSMauro Carvalho Chehab } 972cb7a01acSMauro Carvalho Chehab 973cb7a01acSMauro Carvalho Chehab static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, 974cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *fmt) 975cb7a01acSMauro Carvalho Chehab { 976cb7a01acSMauro Carvalho Chehab return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); 977cb7a01acSMauro Carvalho Chehab } 978cb7a01acSMauro Carvalho Chehab 979cb7a01acSMauro Carvalho Chehab /* 980cb7a01acSMauro Carvalho Chehab * Set a format. 981cb7a01acSMauro Carvalho Chehab */ 982cb7a01acSMauro Carvalho Chehab static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, 983cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *fmt) 984cb7a01acSMauro Carvalho Chehab { 985cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct *ovfmt; 986cb7a01acSMauro Carvalho Chehab struct ov7670_win_size *wsize; 987cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 988cb7a01acSMauro Carvalho Chehab unsigned char com7; 989cb7a01acSMauro Carvalho Chehab int ret; 990cb7a01acSMauro Carvalho Chehab 991cb7a01acSMauro Carvalho Chehab ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); 992cb7a01acSMauro Carvalho Chehab 993cb7a01acSMauro Carvalho Chehab if (ret) 994cb7a01acSMauro Carvalho Chehab return ret; 995cb7a01acSMauro Carvalho Chehab /* 996cb7a01acSMauro Carvalho Chehab * COM7 is a pain in the ass, it doesn't like to be read then 997cb7a01acSMauro Carvalho Chehab * quickly written afterward. But we have everything we need 998cb7a01acSMauro Carvalho Chehab * to set it absolutely here, as long as the format-specific 999cb7a01acSMauro Carvalho Chehab * register sets list it first. 1000cb7a01acSMauro Carvalho Chehab */ 1001cb7a01acSMauro Carvalho Chehab com7 = ovfmt->regs[0].value; 1002cb7a01acSMauro Carvalho Chehab com7 |= wsize->com7_bit; 1003cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_COM7, com7); 1004cb7a01acSMauro Carvalho Chehab /* 1005cb7a01acSMauro Carvalho Chehab * Now write the rest of the array. Also store start/stops 1006cb7a01acSMauro Carvalho Chehab */ 1007cb7a01acSMauro Carvalho Chehab ov7670_write_array(sd, ovfmt->regs + 1); 1008cb7a01acSMauro Carvalho Chehab ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, 1009cb7a01acSMauro Carvalho Chehab wsize->vstop); 1010cb7a01acSMauro Carvalho Chehab ret = 0; 1011cb7a01acSMauro Carvalho Chehab if (wsize->regs) 1012cb7a01acSMauro Carvalho Chehab ret = ov7670_write_array(sd, wsize->regs); 1013cb7a01acSMauro Carvalho Chehab info->fmt = ovfmt; 1014cb7a01acSMauro Carvalho Chehab 1015cb7a01acSMauro Carvalho Chehab /* 1016cb7a01acSMauro Carvalho Chehab * If we're running RGB565, we must rewrite clkrc after setting 1017cb7a01acSMauro Carvalho Chehab * the other parameters or the image looks poor. If we're *not* 1018cb7a01acSMauro Carvalho Chehab * doing RGB565, we must not rewrite clkrc or the image looks 1019cb7a01acSMauro Carvalho Chehab * *really* poor. 1020cb7a01acSMauro Carvalho Chehab * 1021cb7a01acSMauro Carvalho Chehab * (Update) Now that we retain clkrc state, we should be able 1022cb7a01acSMauro Carvalho Chehab * to write it unconditionally, and that will make the frame 1023cb7a01acSMauro Carvalho Chehab * rate persistent too. 1024cb7a01acSMauro Carvalho Chehab */ 1025cb7a01acSMauro Carvalho Chehab if (ret == 0) 1026cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_CLKRC, info->clkrc); 1027cb7a01acSMauro Carvalho Chehab return 0; 1028cb7a01acSMauro Carvalho Chehab } 1029cb7a01acSMauro Carvalho Chehab 1030cb7a01acSMauro Carvalho Chehab /* 1031cb7a01acSMauro Carvalho Chehab * Implement G/S_PARM. There is a "high quality" mode we could try 1032cb7a01acSMauro Carvalho Chehab * to do someday; for now, we just do the frame rate tweak. 1033cb7a01acSMauro Carvalho Chehab */ 1034cb7a01acSMauro Carvalho Chehab static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) 1035cb7a01acSMauro Carvalho Chehab { 1036cb7a01acSMauro Carvalho Chehab struct v4l2_captureparm *cp = &parms->parm.capture; 1037cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 1038cb7a01acSMauro Carvalho Chehab 1039cb7a01acSMauro Carvalho Chehab if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 1040cb7a01acSMauro Carvalho Chehab return -EINVAL; 1041cb7a01acSMauro Carvalho Chehab 1042cb7a01acSMauro Carvalho Chehab memset(cp, 0, sizeof(struct v4l2_captureparm)); 1043cb7a01acSMauro Carvalho Chehab cp->capability = V4L2_CAP_TIMEPERFRAME; 1044f6dd927fSJavier Martin info->devtype->get_framerate(sd, &cp->timeperframe); 1045f6dd927fSJavier Martin 1046cb7a01acSMauro Carvalho Chehab return 0; 1047cb7a01acSMauro Carvalho Chehab } 1048cb7a01acSMauro Carvalho Chehab 1049cb7a01acSMauro Carvalho Chehab static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) 1050cb7a01acSMauro Carvalho Chehab { 1051cb7a01acSMauro Carvalho Chehab struct v4l2_captureparm *cp = &parms->parm.capture; 1052cb7a01acSMauro Carvalho Chehab struct v4l2_fract *tpf = &cp->timeperframe; 1053cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 1054cb7a01acSMauro Carvalho Chehab 1055cb7a01acSMauro Carvalho Chehab if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 1056cb7a01acSMauro Carvalho Chehab return -EINVAL; 1057cb7a01acSMauro Carvalho Chehab if (cp->extendedmode != 0) 1058cb7a01acSMauro Carvalho Chehab return -EINVAL; 1059cb7a01acSMauro Carvalho Chehab 1060f6dd927fSJavier Martin return info->devtype->set_framerate(sd, tpf); 1061cb7a01acSMauro Carvalho Chehab } 1062cb7a01acSMauro Carvalho Chehab 1063cb7a01acSMauro Carvalho Chehab 1064cb7a01acSMauro Carvalho Chehab /* 1065cb7a01acSMauro Carvalho Chehab * Frame intervals. Since frame rates are controlled with the clock 1066cb7a01acSMauro Carvalho Chehab * divider, we can only do 30/n for integer n values. So no continuous 1067cb7a01acSMauro Carvalho Chehab * or stepwise options. Here we just pick a handful of logical values. 1068cb7a01acSMauro Carvalho Chehab */ 1069cb7a01acSMauro Carvalho Chehab 1070cb7a01acSMauro Carvalho Chehab static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; 1071cb7a01acSMauro Carvalho Chehab 107217bef885SHans Verkuil static int ov7670_enum_frame_interval(struct v4l2_subdev *sd, 107317bef885SHans Verkuil struct v4l2_subdev_pad_config *cfg, 107417bef885SHans Verkuil struct v4l2_subdev_frame_interval_enum *fie) 1075cb7a01acSMauro Carvalho Chehab { 1076b8cc79fdSHans Verkuil struct ov7670_info *info = to_state(sd); 1077b8cc79fdSHans Verkuil unsigned int n_win_sizes = info->devtype->n_win_sizes; 1078b8cc79fdSHans Verkuil int i; 1079b8cc79fdSHans Verkuil 108017bef885SHans Verkuil if (fie->pad) 1081cb7a01acSMauro Carvalho Chehab return -EINVAL; 108217bef885SHans Verkuil if (fie->index >= ARRAY_SIZE(ov7670_frame_rates)) 108317bef885SHans Verkuil return -EINVAL; 1084b8cc79fdSHans Verkuil 1085b8cc79fdSHans Verkuil /* 1086b8cc79fdSHans Verkuil * Check if the width/height is valid. 1087b8cc79fdSHans Verkuil * 1088b8cc79fdSHans Verkuil * If a minimum width/height was requested, filter out the capture 1089b8cc79fdSHans Verkuil * windows that fall outside that. 1090b8cc79fdSHans Verkuil */ 1091b8cc79fdSHans Verkuil for (i = 0; i < n_win_sizes; i++) { 1092b8cc79fdSHans Verkuil struct ov7670_win_size *win = &info->devtype->win_sizes[i]; 1093b8cc79fdSHans Verkuil 1094b8cc79fdSHans Verkuil if (info->min_width && win->width < info->min_width) 1095b8cc79fdSHans Verkuil continue; 1096b8cc79fdSHans Verkuil if (info->min_height && win->height < info->min_height) 1097b8cc79fdSHans Verkuil continue; 1098b8cc79fdSHans Verkuil if (fie->width == win->width && fie->height == win->height) 1099b8cc79fdSHans Verkuil break; 1100b8cc79fdSHans Verkuil } 1101b8cc79fdSHans Verkuil if (i == n_win_sizes) 1102b8cc79fdSHans Verkuil return -EINVAL; 110317bef885SHans Verkuil fie->interval.numerator = 1; 110417bef885SHans Verkuil fie->interval.denominator = ov7670_frame_rates[fie->index]; 1105cb7a01acSMauro Carvalho Chehab return 0; 1106cb7a01acSMauro Carvalho Chehab } 1107cb7a01acSMauro Carvalho Chehab 1108cb7a01acSMauro Carvalho Chehab /* 1109cb7a01acSMauro Carvalho Chehab * Frame size enumeration 1110cb7a01acSMauro Carvalho Chehab */ 111117bef885SHans Verkuil static int ov7670_enum_frame_size(struct v4l2_subdev *sd, 111217bef885SHans Verkuil struct v4l2_subdev_pad_config *cfg, 111317bef885SHans Verkuil struct v4l2_subdev_frame_size_enum *fse) 1114cb7a01acSMauro Carvalho Chehab { 1115cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 1116cb7a01acSMauro Carvalho Chehab int i; 1117cb7a01acSMauro Carvalho Chehab int num_valid = -1; 111817bef885SHans Verkuil __u32 index = fse->index; 1119d058e237SJavier Martin unsigned int n_win_sizes = info->devtype->n_win_sizes; 1120cb7a01acSMauro Carvalho Chehab 112117bef885SHans Verkuil if (fse->pad) 112217bef885SHans Verkuil return -EINVAL; 112317bef885SHans Verkuil 1124cb7a01acSMauro Carvalho Chehab /* 1125cb7a01acSMauro Carvalho Chehab * If a minimum width/height was requested, filter out the capture 1126cb7a01acSMauro Carvalho Chehab * windows that fall outside that. 1127cb7a01acSMauro Carvalho Chehab */ 1128d058e237SJavier Martin for (i = 0; i < n_win_sizes; i++) { 1129322e6d19SGuennadi Liakhovetski struct ov7670_win_size *win = &info->devtype->win_sizes[i]; 1130cb7a01acSMauro Carvalho Chehab if (info->min_width && win->width < info->min_width) 1131cb7a01acSMauro Carvalho Chehab continue; 1132cb7a01acSMauro Carvalho Chehab if (info->min_height && win->height < info->min_height) 1133cb7a01acSMauro Carvalho Chehab continue; 1134cb7a01acSMauro Carvalho Chehab if (index == ++num_valid) { 113517bef885SHans Verkuil fse->min_width = fse->max_width = win->width; 113617bef885SHans Verkuil fse->min_height = fse->max_height = win->height; 1137cb7a01acSMauro Carvalho Chehab return 0; 1138cb7a01acSMauro Carvalho Chehab } 1139cb7a01acSMauro Carvalho Chehab } 1140cb7a01acSMauro Carvalho Chehab 1141cb7a01acSMauro Carvalho Chehab return -EINVAL; 1142cb7a01acSMauro Carvalho Chehab } 1143cb7a01acSMauro Carvalho Chehab 1144cb7a01acSMauro Carvalho Chehab /* 1145cb7a01acSMauro Carvalho Chehab * Code for dealing with controls. 1146cb7a01acSMauro Carvalho Chehab */ 1147cb7a01acSMauro Carvalho Chehab 1148cb7a01acSMauro Carvalho Chehab static int ov7670_store_cmatrix(struct v4l2_subdev *sd, 1149cb7a01acSMauro Carvalho Chehab int matrix[CMATRIX_LEN]) 1150cb7a01acSMauro Carvalho Chehab { 1151cb7a01acSMauro Carvalho Chehab int i, ret; 1152cb7a01acSMauro Carvalho Chehab unsigned char signbits = 0; 1153cb7a01acSMauro Carvalho Chehab 1154cb7a01acSMauro Carvalho Chehab /* 1155cb7a01acSMauro Carvalho Chehab * Weird crap seems to exist in the upper part of 1156cb7a01acSMauro Carvalho Chehab * the sign bits register, so let's preserve it. 1157cb7a01acSMauro Carvalho Chehab */ 1158cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); 1159cb7a01acSMauro Carvalho Chehab signbits &= 0xc0; 1160cb7a01acSMauro Carvalho Chehab 1161cb7a01acSMauro Carvalho Chehab for (i = 0; i < CMATRIX_LEN; i++) { 1162cb7a01acSMauro Carvalho Chehab unsigned char raw; 1163cb7a01acSMauro Carvalho Chehab 1164cb7a01acSMauro Carvalho Chehab if (matrix[i] < 0) { 1165cb7a01acSMauro Carvalho Chehab signbits |= (1 << i); 1166cb7a01acSMauro Carvalho Chehab if (matrix[i] < -255) 1167cb7a01acSMauro Carvalho Chehab raw = 0xff; 1168cb7a01acSMauro Carvalho Chehab else 1169cb7a01acSMauro Carvalho Chehab raw = (-1 * matrix[i]) & 0xff; 1170cb7a01acSMauro Carvalho Chehab } 1171cb7a01acSMauro Carvalho Chehab else { 1172cb7a01acSMauro Carvalho Chehab if (matrix[i] > 255) 1173cb7a01acSMauro Carvalho Chehab raw = 0xff; 1174cb7a01acSMauro Carvalho Chehab else 1175cb7a01acSMauro Carvalho Chehab raw = matrix[i] & 0xff; 1176cb7a01acSMauro Carvalho Chehab } 1177cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); 1178cb7a01acSMauro Carvalho Chehab } 1179cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); 1180cb7a01acSMauro Carvalho Chehab return ret; 1181cb7a01acSMauro Carvalho Chehab } 1182cb7a01acSMauro Carvalho Chehab 1183cb7a01acSMauro Carvalho Chehab 1184cb7a01acSMauro Carvalho Chehab /* 1185cb7a01acSMauro Carvalho Chehab * Hue also requires messing with the color matrix. It also requires 1186cb7a01acSMauro Carvalho Chehab * trig functions, which tend not to be well supported in the kernel. 1187cb7a01acSMauro Carvalho Chehab * So here is a simple table of sine values, 0-90 degrees, in steps 1188cb7a01acSMauro Carvalho Chehab * of five degrees. Values are multiplied by 1000. 1189cb7a01acSMauro Carvalho Chehab * 1190cb7a01acSMauro Carvalho Chehab * The following naive approximate trig functions require an argument 1191cb7a01acSMauro Carvalho Chehab * carefully limited to -180 <= theta <= 180. 1192cb7a01acSMauro Carvalho Chehab */ 1193cb7a01acSMauro Carvalho Chehab #define SIN_STEP 5 1194cb7a01acSMauro Carvalho Chehab static const int ov7670_sin_table[] = { 1195cb7a01acSMauro Carvalho Chehab 0, 87, 173, 258, 342, 422, 1196cb7a01acSMauro Carvalho Chehab 499, 573, 642, 707, 766, 819, 1197cb7a01acSMauro Carvalho Chehab 866, 906, 939, 965, 984, 996, 1198cb7a01acSMauro Carvalho Chehab 1000 1199cb7a01acSMauro Carvalho Chehab }; 1200cb7a01acSMauro Carvalho Chehab 1201cb7a01acSMauro Carvalho Chehab static int ov7670_sine(int theta) 1202cb7a01acSMauro Carvalho Chehab { 1203cb7a01acSMauro Carvalho Chehab int chs = 1; 1204cb7a01acSMauro Carvalho Chehab int sine; 1205cb7a01acSMauro Carvalho Chehab 1206cb7a01acSMauro Carvalho Chehab if (theta < 0) { 1207cb7a01acSMauro Carvalho Chehab theta = -theta; 1208cb7a01acSMauro Carvalho Chehab chs = -1; 1209cb7a01acSMauro Carvalho Chehab } 1210cb7a01acSMauro Carvalho Chehab if (theta <= 90) 1211cb7a01acSMauro Carvalho Chehab sine = ov7670_sin_table[theta/SIN_STEP]; 1212cb7a01acSMauro Carvalho Chehab else { 1213cb7a01acSMauro Carvalho Chehab theta -= 90; 1214cb7a01acSMauro Carvalho Chehab sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; 1215cb7a01acSMauro Carvalho Chehab } 1216cb7a01acSMauro Carvalho Chehab return sine*chs; 1217cb7a01acSMauro Carvalho Chehab } 1218cb7a01acSMauro Carvalho Chehab 1219cb7a01acSMauro Carvalho Chehab static int ov7670_cosine(int theta) 1220cb7a01acSMauro Carvalho Chehab { 1221cb7a01acSMauro Carvalho Chehab theta = 90 - theta; 1222cb7a01acSMauro Carvalho Chehab if (theta > 180) 1223cb7a01acSMauro Carvalho Chehab theta -= 360; 1224cb7a01acSMauro Carvalho Chehab else if (theta < -180) 1225cb7a01acSMauro Carvalho Chehab theta += 360; 1226cb7a01acSMauro Carvalho Chehab return ov7670_sine(theta); 1227cb7a01acSMauro Carvalho Chehab } 1228cb7a01acSMauro Carvalho Chehab 1229cb7a01acSMauro Carvalho Chehab 1230cb7a01acSMauro Carvalho Chehab 1231cb7a01acSMauro Carvalho Chehab 1232cb7a01acSMauro Carvalho Chehab static void ov7670_calc_cmatrix(struct ov7670_info *info, 1233492959c7SJavier Martin int matrix[CMATRIX_LEN], int sat, int hue) 1234cb7a01acSMauro Carvalho Chehab { 1235cb7a01acSMauro Carvalho Chehab int i; 1236cb7a01acSMauro Carvalho Chehab /* 1237cb7a01acSMauro Carvalho Chehab * Apply the current saturation setting first. 1238cb7a01acSMauro Carvalho Chehab */ 1239cb7a01acSMauro Carvalho Chehab for (i = 0; i < CMATRIX_LEN; i++) 1240492959c7SJavier Martin matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7; 1241cb7a01acSMauro Carvalho Chehab /* 1242cb7a01acSMauro Carvalho Chehab * Then, if need be, rotate the hue value. 1243cb7a01acSMauro Carvalho Chehab */ 1244492959c7SJavier Martin if (hue != 0) { 1245cb7a01acSMauro Carvalho Chehab int sinth, costh, tmpmatrix[CMATRIX_LEN]; 1246cb7a01acSMauro Carvalho Chehab 1247cb7a01acSMauro Carvalho Chehab memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); 1248492959c7SJavier Martin sinth = ov7670_sine(hue); 1249492959c7SJavier Martin costh = ov7670_cosine(hue); 1250cb7a01acSMauro Carvalho Chehab 1251cb7a01acSMauro Carvalho Chehab matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; 1252cb7a01acSMauro Carvalho Chehab matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; 1253cb7a01acSMauro Carvalho Chehab matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; 1254cb7a01acSMauro Carvalho Chehab matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; 1255cb7a01acSMauro Carvalho Chehab matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; 1256cb7a01acSMauro Carvalho Chehab matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; 1257cb7a01acSMauro Carvalho Chehab } 1258cb7a01acSMauro Carvalho Chehab } 1259cb7a01acSMauro Carvalho Chehab 1260cb7a01acSMauro Carvalho Chehab 1261cb7a01acSMauro Carvalho Chehab 1262492959c7SJavier Martin static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue) 1263cb7a01acSMauro Carvalho Chehab { 1264cb7a01acSMauro Carvalho Chehab struct ov7670_info *info = to_state(sd); 1265cb7a01acSMauro Carvalho Chehab int matrix[CMATRIX_LEN]; 1266cb7a01acSMauro Carvalho Chehab int ret; 1267cb7a01acSMauro Carvalho Chehab 1268492959c7SJavier Martin ov7670_calc_cmatrix(info, matrix, sat, hue); 1269cb7a01acSMauro Carvalho Chehab ret = ov7670_store_cmatrix(sd, matrix); 1270cb7a01acSMauro Carvalho Chehab return ret; 1271cb7a01acSMauro Carvalho Chehab } 1272cb7a01acSMauro Carvalho Chehab 1273cb7a01acSMauro Carvalho Chehab 1274cb7a01acSMauro Carvalho Chehab /* 1275cb7a01acSMauro Carvalho Chehab * Some weird registers seem to store values in a sign/magnitude format! 1276cb7a01acSMauro Carvalho Chehab */ 1277cb7a01acSMauro Carvalho Chehab 1278cb7a01acSMauro Carvalho Chehab static unsigned char ov7670_abs_to_sm(unsigned char v) 1279cb7a01acSMauro Carvalho Chehab { 1280cb7a01acSMauro Carvalho Chehab if (v > 127) 1281cb7a01acSMauro Carvalho Chehab return v & 0x7f; 1282cb7a01acSMauro Carvalho Chehab return (128 - v) | 0x80; 1283cb7a01acSMauro Carvalho Chehab } 1284cb7a01acSMauro Carvalho Chehab 1285cb7a01acSMauro Carvalho Chehab static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) 1286cb7a01acSMauro Carvalho Chehab { 1287cb7a01acSMauro Carvalho Chehab unsigned char com8 = 0, v; 1288cb7a01acSMauro Carvalho Chehab int ret; 1289cb7a01acSMauro Carvalho Chehab 1290cb7a01acSMauro Carvalho Chehab ov7670_read(sd, REG_COM8, &com8); 1291cb7a01acSMauro Carvalho Chehab com8 &= ~COM8_AEC; 1292cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_COM8, com8); 1293cb7a01acSMauro Carvalho Chehab v = ov7670_abs_to_sm(value); 1294cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_BRIGHT, v); 1295cb7a01acSMauro Carvalho Chehab return ret; 1296cb7a01acSMauro Carvalho Chehab } 1297cb7a01acSMauro Carvalho Chehab 1298cb7a01acSMauro Carvalho Chehab static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) 1299cb7a01acSMauro Carvalho Chehab { 1300cb7a01acSMauro Carvalho Chehab return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); 1301cb7a01acSMauro Carvalho Chehab } 1302cb7a01acSMauro Carvalho Chehab 1303cb7a01acSMauro Carvalho Chehab static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) 1304cb7a01acSMauro Carvalho Chehab { 1305cb7a01acSMauro Carvalho Chehab unsigned char v = 0; 1306cb7a01acSMauro Carvalho Chehab int ret; 1307cb7a01acSMauro Carvalho Chehab 1308cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_MVFP, &v); 1309cb7a01acSMauro Carvalho Chehab if (value) 1310cb7a01acSMauro Carvalho Chehab v |= MVFP_MIRROR; 1311cb7a01acSMauro Carvalho Chehab else 1312cb7a01acSMauro Carvalho Chehab v &= ~MVFP_MIRROR; 1313cb7a01acSMauro Carvalho Chehab msleep(10); /* FIXME */ 1314cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_MVFP, v); 1315cb7a01acSMauro Carvalho Chehab return ret; 1316cb7a01acSMauro Carvalho Chehab } 1317cb7a01acSMauro Carvalho Chehab 1318cb7a01acSMauro Carvalho Chehab static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) 1319cb7a01acSMauro Carvalho Chehab { 1320cb7a01acSMauro Carvalho Chehab unsigned char v = 0; 1321cb7a01acSMauro Carvalho Chehab int ret; 1322cb7a01acSMauro Carvalho Chehab 1323cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_MVFP, &v); 1324cb7a01acSMauro Carvalho Chehab if (value) 1325cb7a01acSMauro Carvalho Chehab v |= MVFP_FLIP; 1326cb7a01acSMauro Carvalho Chehab else 1327cb7a01acSMauro Carvalho Chehab v &= ~MVFP_FLIP; 1328cb7a01acSMauro Carvalho Chehab msleep(10); /* FIXME */ 1329cb7a01acSMauro Carvalho Chehab ret += ov7670_write(sd, REG_MVFP, v); 1330cb7a01acSMauro Carvalho Chehab return ret; 1331cb7a01acSMauro Carvalho Chehab } 1332cb7a01acSMauro Carvalho Chehab 1333cb7a01acSMauro Carvalho Chehab /* 1334cb7a01acSMauro Carvalho Chehab * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes 1335cb7a01acSMauro Carvalho Chehab * the data sheet, the VREF parts should be the most significant, but 1336cb7a01acSMauro Carvalho Chehab * experience shows otherwise. There seems to be little value in 1337cb7a01acSMauro Carvalho Chehab * messing with the VREF bits, so we leave them alone. 1338cb7a01acSMauro Carvalho Chehab */ 1339cb7a01acSMauro Carvalho Chehab static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) 1340cb7a01acSMauro Carvalho Chehab { 1341cb7a01acSMauro Carvalho Chehab int ret; 1342cb7a01acSMauro Carvalho Chehab unsigned char gain; 1343cb7a01acSMauro Carvalho Chehab 1344cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_GAIN, &gain); 1345cb7a01acSMauro Carvalho Chehab *value = gain; 1346cb7a01acSMauro Carvalho Chehab return ret; 1347cb7a01acSMauro Carvalho Chehab } 1348cb7a01acSMauro Carvalho Chehab 1349cb7a01acSMauro Carvalho Chehab static int ov7670_s_gain(struct v4l2_subdev *sd, int value) 1350cb7a01acSMauro Carvalho Chehab { 1351cb7a01acSMauro Carvalho Chehab int ret; 1352cb7a01acSMauro Carvalho Chehab unsigned char com8; 1353cb7a01acSMauro Carvalho Chehab 1354cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_GAIN, value & 0xff); 1355cb7a01acSMauro Carvalho Chehab /* Have to turn off AGC as well */ 1356cb7a01acSMauro Carvalho Chehab if (ret == 0) { 1357cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_COM8, &com8); 1358cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); 1359cb7a01acSMauro Carvalho Chehab } 1360cb7a01acSMauro Carvalho Chehab return ret; 1361cb7a01acSMauro Carvalho Chehab } 1362cb7a01acSMauro Carvalho Chehab 1363cb7a01acSMauro Carvalho Chehab /* 1364cb7a01acSMauro Carvalho Chehab * Tweak autogain. 1365cb7a01acSMauro Carvalho Chehab */ 1366cb7a01acSMauro Carvalho Chehab static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) 1367cb7a01acSMauro Carvalho Chehab { 1368cb7a01acSMauro Carvalho Chehab int ret; 1369cb7a01acSMauro Carvalho Chehab unsigned char com8; 1370cb7a01acSMauro Carvalho Chehab 1371cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_COM8, &com8); 1372cb7a01acSMauro Carvalho Chehab if (ret == 0) { 1373cb7a01acSMauro Carvalho Chehab if (value) 1374cb7a01acSMauro Carvalho Chehab com8 |= COM8_AGC; 1375cb7a01acSMauro Carvalho Chehab else 1376cb7a01acSMauro Carvalho Chehab com8 &= ~COM8_AGC; 1377cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM8, com8); 1378cb7a01acSMauro Carvalho Chehab } 1379cb7a01acSMauro Carvalho Chehab return ret; 1380cb7a01acSMauro Carvalho Chehab } 1381cb7a01acSMauro Carvalho Chehab 1382cb7a01acSMauro Carvalho Chehab static int ov7670_s_exp(struct v4l2_subdev *sd, int value) 1383cb7a01acSMauro Carvalho Chehab { 1384cb7a01acSMauro Carvalho Chehab int ret; 1385cb7a01acSMauro Carvalho Chehab unsigned char com1, com8, aech, aechh; 1386cb7a01acSMauro Carvalho Chehab 1387cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_COM1, &com1) + 1388d487df9eSMauro Carvalho Chehab ov7670_read(sd, REG_COM8, &com8) + 1389cb7a01acSMauro Carvalho Chehab ov7670_read(sd, REG_AECHH, &aechh); 1390cb7a01acSMauro Carvalho Chehab if (ret) 1391cb7a01acSMauro Carvalho Chehab return ret; 1392cb7a01acSMauro Carvalho Chehab 1393cb7a01acSMauro Carvalho Chehab com1 = (com1 & 0xfc) | (value & 0x03); 1394cb7a01acSMauro Carvalho Chehab aech = (value >> 2) & 0xff; 1395cb7a01acSMauro Carvalho Chehab aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); 1396cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM1, com1) + 1397cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_AECH, aech) + 1398cb7a01acSMauro Carvalho Chehab ov7670_write(sd, REG_AECHH, aechh); 1399cb7a01acSMauro Carvalho Chehab /* Have to turn off AEC as well */ 1400cb7a01acSMauro Carvalho Chehab if (ret == 0) 1401cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); 1402cb7a01acSMauro Carvalho Chehab return ret; 1403cb7a01acSMauro Carvalho Chehab } 1404cb7a01acSMauro Carvalho Chehab 1405cb7a01acSMauro Carvalho Chehab /* 1406cb7a01acSMauro Carvalho Chehab * Tweak autoexposure. 1407cb7a01acSMauro Carvalho Chehab */ 1408cb7a01acSMauro Carvalho Chehab static int ov7670_s_autoexp(struct v4l2_subdev *sd, 1409cb7a01acSMauro Carvalho Chehab enum v4l2_exposure_auto_type value) 1410cb7a01acSMauro Carvalho Chehab { 1411cb7a01acSMauro Carvalho Chehab int ret; 1412cb7a01acSMauro Carvalho Chehab unsigned char com8; 1413cb7a01acSMauro Carvalho Chehab 1414cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, REG_COM8, &com8); 1415cb7a01acSMauro Carvalho Chehab if (ret == 0) { 1416cb7a01acSMauro Carvalho Chehab if (value == V4L2_EXPOSURE_AUTO) 1417cb7a01acSMauro Carvalho Chehab com8 |= COM8_AEC; 1418cb7a01acSMauro Carvalho Chehab else 1419cb7a01acSMauro Carvalho Chehab com8 &= ~COM8_AEC; 1420cb7a01acSMauro Carvalho Chehab ret = ov7670_write(sd, REG_COM8, com8); 1421cb7a01acSMauro Carvalho Chehab } 1422cb7a01acSMauro Carvalho Chehab return ret; 1423cb7a01acSMauro Carvalho Chehab } 1424cb7a01acSMauro Carvalho Chehab 1425cb7a01acSMauro Carvalho Chehab 1426492959c7SJavier Martin static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 1427cb7a01acSMauro Carvalho Chehab { 1428492959c7SJavier Martin struct v4l2_subdev *sd = to_sd(ctrl); 1429492959c7SJavier Martin struct ov7670_info *info = to_state(sd); 1430492959c7SJavier Martin 1431492959c7SJavier Martin switch (ctrl->id) { 1432cb7a01acSMauro Carvalho Chehab case V4L2_CID_AUTOGAIN: 1433492959c7SJavier Martin return ov7670_g_gain(sd, &info->gain->val); 1434cb7a01acSMauro Carvalho Chehab } 1435cb7a01acSMauro Carvalho Chehab return -EINVAL; 1436cb7a01acSMauro Carvalho Chehab } 1437cb7a01acSMauro Carvalho Chehab 1438492959c7SJavier Martin static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl) 1439cb7a01acSMauro Carvalho Chehab { 1440492959c7SJavier Martin struct v4l2_subdev *sd = to_sd(ctrl); 1441492959c7SJavier Martin struct ov7670_info *info = to_state(sd); 1442492959c7SJavier Martin 1443cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 1444cb7a01acSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 1445492959c7SJavier Martin return ov7670_s_brightness(sd, ctrl->val); 1446cb7a01acSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 1447492959c7SJavier Martin return ov7670_s_contrast(sd, ctrl->val); 1448cb7a01acSMauro Carvalho Chehab case V4L2_CID_SATURATION: 1449492959c7SJavier Martin return ov7670_s_sat_hue(sd, 1450492959c7SJavier Martin info->saturation->val, info->hue->val); 1451cb7a01acSMauro Carvalho Chehab case V4L2_CID_VFLIP: 1452492959c7SJavier Martin return ov7670_s_vflip(sd, ctrl->val); 1453cb7a01acSMauro Carvalho Chehab case V4L2_CID_HFLIP: 1454492959c7SJavier Martin return ov7670_s_hflip(sd, ctrl->val); 1455cb7a01acSMauro Carvalho Chehab case V4L2_CID_AUTOGAIN: 1456492959c7SJavier Martin /* Only set manual gain if auto gain is not explicitly 1457492959c7SJavier Martin turned on. */ 1458492959c7SJavier Martin if (!ctrl->val) { 1459492959c7SJavier Martin /* ov7670_s_gain turns off auto gain */ 1460492959c7SJavier Martin return ov7670_s_gain(sd, info->gain->val); 1461492959c7SJavier Martin } 1462492959c7SJavier Martin return ov7670_s_autogain(sd, ctrl->val); 1463cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE_AUTO: 1464492959c7SJavier Martin /* Only set manual exposure if auto exposure is not explicitly 1465492959c7SJavier Martin turned on. */ 1466492959c7SJavier Martin if (ctrl->val == V4L2_EXPOSURE_MANUAL) { 1467492959c7SJavier Martin /* ov7670_s_exp turns off auto exposure */ 1468492959c7SJavier Martin return ov7670_s_exp(sd, info->exposure->val); 1469492959c7SJavier Martin } 1470492959c7SJavier Martin return ov7670_s_autoexp(sd, ctrl->val); 1471cb7a01acSMauro Carvalho Chehab } 1472cb7a01acSMauro Carvalho Chehab return -EINVAL; 1473cb7a01acSMauro Carvalho Chehab } 1474cb7a01acSMauro Carvalho Chehab 1475492959c7SJavier Martin static const struct v4l2_ctrl_ops ov7670_ctrl_ops = { 1476492959c7SJavier Martin .s_ctrl = ov7670_s_ctrl, 1477492959c7SJavier Martin .g_volatile_ctrl = ov7670_g_volatile_ctrl, 1478492959c7SJavier Martin }; 1479cb7a01acSMauro Carvalho Chehab 1480cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG 1481cb7a01acSMauro Carvalho Chehab static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) 1482cb7a01acSMauro Carvalho Chehab { 1483cb7a01acSMauro Carvalho Chehab unsigned char val = 0; 1484cb7a01acSMauro Carvalho Chehab int ret; 1485cb7a01acSMauro Carvalho Chehab 1486cb7a01acSMauro Carvalho Chehab ret = ov7670_read(sd, reg->reg & 0xff, &val); 1487cb7a01acSMauro Carvalho Chehab reg->val = val; 1488cb7a01acSMauro Carvalho Chehab reg->size = 1; 1489cb7a01acSMauro Carvalho Chehab return ret; 1490cb7a01acSMauro Carvalho Chehab } 1491cb7a01acSMauro Carvalho Chehab 1492977ba3b1SHans Verkuil static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) 1493cb7a01acSMauro Carvalho Chehab { 1494cb7a01acSMauro Carvalho Chehab ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); 1495cb7a01acSMauro Carvalho Chehab return 0; 1496cb7a01acSMauro Carvalho Chehab } 1497cb7a01acSMauro Carvalho Chehab #endif 1498cb7a01acSMauro Carvalho Chehab 1499cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 1500cb7a01acSMauro Carvalho Chehab 1501cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops ov7670_core_ops = { 1502cb7a01acSMauro Carvalho Chehab .reset = ov7670_reset, 1503cb7a01acSMauro Carvalho Chehab .init = ov7670_init, 1504cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG 1505cb7a01acSMauro Carvalho Chehab .g_register = ov7670_g_register, 1506cb7a01acSMauro Carvalho Chehab .s_register = ov7670_s_register, 1507cb7a01acSMauro Carvalho Chehab #endif 1508cb7a01acSMauro Carvalho Chehab }; 1509cb7a01acSMauro Carvalho Chehab 1510cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops ov7670_video_ops = { 1511cb7a01acSMauro Carvalho Chehab .enum_mbus_fmt = ov7670_enum_mbus_fmt, 1512cb7a01acSMauro Carvalho Chehab .try_mbus_fmt = ov7670_try_mbus_fmt, 1513cb7a01acSMauro Carvalho Chehab .s_mbus_fmt = ov7670_s_mbus_fmt, 1514cb7a01acSMauro Carvalho Chehab .s_parm = ov7670_s_parm, 1515cb7a01acSMauro Carvalho Chehab .g_parm = ov7670_g_parm, 151617bef885SHans Verkuil }; 151717bef885SHans Verkuil 151817bef885SHans Verkuil static const struct v4l2_subdev_pad_ops ov7670_pad_ops = { 151917bef885SHans Verkuil .enum_frame_interval = ov7670_enum_frame_interval, 152017bef885SHans Verkuil .enum_frame_size = ov7670_enum_frame_size, 1521cb7a01acSMauro Carvalho Chehab }; 1522cb7a01acSMauro Carvalho Chehab 1523cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops ov7670_ops = { 1524cb7a01acSMauro Carvalho Chehab .core = &ov7670_core_ops, 1525cb7a01acSMauro Carvalho Chehab .video = &ov7670_video_ops, 152617bef885SHans Verkuil .pad = &ov7670_pad_ops, 1527cb7a01acSMauro Carvalho Chehab }; 1528cb7a01acSMauro Carvalho Chehab 1529cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 1530cb7a01acSMauro Carvalho Chehab 1531d058e237SJavier Martin static const struct ov7670_devtype ov7670_devdata[] = { 1532d058e237SJavier Martin [MODEL_OV7670] = { 1533d058e237SJavier Martin .win_sizes = ov7670_win_sizes, 1534d058e237SJavier Martin .n_win_sizes = ARRAY_SIZE(ov7670_win_sizes), 1535f6dd927fSJavier Martin .set_framerate = ov7670_set_framerate_legacy, 1536f6dd927fSJavier Martin .get_framerate = ov7670_get_framerate_legacy, 1537d058e237SJavier Martin }, 1538d058e237SJavier Martin [MODEL_OV7675] = { 1539d058e237SJavier Martin .win_sizes = ov7675_win_sizes, 1540d058e237SJavier Martin .n_win_sizes = ARRAY_SIZE(ov7675_win_sizes), 1541f6dd927fSJavier Martin .set_framerate = ov7675_set_framerate, 1542f6dd927fSJavier Martin .get_framerate = ov7675_get_framerate, 1543d058e237SJavier Martin }, 1544d058e237SJavier Martin }; 1545d058e237SJavier Martin 1546cb7a01acSMauro Carvalho Chehab static int ov7670_probe(struct i2c_client *client, 1547cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *id) 1548cb7a01acSMauro Carvalho Chehab { 1549f6dd927fSJavier Martin struct v4l2_fract tpf; 1550cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd; 1551cb7a01acSMauro Carvalho Chehab struct ov7670_info *info; 1552cb7a01acSMauro Carvalho Chehab int ret; 1553cb7a01acSMauro Carvalho Chehab 1554c02b211dSLaurent Pinchart info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); 1555cb7a01acSMauro Carvalho Chehab if (info == NULL) 1556cb7a01acSMauro Carvalho Chehab return -ENOMEM; 1557cb7a01acSMauro Carvalho Chehab sd = &info->sd; 1558cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(sd, client, &ov7670_ops); 1559cb7a01acSMauro Carvalho Chehab 1560cb7a01acSMauro Carvalho Chehab info->clock_speed = 30; /* default: a guess */ 1561cb7a01acSMauro Carvalho Chehab if (client->dev.platform_data) { 1562cb7a01acSMauro Carvalho Chehab struct ov7670_config *config = client->dev.platform_data; 1563cb7a01acSMauro Carvalho Chehab 1564cb7a01acSMauro Carvalho Chehab /* 1565cb7a01acSMauro Carvalho Chehab * Must apply configuration before initializing device, because it 1566cb7a01acSMauro Carvalho Chehab * selects I/O method. 1567cb7a01acSMauro Carvalho Chehab */ 1568cb7a01acSMauro Carvalho Chehab info->min_width = config->min_width; 1569cb7a01acSMauro Carvalho Chehab info->min_height = config->min_height; 1570cb7a01acSMauro Carvalho Chehab info->use_smbus = config->use_smbus; 1571cb7a01acSMauro Carvalho Chehab 1572cb7a01acSMauro Carvalho Chehab if (config->clock_speed) 1573cb7a01acSMauro Carvalho Chehab info->clock_speed = config->clock_speed; 157404ee6d92SJavier Martin 157504ee6d92SJavier Martin /* 157604ee6d92SJavier Martin * It should be allowed for ov7670 too when it is migrated to 157704ee6d92SJavier Martin * the new frame rate formula. 157804ee6d92SJavier Martin */ 157904ee6d92SJavier Martin if (config->pll_bypass && id->driver_data != MODEL_OV7670) 158004ee6d92SJavier Martin info->pll_bypass = true; 1581ee95258eSJavier Martin 1582ee95258eSJavier Martin if (config->pclk_hb_disable) 1583ee95258eSJavier Martin info->pclk_hb_disable = true; 1584cb7a01acSMauro Carvalho Chehab } 1585cb7a01acSMauro Carvalho Chehab 1586cb7a01acSMauro Carvalho Chehab /* Make sure it's an ov7670 */ 1587cb7a01acSMauro Carvalho Chehab ret = ov7670_detect(sd); 1588cb7a01acSMauro Carvalho Chehab if (ret) { 1589cb7a01acSMauro Carvalho Chehab v4l_dbg(1, debug, client, 1590cb7a01acSMauro Carvalho Chehab "chip found @ 0x%x (%s) is not an ov7670 chip.\n", 1591cb7a01acSMauro Carvalho Chehab client->addr << 1, client->adapter->name); 1592cb7a01acSMauro Carvalho Chehab return ret; 1593cb7a01acSMauro Carvalho Chehab } 1594cb7a01acSMauro Carvalho Chehab v4l_info(client, "chip found @ 0x%02x (%s)\n", 1595cb7a01acSMauro Carvalho Chehab client->addr << 1, client->adapter->name); 1596cb7a01acSMauro Carvalho Chehab 1597d058e237SJavier Martin info->devtype = &ov7670_devdata[id->driver_data]; 1598cb7a01acSMauro Carvalho Chehab info->fmt = &ov7670_formats[0]; 1599f6dd927fSJavier Martin info->clkrc = 0; 1600f6dd927fSJavier Martin 1601f6dd927fSJavier Martin /* Set default frame rate to 30 fps */ 1602f6dd927fSJavier Martin tpf.numerator = 1; 1603f6dd927fSJavier Martin tpf.denominator = 30; 1604f6dd927fSJavier Martin info->devtype->set_framerate(sd, &tpf); 1605f6dd927fSJavier Martin 1606ee95258eSJavier Martin if (info->pclk_hb_disable) 1607ee95258eSJavier Martin ov7670_write(sd, REG_COM10, COM10_PCLK_HB); 1608ee95258eSJavier Martin 1609492959c7SJavier Martin v4l2_ctrl_handler_init(&info->hdl, 10); 1610492959c7SJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1611492959c7SJavier Martin V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); 1612492959c7SJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1613492959c7SJavier Martin V4L2_CID_CONTRAST, 0, 127, 1, 64); 1614492959c7SJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1615492959c7SJavier Martin V4L2_CID_VFLIP, 0, 1, 1, 0); 1616492959c7SJavier Martin v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1617492959c7SJavier Martin V4L2_CID_HFLIP, 0, 1, 1, 0); 1618492959c7SJavier Martin info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1619492959c7SJavier Martin V4L2_CID_SATURATION, 0, 256, 1, 128); 1620492959c7SJavier Martin info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1621492959c7SJavier Martin V4L2_CID_HUE, -180, 180, 5, 0); 1622492959c7SJavier Martin info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1623492959c7SJavier Martin V4L2_CID_GAIN, 0, 255, 1, 128); 1624492959c7SJavier Martin info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1625492959c7SJavier Martin V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 1626492959c7SJavier Martin info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, 1627492959c7SJavier Martin V4L2_CID_EXPOSURE, 0, 65535, 1, 500); 1628492959c7SJavier Martin info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops, 1629492959c7SJavier Martin V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, 1630492959c7SJavier Martin V4L2_EXPOSURE_AUTO); 1631492959c7SJavier Martin sd->ctrl_handler = &info->hdl; 1632492959c7SJavier Martin if (info->hdl.error) { 1633492959c7SJavier Martin int err = info->hdl.error; 1634492959c7SJavier Martin 1635492959c7SJavier Martin v4l2_ctrl_handler_free(&info->hdl); 1636492959c7SJavier Martin return err; 1637492959c7SJavier Martin } 1638492959c7SJavier Martin /* 1639492959c7SJavier Martin * We have checked empirically that hw allows to read back the gain 1640492959c7SJavier Martin * value chosen by auto gain but that's not the case for auto exposure. 1641492959c7SJavier Martin */ 1642492959c7SJavier Martin v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true); 1643492959c7SJavier Martin v4l2_ctrl_auto_cluster(2, &info->auto_exposure, 1644492959c7SJavier Martin V4L2_EXPOSURE_MANUAL, false); 1645492959c7SJavier Martin v4l2_ctrl_cluster(2, &info->saturation); 1646492959c7SJavier Martin v4l2_ctrl_handler_setup(&info->hdl); 1647492959c7SJavier Martin 1648cb7a01acSMauro Carvalho Chehab return 0; 1649cb7a01acSMauro Carvalho Chehab } 1650cb7a01acSMauro Carvalho Chehab 1651cb7a01acSMauro Carvalho Chehab 1652cb7a01acSMauro Carvalho Chehab static int ov7670_remove(struct i2c_client *client) 1653cb7a01acSMauro Carvalho Chehab { 1654cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 1655492959c7SJavier Martin struct ov7670_info *info = to_state(sd); 1656cb7a01acSMauro Carvalho Chehab 1657cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(sd); 1658492959c7SJavier Martin v4l2_ctrl_handler_free(&info->hdl); 1659cb7a01acSMauro Carvalho Chehab return 0; 1660cb7a01acSMauro Carvalho Chehab } 1661cb7a01acSMauro Carvalho Chehab 1662cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id ov7670_id[] = { 1663d058e237SJavier Martin { "ov7670", MODEL_OV7670 }, 1664d058e237SJavier Martin { "ov7675", MODEL_OV7675 }, 1665cb7a01acSMauro Carvalho Chehab { } 1666cb7a01acSMauro Carvalho Chehab }; 1667cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, ov7670_id); 1668cb7a01acSMauro Carvalho Chehab 1669cb7a01acSMauro Carvalho Chehab static struct i2c_driver ov7670_driver = { 1670cb7a01acSMauro Carvalho Chehab .driver = { 1671cb7a01acSMauro Carvalho Chehab .owner = THIS_MODULE, 1672cb7a01acSMauro Carvalho Chehab .name = "ov7670", 1673cb7a01acSMauro Carvalho Chehab }, 1674cb7a01acSMauro Carvalho Chehab .probe = ov7670_probe, 1675cb7a01acSMauro Carvalho Chehab .remove = ov7670_remove, 1676cb7a01acSMauro Carvalho Chehab .id_table = ov7670_id, 1677cb7a01acSMauro Carvalho Chehab }; 1678cb7a01acSMauro Carvalho Chehab 1679cb7a01acSMauro Carvalho Chehab module_i2c_driver(ov7670_driver); 1680