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