/* * OmniVision OV7610/OV7110 Camera Chip Support Code * * Copyright (c) 1999-2002 Mark McClelland * http://alpha.dyndns.org/ov511/ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This file is not a module yet */ #define __NO_VERSION__ #if defined(OUTSIDE_KERNEL) #if !defined(EXPORT_SYMTAB) #define EXPORT_SYMTAB #endif #include #if defined(CONFIG_MODVERSIONS) && !defined(MODVERSIONS) #define MODVERSIONS #endif #include #ifdef MODVERSIONS #include #endif #else #include #include #endif #include #include "ov511.h" /* OV7610 registers */ #define REG_GAIN 0x00 /* gain setting (5:0) */ #define REG_BLUE 0x01 /* blue channel balance */ #define REG_RED 0x02 /* red channel balance */ #define REG_SAT 0x03 /* saturation */ /* 04 reserved */ #define REG_CNT 0x05 /* Y contrast */ #define REG_BRT 0x06 /* Y brightness */ /* 08-0b reserved */ #define REG_BLUE_BIAS 0x0C /* blue channel bias (5:0) */ #define REG_RED_BIAS 0x0D /* read channel bias (5:0) */ #define REG_GAMMA_COEFF 0x0E /* gamma settings */ #define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ #define REG_EXP 0x10 /* manual exposure setting */ #define REG_CLOCK 0x11 /* polarity/clock prescaler */ #define REG_COM_A 0x12 /* misc common regs */ #define REG_COM_B 0x13 /* misc common regs */ #define REG_COM_C 0x14 /* misc common regs */ #define REG_COM_D 0x15 /* misc common regs */ #define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ #define REG_HWIN_START 0x17 /* horizontal window start */ #define REG_HWIN_END 0x18 /* horizontal window end */ #define REG_VWIN_START 0x19 /* vertical window start */ #define REG_VWIN_END 0x1A /* vertical window end */ #define REG_PIXEL_SHIFT 0x1B /* pixel shift */ #define REG_ID_HIGH 0x1C /* manufacturer ID MSB */ #define REG_ID_LOW 0x1D /* manufacturer ID LSB */ /* 0e-0f reserved */ #define REG_COM_E 0x20 /* misc common regs */ #define REG_YOFFSET 0x21 /* Y channel offset */ #define REG_UOFFSET 0x22 /* U channel offset */ /* 23 reserved */ #define REG_ECW 0x24 /* Exposure white level for AEC */ #define REG_ECB 0x25 /* Exposure black level for AEC */ #define REG_COM_F 0x26 /* misc settings */ #define REG_COM_G 0x27 /* misc settings */ #define REG_COM_H 0x28 /* misc settings */ #define REG_COM_I 0x29 /* misc settings */ #define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ #define REG_FRAMERATE_L 0x2B /* frame rate LSB */ #define REG_ALC 0x2C /* Auto Level Control settings */ #define REG_COM_J 0x2D /* misc settings */ #define REG_VOFFSET 0x2E /* V channel offset adjustment */ #define REG_ARRAY_BIAS 0x2F /* Array bias -- don't change */ /* 30-32 reserved */ #define REG_YGAMMA 0x33 /* misc gamma settings (7:6) */ #define REG_BIAS_ADJUST 0x34 /* misc bias settings */ #define REG_COM_L 0x35 /* misc settings */ /* 36-37 reserved */ #define REG_COM_K 0x38 /* misc registers */ extern int debug; /* Exists in ov511.c and ov518.c */ struct ov7x10 { struct ov_i2c *i2c; int auto_brt; int auto_exp; int bandfilt; int mirror; }; /* Lawrence Glaister reports: * * Register 0x0f in the 7610 has the following effects: * * 0x85 (AEC method 1): Best overall, good contrast range * 0x45 (AEC method 2): Very overexposed * 0xa5 (spec sheet default): Ok, but the black level is * shifted resulting in loss of contrast * 0x05 (old driver setting): very overexposed, too much * contrast */ static struct ovsensor_regvals regvalsNorm7x10[] = { { 0x10, 0xff }, { 0x16, 0x06 }, { 0x28, 0x24 }, { 0x2b, 0xac }, { 0x12, 0x00 }, { 0x38, 0x81 }, { 0x28, 0x24 }, /* 0c */ { 0x0f, 0x85 }, /* lg's setting */ { 0x15, 0x01 }, { 0x20, 0x1c }, { 0x23, 0x2a }, { 0x24, 0x10 }, { 0x25, 0x8a }, { 0x26, 0xa2 }, { 0x27, 0xc2 }, { 0x2a, 0x04 }, { 0x2c, 0xfe }, { 0x2d, 0x93 }, { 0x30, 0x71 }, { 0x31, 0x60 }, { 0x32, 0x26 }, { 0x33, 0x20 }, { 0x34, 0x48 }, { 0x12, 0x24 }, { 0x11, 0x01 }, { 0x0c, 0x24 }, { 0x0d, 0x24 }, { 0xff, 0xff }, /* END MARKER */ }; /* This initializes the OV7x10 sensor and relevant variables. */ static int ov7x10_configure(struct usb_ov511 *ov) { struct ov7x10 *s; int rc; PDEBUG(4, "entered"); ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); if (!s) return -ENOMEM; memset(s, 0, sizeof *s); s->i2c = ov->i2c; s->auto_brt = 1; s->auto_exp = 1; /* Set sensor-specific vars */ ov->maxwidth = 640; ov->maxheight = 480; ov->minwidth = 64; ov->minheight = 48; rc = ovsensor_write_regvals(ov, regvalsNorm7x10); return rc; } static int ov7x10_set_control(struct usb_ov511 *ov, struct ovsensor_control *ctl) { struct ov7x10 *s = ov->spriv; struct ov_i2c *i2c = s->i2c; int rc; int v = ctl->value; switch (ctl->id) { case OVSENSOR_CID_CONT: rc = i2c->w(ov, REG_CNT, v >> 8); break; case OVSENSOR_CID_BRIGHT: rc = i2c->w(ov, REG_BRT, v >> 8); break; case OVSENSOR_CID_SAT: rc = i2c->w(ov, REG_SAT, v >> 8); break; case OVSENSOR_CID_HUE: rc = i2c->w(ov, REG_RED, 0xFF - (v >> 8)); if (rc < 0) goto out; rc = i2c->w(ov, REG_BLUE, v >> 8); break; case OVSENSOR_CID_EXP: rc = i2c->w(ov, REG_EXP, v); break; case OVSENSOR_CID_FREQ: { int sixty = (v == 60); rc = i2c->w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); if (rc < 0) goto out; rc = i2c->w(ov, 0x2b, sixty?0x00:0xac); if (rc < 0) goto out; rc = i2c->w_mask(ov, 0x13, 0x10, 0x10); if (rc < 0) goto out; rc = i2c->w_mask(ov, 0x13, 0x00, 0x10); break; } case OVSENSOR_CID_BANDFILT: rc = i2c->w_mask(ov, 0x2d, v?0x04:0x00, 0x04); s->bandfilt = v; break; case OVSENSOR_CID_AUTOBRIGHT: rc = i2c->w_mask(ov, 0x2d, v?0x10:0x00, 0x10); s->auto_brt = v; break; case OVSENSOR_CID_AUTOEXP: rc = i2c->w_mask(ov, 0x29, v?0x00:0x80, 0x80); s->auto_exp = v; break; case OVSENSOR_CID_MIRROR: rc = i2c->w_mask(ov, 0x12, v?0x40:0x00, 0x40); s->mirror = v; break; default: PDEBUG(2, "control not supported: %d", ctl->id); return -EPERM; } out: PDEBUG(3, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); return rc; } static int ov7x10_get_control(struct usb_ov511 *ov, struct ovsensor_control *ctl) { struct ov7x10 *s = ov->spriv; struct ov_i2c *i2c = s->i2c; int rc = 0; unsigned char val = 0; switch (ctl->id) { case OVSENSOR_CID_CONT: rc = i2c->r(ov, REG_CNT, &val); ctl->value = val << 8; break; case OVSENSOR_CID_BRIGHT: rc = i2c->r(ov, REG_BRT, &val); ctl->value = val << 8; break; case OVSENSOR_CID_SAT: rc = i2c->r(ov, REG_SAT, &val); ctl->value = val << 8; break; case OVSENSOR_CID_HUE: rc = i2c->r(ov, REG_BLUE, &val); ctl->value = val << 8; break; case OVSENSOR_CID_EXP: rc = i2c->r(ov, REG_EXP, &val); ctl->value = val; break; case OVSENSOR_CID_BANDFILT: ctl->value = s->bandfilt; break; case OVSENSOR_CID_AUTOBRIGHT: ctl->value = s->auto_brt; break; case OVSENSOR_CID_AUTOEXP: ctl->value = s->auto_exp; break; case OVSENSOR_CID_MIRROR: ctl->value = s->mirror; break; default: PDEBUG(2, "control not supported: %d", ctl->id); return -EPERM; } PDEBUG(3, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); return rc; } static int ov7x10_command(struct usb_ov511 *ov, unsigned int cmd, void *arg) { switch (cmd) { case OVSENSOR_CMD_S_CTRL: return ov7x10_set_control(ov, arg); case OVSENSOR_CMD_G_CTRL: return ov7x10_get_control(ov, arg); default: PDEBUG(2, "command not supported: %d", cmd); return -ENOIOCTLCMD; } } struct ovsensor_ops ov7x10_ops = { configure: ov7x10_configure, command: ov7x10_command, };