1/* OmniVision OV7610/OV7110 Camera Chip Support Code 2 * 3 * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> 4 * http://alpha.dyndns.org/ov511/ 5 * 6 * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. 12 */ 13 14#define DEBUG 15 16#include <linux/slab.h> 17#include "ovcamchip_priv.h" 18 19/* Registers */ 20#define REG_GAIN 0x00 /* gain [5:0] */ 21#define REG_BLUE 0x01 /* blue channel balance */ 22#define REG_RED 0x02 /* red channel balance */ 23#define REG_SAT 0x03 /* saturation */ 24#define REG_CNT 0x05 /* Y contrast */ 25#define REG_BRT 0x06 /* Y brightness */ 26#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ 27#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ 28#define REG_GAMMA_COEFF 0x0E /* gamma settings */ 29#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ 30#define REG_EXP 0x10 /* manual exposure setting */ 31#define REG_CLOCK 0x11 /* polarity/clock prescaler */ 32#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ 33#define REG_HWIN_START 0x17 /* horizontal window start */ 34#define REG_HWIN_END 0x18 /* horizontal window end */ 35#define REG_VWIN_START 0x19 /* vertical window start */ 36#define REG_VWIN_END 0x1A /* vertical window end */ 37#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ 38#define REG_YOFFSET 0x21 /* Y channel offset */ 39#define REG_UOFFSET 0x22 /* U channel offset */ 40#define REG_ECW 0x24 /* exposure white level for AEC */ 41#define REG_ECB 0x25 /* exposure black level for AEC */ 42#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ 43#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ 44#define REG_ALC 0x2C /* Auto Level Control settings */ 45#define REG_VOFFSET 0x2E /* V channel offset adjustment */ 46#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ 47#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ 48#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ 49 50/* Window parameters */ 51#define HWSBASE 0x38 52#define HWEBASE 0x3a 53#define VWSBASE 0x05 54#define VWEBASE 0x05 55 56struct ov7x10 { 57 int auto_brt; 58 int auto_exp; 59 int bandfilt; 60 int mirror; 61}; 62 63/* Lawrence Glaister <lg@jfm.bc.ca> reports: 64 * 65 * Register 0x0f in the 7610 has the following effects: 66 * 67 * 0x85 (AEC method 1): Best overall, good contrast range 68 * 0x45 (AEC method 2): Very overexposed 69 * 0xa5 (spec sheet default): Ok, but the black level is 70 * shifted resulting in loss of contrast 71 * 0x05 (old driver setting): very overexposed, too much 72 * contrast 73 */ 74static struct ovcamchip_regvals regvals_init_7x10[] = { 75 { 0x10, 0xff }, 76 { 0x16, 0x03 }, 77 { 0x28, 0x24 }, 78 { 0x2b, 0xac }, 79 { 0x12, 0x00 }, 80 { 0x38, 0x81 }, 81 { 0x28, 0x24 }, /* 0c */ 82 { 0x0f, 0x85 }, /* lg's setting */ 83 { 0x15, 0x01 }, 84 { 0x20, 0x1c }, 85 { 0x23, 0x2a }, 86 { 0x24, 0x10 }, 87 { 0x25, 0x8a }, 88 { 0x26, 0xa2 }, 89 { 0x27, 0xc2 }, 90 { 0x2a, 0x04 }, 91 { 0x2c, 0xfe }, 92 { 0x2d, 0x93 }, 93 { 0x30, 0x71 }, 94 { 0x31, 0x60 }, 95 { 0x32, 0x26 }, 96 { 0x33, 0x20 }, 97 { 0x34, 0x48 }, 98 { 0x12, 0x24 }, 99 { 0x11, 0x01 }, 100 { 0x0c, 0x24 }, 101 { 0x0d, 0x24 }, 102 { 0xff, 0xff }, /* END MARKER */ 103}; 104 105/* This initializes the OV7x10 camera chip and relevant variables. */ 106static int ov7x10_init(struct i2c_client *c) 107{ 108 struct ovcamchip *ov = i2c_get_clientdata(c); 109 struct ov7x10 *s; 110 int rc; 111 112 DDEBUG(4, &c->dev, "entered"); 113 114 rc = ov_write_regvals(c, regvals_init_7x10); 115 if (rc < 0) 116 return rc; 117 118 ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); 119 if (!s) 120 return -ENOMEM; 121 122 s->auto_brt = 1; 123 s->auto_exp = 1; 124 125 return rc; 126} 127 128static int ov7x10_free(struct i2c_client *c) 129{ 130 struct ovcamchip *ov = i2c_get_clientdata(c); 131 132 kfree(ov->spriv); 133 return 0; 134} 135 136static int ov7x10_set_control(struct i2c_client *c, 137 struct ovcamchip_control *ctl) 138{ 139 struct ovcamchip *ov = i2c_get_clientdata(c); 140 struct ov7x10 *s = ov->spriv; 141 int rc; 142 int v = ctl->value; 143 144 switch (ctl->id) { 145 case OVCAMCHIP_CID_CONT: 146 rc = ov_write(c, REG_CNT, v >> 8); 147 break; 148 case OVCAMCHIP_CID_BRIGHT: 149 rc = ov_write(c, REG_BRT, v >> 8); 150 break; 151 case OVCAMCHIP_CID_SAT: 152 rc = ov_write(c, REG_SAT, v >> 8); 153 break; 154 case OVCAMCHIP_CID_HUE: 155 rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); 156 if (rc < 0) 157 goto out; 158 159 rc = ov_write(c, REG_BLUE, v >> 8); 160 break; 161 case OVCAMCHIP_CID_EXP: 162 rc = ov_write(c, REG_EXP, v); 163 break; 164 case OVCAMCHIP_CID_FREQ: 165 { 166 int sixty = (v == 60); 167 168 rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); 169 if (rc < 0) 170 goto out; 171 172 rc = ov_write(c, 0x2b, sixty?0x00:0xac); 173 if (rc < 0) 174 goto out; 175 176 rc = ov_write_mask(c, 0x13, 0x10, 0x10); 177 if (rc < 0) 178 goto out; 179 180 rc = ov_write_mask(c, 0x13, 0x00, 0x10); 181 break; 182 } 183 case OVCAMCHIP_CID_BANDFILT: 184 rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); 185 s->bandfilt = v; 186 break; 187 case OVCAMCHIP_CID_AUTOBRIGHT: 188 rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); 189 s->auto_brt = v; 190 break; 191 case OVCAMCHIP_CID_AUTOEXP: 192 rc = ov_write_mask(c, 0x29, v?0x00:0x80, 0x80); 193 s->auto_exp = v; 194 break; 195 case OVCAMCHIP_CID_MIRROR: 196 rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); 197 s->mirror = v; 198 break; 199 default: 200 DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); 201 return -EPERM; 202 } 203 204out: 205 DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); 206 return rc; 207} 208 209static int ov7x10_get_control(struct i2c_client *c, 210 struct ovcamchip_control *ctl) 211{ 212 struct ovcamchip *ov = i2c_get_clientdata(c); 213 struct ov7x10 *s = ov->spriv; 214 int rc = 0; 215 unsigned char val = 0; 216 217 switch (ctl->id) { 218 case OVCAMCHIP_CID_CONT: 219 rc = ov_read(c, REG_CNT, &val); 220 ctl->value = val << 8; 221 break; 222 case OVCAMCHIP_CID_BRIGHT: 223 rc = ov_read(c, REG_BRT, &val); 224 ctl->value = val << 8; 225 break; 226 case OVCAMCHIP_CID_SAT: 227 rc = ov_read(c, REG_SAT, &val); 228 ctl->value = val << 8; 229 break; 230 case OVCAMCHIP_CID_HUE: 231 rc = ov_read(c, REG_BLUE, &val); 232 ctl->value = val << 8; 233 break; 234 case OVCAMCHIP_CID_EXP: 235 rc = ov_read(c, REG_EXP, &val); 236 ctl->value = val; 237 break; 238 case OVCAMCHIP_CID_BANDFILT: 239 ctl->value = s->bandfilt; 240 break; 241 case OVCAMCHIP_CID_AUTOBRIGHT: 242 ctl->value = s->auto_brt; 243 break; 244 case OVCAMCHIP_CID_AUTOEXP: 245 ctl->value = s->auto_exp; 246 break; 247 case OVCAMCHIP_CID_MIRROR: 248 ctl->value = s->mirror; 249 break; 250 default: 251 DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); 252 return -EPERM; 253 } 254 255 DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); 256 return rc; 257} 258 259static int ov7x10_mode_init(struct i2c_client *c, struct ovcamchip_window *win) 260{ 261 int qvga = win->quarter; 262 263 /******** QVGA-specific regs ********/ 264 265 ov_write(c, 0x14, qvga?0x24:0x04); 266 267 /******** Palette-specific regs ********/ 268 269 if (win->format == VIDEO_PALETTE_GREY) { 270 ov_write_mask(c, 0x0e, 0x40, 0x40); 271 ov_write_mask(c, 0x13, 0x20, 0x20); 272 } else { 273 ov_write_mask(c, 0x0e, 0x00, 0x40); 274 ov_write_mask(c, 0x13, 0x00, 0x20); 275 } 276 277 /******** Clock programming ********/ 278 279 ov_write(c, 0x11, win->clockdiv); 280 281 /******** Resolution-specific ********/ 282 283 if (win->width == 640 && win->height == 480) 284 ov_write(c, 0x35, 0x9e); 285 else 286 ov_write(c, 0x35, 0x1e); 287 288 return 0; 289} 290 291static int ov7x10_set_window(struct i2c_client *c, struct ovcamchip_window *win) 292{ 293 int ret, hwscale, vwscale; 294 295 ret = ov7x10_mode_init(c, win); 296 if (ret < 0) 297 return ret; 298 299 if (win->quarter) { 300 hwscale = 1; 301 vwscale = 0; 302 } else { 303 hwscale = 2; 304 vwscale = 1; 305 } 306 307 ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); 308 ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); 309 ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); 310 ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); 311 312 return 0; 313} 314 315static int ov7x10_command(struct i2c_client *c, unsigned int cmd, void *arg) 316{ 317 switch (cmd) { 318 case OVCAMCHIP_CMD_S_CTRL: 319 return ov7x10_set_control(c, arg); 320 case OVCAMCHIP_CMD_G_CTRL: 321 return ov7x10_get_control(c, arg); 322 case OVCAMCHIP_CMD_S_MODE: 323 return ov7x10_set_window(c, arg); 324 default: 325 DDEBUG(2, &c->dev, "command not supported: %d", cmd); 326 return -ENOIOCTLCMD; 327 } 328} 329 330struct ovcamchip_ops ov7x10_ops = { 331 .init = ov7x10_init, 332 .free = ov7x10_free, 333 .command = ov7x10_command, 334}; 335