Commit 9b3b3c3b authored by Mark W. McClelland's avatar Mark W. McClelland Committed by Linus Torvalds

[PATCH] Add ovcamchip driver

This patch adds a new driver for the OmniVision OV6xx0 and OV7xx0 series of
CMOS image sensors.  It is currently used by the w9968cf USB webcam driver,
which is already in mainline 2.6.  Up until now it had to be compiled
outside the kernel tree, which is clearly suboptimal.

It is also used by version 2 of the ov511 USB webcam driver, which will be
merged in the near future.  That will reduce some code duplication, since
the existing ov511 has much of this code built-in.

This was previously submitted to Linux-USB-Devel, and I have fixed the
concerns that came up at that time.

Developer's Certificate of Origin 1.0

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
        have the right to submit it under the open source license
        indicated in the file; or

(b) The contribution is based upon previous work that, to the best
        of my knowledge, is covered under an appropriate open source
        license and I have the right under that license to submit that
        work with modifications, whether created in whole or in part
        by me, under the same open source license (unless I am
        by me, under the same open source license (unless I am
        permitted to submit under a different license), as indicated
        in the file; or

(c) The contribution was provided directly to me by some other
        person who certified (a), (b) or (c) and I have not modified
        it.
Signed-off-by: default avatarMark McClelland <mark@alpha.dyndns.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent dd254918
...@@ -295,5 +295,16 @@ config VIDEO_CX88 ...@@ -295,5 +295,16 @@ config VIDEO_CX88
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called cx8800 module will be called cx8800
config VIDEO_OVCAMCHIP
tristate "OmniVision Camera Chip support"
depends on VIDEO_DEV && I2C
---help---
Support for the OmniVision OV6xxx and OV7xxx series of camera chips.
This driver is intended to be used with the ov511 and w9968cf USB
camera drivers.
To compile this driver as a module, choose M here: the
module will be called ovcamchip
endmenu endmenu
...@@ -37,6 +37,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o ...@@ -37,6 +37,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_MEYE) += meye.o
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/
obj-$(CONFIG_VIDEO_MXB) += saa7111.o tuner.o tda9840.o tea6415c.o tea6420.o mxb.o obj-$(CONFIG_VIDEO_MXB) += saa7111.o tuner.o tda9840.o tea6415c.o tea6420.o mxb.o
obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
......
ovcamchip-objs := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \
ov76be.o
obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o
This diff is collapsed.
/* OmniVision OV6630/OV6130 Camera Chip Support Code
*
* Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org>
* 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. NO WARRANTY OF ANY KIND is expressed or implied.
*/
#define DEBUG
#include <linux/slab.h>
#include "ovcamchip_priv.h"
/* Registers */
#define REG_GAIN 0x00 /* gain [5:0] */
#define REG_BLUE 0x01 /* blue gain */
#define REG_RED 0x02 /* red gain */
#define REG_SAT 0x03 /* saturation [7:3] */
#define REG_CNT 0x05 /* Y contrast [3:0] */
#define REG_BRT 0x06 /* Y brightness */
#define REG_SHARP 0x07 /* sharpness */
#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */
#define REG_WB_RED 0x0D /* WB red ratio [5:0] */
#define REG_EXP 0x10 /* exposure */
/* Window parameters */
#define HWSBASE 0x38
#define HWEBASE 0x3A
#define VWSBASE 0x05
#define VWEBASE 0x06
struct ov6x30 {
int auto_brt;
int auto_exp;
int backlight;
int bandfilt;
int mirror;
};
static struct ovcamchip_regvals regvals_init_6x30[] = {
{ 0x12, 0x80 }, /* reset */
{ 0x00, 0x1f }, /* Gain */
{ 0x01, 0x99 }, /* Blue gain */
{ 0x02, 0x7c }, /* Red gain */
{ 0x03, 0xc0 }, /* Saturation */
{ 0x05, 0x0a }, /* Contrast */
{ 0x06, 0x95 }, /* Brightness */
{ 0x07, 0x2d }, /* Sharpness */
{ 0x0c, 0x20 },
{ 0x0d, 0x20 },
{ 0x0e, 0x20 },
{ 0x0f, 0x05 },
{ 0x10, 0x9a }, /* "exposure check" */
{ 0x11, 0x00 }, /* Pixel clock = fastest */
{ 0x12, 0x24 }, /* Enable AGC and AWB */
{ 0x13, 0x21 },
{ 0x14, 0x80 },
{ 0x15, 0x01 },
{ 0x16, 0x03 },
{ 0x17, 0x38 },
{ 0x18, 0xea },
{ 0x19, 0x04 },
{ 0x1a, 0x93 },
{ 0x1b, 0x00 },
{ 0x1e, 0xc4 },
{ 0x1f, 0x04 },
{ 0x20, 0x20 },
{ 0x21, 0x10 },
{ 0x22, 0x88 },
{ 0x23, 0xc0 }, /* Crystal circuit power level */
{ 0x25, 0x9a }, /* Increase AEC black pixel ratio */
{ 0x26, 0xb2 }, /* BLC enable */
{ 0x27, 0xa2 },
{ 0x28, 0x00 },
{ 0x29, 0x00 },
{ 0x2a, 0x84 }, /* (keep) */
{ 0x2b, 0xa8 }, /* (keep) */
{ 0x2c, 0xa0 },
{ 0x2d, 0x95 }, /* Enable auto-brightness */
{ 0x2e, 0x88 },
{ 0x33, 0x26 },
{ 0x34, 0x03 },
{ 0x36, 0x8f },
{ 0x37, 0x80 },
{ 0x38, 0x83 },
{ 0x39, 0x80 },
{ 0x3a, 0x0f },
{ 0x3b, 0x3c },
{ 0x3c, 0x1a },
{ 0x3d, 0x80 },
{ 0x3e, 0x80 },
{ 0x3f, 0x0e },
{ 0x40, 0x00 }, /* White bal */
{ 0x41, 0x00 }, /* White bal */
{ 0x42, 0x80 },
{ 0x43, 0x3f }, /* White bal */
{ 0x44, 0x80 },
{ 0x45, 0x20 },
{ 0x46, 0x20 },
{ 0x47, 0x80 },
{ 0x48, 0x7f },
{ 0x49, 0x00 },
{ 0x4a, 0x00 },
{ 0x4b, 0x80 },
{ 0x4c, 0xd0 },
{ 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
{ 0x4e, 0x40 },
{ 0x4f, 0x07 }, /* UV average mode, color killer: strongest */
{ 0x50, 0xff },
{ 0x54, 0x23 }, /* Max AGC gain: 18dB */
{ 0x55, 0xff },
{ 0x56, 0x12 },
{ 0x57, 0x81 }, /* (default) */
{ 0x58, 0x75 },
{ 0x59, 0x01 }, /* AGC dark current compensation: +1 */
{ 0x5a, 0x2c },
{ 0x5b, 0x0f }, /* AWB chrominance levels */
{ 0x5c, 0x10 },
{ 0x3d, 0x80 },
{ 0x27, 0xa6 },
/* Toggle AWB off and on */
{ 0x12, 0x20 },
{ 0x12, 0x24 },
{ 0xff, 0xff }, /* END MARKER */
};
/* This initializes the OV6x30 camera chip and relevant variables. */
static int ov6x30_init(struct i2c_client *c)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov6x30 *s;
int rc;
DDEBUG(4, &c->dev, "entered");
rc = ov_write_regvals(c, regvals_init_6x30);
if (rc < 0)
return rc;
ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL);
if (!s)
return -ENOMEM;
memset(s, 0, sizeof *s);
s->auto_brt = 1;
s->auto_exp = 1;
return rc;
}
static int ov6x30_free(struct i2c_client *c)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
kfree(ov->spriv);
return 0;
}
static int ov6x30_set_control(struct i2c_client *c,
struct ovcamchip_control *ctl)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov6x30 *s = ov->spriv;
int rc;
int v = ctl->value;
switch (ctl->id) {
case OVCAMCHIP_CID_CONT:
rc = ov_write_mask(c, REG_CNT, v >> 12, 0x0f);
break;
case OVCAMCHIP_CID_BRIGHT:
rc = ov_write(c, REG_BRT, v >> 8);
break;
case OVCAMCHIP_CID_SAT:
rc = ov_write(c, REG_SAT, v >> 8);
break;
case OVCAMCHIP_CID_HUE:
rc = ov_write(c, REG_RED, 0xFF - (v >> 8));
if (rc < 0)
goto out;
rc = ov_write(c, REG_BLUE, v >> 8);
break;
case OVCAMCHIP_CID_EXP:
rc = ov_write(c, REG_EXP, v);
break;
case OVCAMCHIP_CID_FREQ:
{
int sixty = (v == 60);
rc = ov_write(c, 0x2b, sixty?0xa8:0x28);
if (rc < 0)
goto out;
rc = ov_write(c, 0x2a, sixty?0x84:0xa4);
break;
}
case OVCAMCHIP_CID_BANDFILT:
rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04);
s->bandfilt = v;
break;
case OVCAMCHIP_CID_AUTOBRIGHT:
rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10);
s->auto_brt = v;
break;
case OVCAMCHIP_CID_AUTOEXP:
rc = ov_write_mask(c, 0x28, v?0x00:0x10, 0x10);
s->auto_exp = v;
break;
case OVCAMCHIP_CID_BACKLIGHT:
{
rc = ov_write_mask(c, 0x4e, v?0x80:0x60, 0xe0);
if (rc < 0)
goto out;
rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08);
if (rc < 0)
goto out;
rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02);
s->backlight = v;
break;
}
case OVCAMCHIP_CID_MIRROR:
rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40);
s->mirror = v;
break;
default:
DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
return -EPERM;
}
out:
DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc);
return rc;
}
static int ov6x30_get_control(struct i2c_client *c,
struct ovcamchip_control *ctl)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov6x30 *s = ov->spriv;
int rc = 0;
unsigned char val = 0;
switch (ctl->id) {
case OVCAMCHIP_CID_CONT:
rc = ov_read(c, REG_CNT, &val);
ctl->value = (val & 0x0f) << 12;
break;
case OVCAMCHIP_CID_BRIGHT:
rc = ov_read(c, REG_BRT, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_SAT:
rc = ov_read(c, REG_SAT, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_HUE:
rc = ov_read(c, REG_BLUE, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_EXP:
rc = ov_read(c, REG_EXP, &val);
ctl->value = val;
break;
case OVCAMCHIP_CID_BANDFILT:
ctl->value = s->bandfilt;
break;
case OVCAMCHIP_CID_AUTOBRIGHT:
ctl->value = s->auto_brt;
break;
case OVCAMCHIP_CID_AUTOEXP:
ctl->value = s->auto_exp;
break;
case OVCAMCHIP_CID_BACKLIGHT:
ctl->value = s->backlight;
break;
case OVCAMCHIP_CID_MIRROR:
ctl->value = s->mirror;
break;
default:
DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
return -EPERM;
}
DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc);
return rc;
}
static int ov6x30_mode_init(struct i2c_client *c, struct ovcamchip_window *win)
{
/******** QCIF-specific regs ********/
ov_write_mask(c, 0x14, win->quarter?0x20:0x00, 0x20);
/******** Palette-specific regs ********/
if (win->format == VIDEO_PALETTE_GREY) {
if (c->adapter->id == (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518)) {
/* Do nothing - we're already in 8-bit mode */
} else {
ov_write_mask(c, 0x13, 0x20, 0x20);
}
} else {
/* The OV518 needs special treatment. Although both the OV518
* and the OV6630 support a 16-bit video bus, only the 8 bit Y
* bus is actually used. The UV bus is tied to ground.
* Therefore, the OV6630 needs to be in 8-bit multiplexed
* output mode */
if (c->adapter->id == (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518)) {
/* Do nothing - we want to stay in 8-bit mode */
/* Warning: Messing with reg 0x13 breaks OV518 color */
} else {
ov_write_mask(c, 0x13, 0x00, 0x20);
}
}
/******** Clock programming ********/
ov_write(c, 0x11, win->clockdiv);
return 0;
}
static int ov6x30_set_window(struct i2c_client *c, struct ovcamchip_window *win)
{
int ret, hwscale, vwscale;
ret = ov6x30_mode_init(c, win);
if (ret < 0)
return ret;
if (win->quarter) {
hwscale = 0;
vwscale = 0;
} else {
hwscale = 1;
vwscale = 1; /* The datasheet says 0; it's wrong */
}
ov_write(c, 0x17, HWSBASE + (win->x >> hwscale));
ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale));
ov_write(c, 0x19, VWSBASE + (win->y >> vwscale));
ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale));
return 0;
}
static int ov6x30_command(struct i2c_client *c, unsigned int cmd, void *arg)
{
switch (cmd) {
case OVCAMCHIP_CMD_S_CTRL:
return ov6x30_set_control(c, arg);
case OVCAMCHIP_CMD_G_CTRL:
return ov6x30_get_control(c, arg);
case OVCAMCHIP_CMD_S_MODE:
return ov6x30_set_window(c, arg);
default:
DDEBUG(2, &c->dev, "command not supported: %d", cmd);
return -ENOIOCTLCMD;
}
}
struct ovcamchip_ops ov6x30_ops = {
.init = ov6x30_init,
.free = ov6x30_free,
.command = ov6x30_command,
};
/* OmniVision OV76BE Camera Chip Support Code
*
* Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org>
* 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. NO WARRANTY OF ANY KIND is expressed or implied.
*/
#define DEBUG
#include <linux/slab.h>
#include "ovcamchip_priv.h"
/* OV7610 registers: Since the OV76BE is undocumented, we'll settle for these
* for now. */
#define REG_GAIN 0x00 /* gain [5:0] */
#define REG_BLUE 0x01 /* blue channel balance */
#define REG_RED 0x02 /* red channel balance */
#define REG_SAT 0x03 /* saturation */
#define REG_CNT 0x05 /* Y contrast */
#define REG_BRT 0x06 /* Y brightness */
#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */
#define REG_RED_BIAS 0x0D /* red 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_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_YOFFSET 0x21 /* Y channel offset */
#define REG_UOFFSET 0x22 /* U channel offset */
#define REG_ECW 0x24 /* exposure white level for AEC */
#define REG_ECB 0x25 /* exposure black level for AEC */
#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_VOFFSET 0x2E /* V channel offset adjustment */
#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */
#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */
#define REG_BIAS_ADJUST 0x34 /* misc bias settings */
/* Window parameters */
#define HWSBASE 0x38
#define HWEBASE 0x3a
#define VWSBASE 0x05
#define VWEBASE 0x05
struct ov76be {
int auto_brt;
int auto_exp;
int bandfilt;
int mirror;
};
/* NOTE: These are the same as the 7x10 settings, but should eventually be
* optimized for the OV76BE */
static struct ovcamchip_regvals regvals_init_76be[] = {
{ 0x10, 0xff },
{ 0x16, 0x03 },
{ 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 OV76be camera chip and relevant variables. */
static int ov76be_init(struct i2c_client *c)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov76be *s;
int rc;
DDEBUG(4, &c->dev, "entered");
rc = ov_write_regvals(c, regvals_init_76be);
if (rc < 0)
return rc;
ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL);
if (!s)
return -ENOMEM;
memset(s, 0, sizeof *s);
s->auto_brt = 1;
s->auto_exp = 1;
return rc;
}
static int ov76be_free(struct i2c_client *c)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
kfree(ov->spriv);
return 0;
}
static int ov76be_set_control(struct i2c_client *c,
struct ovcamchip_control *ctl)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov76be *s = ov->spriv;
int rc;
int v = ctl->value;
switch (ctl->id) {
case OVCAMCHIP_CID_BRIGHT:
rc = ov_write(c, REG_BRT, v >> 8);
break;
case OVCAMCHIP_CID_SAT:
rc = ov_write(c, REG_SAT, v >> 8);
break;
case OVCAMCHIP_CID_EXP:
rc = ov_write(c, REG_EXP, v);
break;
case OVCAMCHIP_CID_FREQ:
{
int sixty = (v == 60);
rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80);
if (rc < 0)
goto out;
rc = ov_write(c, 0x2b, sixty?0x00:0xac);
if (rc < 0)
goto out;
rc = ov_write_mask(c, 0x76, 0x01, 0x01);
break;
}
case OVCAMCHIP_CID_BANDFILT:
rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04);
s->bandfilt = v;
break;
case OVCAMCHIP_CID_AUTOBRIGHT:
rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10);
s->auto_brt = v;
break;
case OVCAMCHIP_CID_AUTOEXP:
rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01);
s->auto_exp = v;
break;
case OVCAMCHIP_CID_MIRROR:
rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40);
s->mirror = v;
break;
default:
DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
return -EPERM;
}
out:
DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc);
return rc;
}
static int ov76be_get_control(struct i2c_client *c,
struct ovcamchip_control *ctl)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov76be *s = ov->spriv;
int rc = 0;
unsigned char val = 0;
switch (ctl->id) {
case OVCAMCHIP_CID_BRIGHT:
rc = ov_read(c, REG_BRT, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_SAT:
rc = ov_read(c, REG_SAT, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_EXP:
rc = ov_read(c, REG_EXP, &val);
ctl->value = val;
break;
case OVCAMCHIP_CID_BANDFILT:
ctl->value = s->bandfilt;
break;
case OVCAMCHIP_CID_AUTOBRIGHT:
ctl->value = s->auto_brt;
break;
case OVCAMCHIP_CID_AUTOEXP:
ctl->value = s->auto_exp;
break;
case OVCAMCHIP_CID_MIRROR:
ctl->value = s->mirror;
break;
default:
DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
return -EPERM;
}
DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc);
return rc;
}
static int ov76be_mode_init(struct i2c_client *c, struct ovcamchip_window *win)
{
int qvga = win->quarter;
/******** QVGA-specific regs ********/
ov_write(c, 0x14, qvga?0xa4:0x84);
/******** Palette-specific regs ********/
if (win->format == VIDEO_PALETTE_GREY) {
ov_write_mask(c, 0x0e, 0x40, 0x40);
ov_write_mask(c, 0x13, 0x20, 0x20);
} else {
ov_write_mask(c, 0x0e, 0x00, 0x40);
ov_write_mask(c, 0x13, 0x00, 0x20);
}
/******** Clock programming ********/
ov_write(c, 0x11, win->clockdiv);
/******** Resolution-specific ********/
if (win->width == 640 && win->height == 480)
ov_write(c, 0x35, 0x9e);
else
ov_write(c, 0x35, 0x1e);
return 0;
}
static int ov76be_set_window(struct i2c_client *c, struct ovcamchip_window *win)
{
int ret, hwscale, vwscale;
ret = ov76be_mode_init(c, win);
if (ret < 0)
return ret;
if (win->quarter) {
hwscale = 1;
vwscale = 0;
} else {
hwscale = 2;
vwscale = 1;
}
ov_write(c, 0x17, HWSBASE + (win->x >> hwscale));
ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale));
ov_write(c, 0x19, VWSBASE + (win->y >> vwscale));
ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale));
return 0;
}
static int ov76be_command(struct i2c_client *c, unsigned int cmd, void *arg)
{
switch (cmd) {
case OVCAMCHIP_CMD_S_CTRL:
return ov76be_set_control(c, arg);
case OVCAMCHIP_CMD_G_CTRL:
return ov76be_get_control(c, arg);
case OVCAMCHIP_CMD_S_MODE:
return ov76be_set_window(c, arg);
default:
DDEBUG(2, &c->dev, "command not supported: %d", cmd);
return -ENOIOCTLCMD;
}
}
struct ovcamchip_ops ov76be_ops = {
.init = ov76be_init,
.free = ov76be_free,
.command = ov76be_command,
};
/* OmniVision OV7610/OV7110 Camera Chip Support Code
*
* Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org>
* http://alpha.dyndns.org/ov511/
*
* Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
*
* 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. NO WARRANTY OF ANY KIND is expressed or implied.
*/
#define DEBUG
#include <linux/slab.h>
#include "ovcamchip_priv.h"
/* Registers */
#define REG_GAIN 0x00 /* gain [5:0] */
#define REG_BLUE 0x01 /* blue channel balance */
#define REG_RED 0x02 /* red channel balance */
#define REG_SAT 0x03 /* saturation */
#define REG_CNT 0x05 /* Y contrast */
#define REG_BRT 0x06 /* Y brightness */
#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */
#define REG_RED_BIAS 0x0D /* red 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_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_YOFFSET 0x21 /* Y channel offset */
#define REG_UOFFSET 0x22 /* U channel offset */
#define REG_ECW 0x24 /* exposure white level for AEC */
#define REG_ECB 0x25 /* exposure black level for AEC */
#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_VOFFSET 0x2E /* V channel offset adjustment */
#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */
#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */
#define REG_BIAS_ADJUST 0x34 /* misc bias settings */
/* Window parameters */
#define HWSBASE 0x38
#define HWEBASE 0x3a
#define VWSBASE 0x05
#define VWEBASE 0x05
struct ov7x10 {
int auto_brt;
int auto_exp;
int bandfilt;
int mirror;
};
/* Lawrence Glaister <lg@jfm.bc.ca> 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 ovcamchip_regvals regvals_init_7x10[] = {
{ 0x10, 0xff },
{ 0x16, 0x03 },
{ 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 camera chip and relevant variables. */
static int ov7x10_init(struct i2c_client *c)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov7x10 *s;
int rc;
DDEBUG(4, &c->dev, "entered");
rc = ov_write_regvals(c, regvals_init_7x10);
if (rc < 0)
return rc;
ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL);
if (!s)
return -ENOMEM;
memset(s, 0, sizeof *s);
s->auto_brt = 1;
s->auto_exp = 1;
return rc;
}
static int ov7x10_free(struct i2c_client *c)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
kfree(ov->spriv);
return 0;
}
static int ov7x10_set_control(struct i2c_client *c,
struct ovcamchip_control *ctl)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov7x10 *s = ov->spriv;
int rc;
int v = ctl->value;
switch (ctl->id) {
case OVCAMCHIP_CID_CONT:
rc = ov_write(c, REG_CNT, v >> 8);
break;
case OVCAMCHIP_CID_BRIGHT:
rc = ov_write(c, REG_BRT, v >> 8);
break;
case OVCAMCHIP_CID_SAT:
rc = ov_write(c, REG_SAT, v >> 8);
break;
case OVCAMCHIP_CID_HUE:
rc = ov_write(c, REG_RED, 0xFF - (v >> 8));
if (rc < 0)
goto out;
rc = ov_write(c, REG_BLUE, v >> 8);
break;
case OVCAMCHIP_CID_EXP:
rc = ov_write(c, REG_EXP, v);
break;
case OVCAMCHIP_CID_FREQ:
{
int sixty = (v == 60);
rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80);
if (rc < 0)
goto out;
rc = ov_write(c, 0x2b, sixty?0x00:0xac);
if (rc < 0)
goto out;
rc = ov_write_mask(c, 0x13, 0x10, 0x10);
if (rc < 0)
goto out;
rc = ov_write_mask(c, 0x13, 0x00, 0x10);
break;
}
case OVCAMCHIP_CID_BANDFILT:
rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04);
s->bandfilt = v;
break;
case OVCAMCHIP_CID_AUTOBRIGHT:
rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10);
s->auto_brt = v;
break;
case OVCAMCHIP_CID_AUTOEXP:
rc = ov_write_mask(c, 0x29, v?0x00:0x80, 0x80);
s->auto_exp = v;
break;
case OVCAMCHIP_CID_MIRROR:
rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40);
s->mirror = v;
break;
default:
DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
return -EPERM;
}
out:
DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc);
return rc;
}
static int ov7x10_get_control(struct i2c_client *c,
struct ovcamchip_control *ctl)
{
struct ovcamchip *ov = i2c_get_clientdata(c);
struct ov7x10 *s = ov->spriv;
int rc = 0;
unsigned char val = 0;
switch (ctl->id) {
case OVCAMCHIP_CID_CONT:
rc = ov_read(c, REG_CNT, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_BRIGHT:
rc = ov_read(c, REG_BRT, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_SAT:
rc = ov_read(c, REG_SAT, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_HUE:
rc = ov_read(c, REG_BLUE, &val);
ctl->value = val << 8;
break;
case OVCAMCHIP_CID_EXP:
rc = ov_read(c, REG_EXP, &val);
ctl->value = val;
break;
case OVCAMCHIP_CID_BANDFILT:
ctl->value = s->bandfilt;
break;
case OVCAMCHIP_CID_AUTOBRIGHT:
ctl->value = s->auto_brt;
break;
case OVCAMCHIP_CID_AUTOEXP:
ctl->value = s->auto_exp;
break;
case OVCAMCHIP_CID_MIRROR:
ctl->value = s->mirror;
break;
default:
DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
return -EPERM;
}
DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc);
return rc;
}
static int ov7x10_mode_init(struct i2c_client *c, struct ovcamchip_window *win)
{
int qvga = win->quarter;
/******** QVGA-specific regs ********/
ov_write(c, 0x14, qvga?0x24:0x04);
/******** Palette-specific regs ********/
if (win->format == VIDEO_PALETTE_GREY) {
ov_write_mask(c, 0x0e, 0x40, 0x40);
ov_write_mask(c, 0x13, 0x20, 0x20);
} else {
ov_write_mask(c, 0x0e, 0x00, 0x40);
ov_write_mask(c, 0x13, 0x00, 0x20);
}
/******** Clock programming ********/
ov_write(c, 0x11, win->clockdiv);
/******** Resolution-specific ********/
if (win->width == 640 && win->height == 480)
ov_write(c, 0x35, 0x9e);
else
ov_write(c, 0x35, 0x1e);
return 0;
}
static int ov7x10_set_window(struct i2c_client *c, struct ovcamchip_window *win)
{
int ret, hwscale, vwscale;
ret = ov7x10_mode_init(c, win);
if (ret < 0)
return ret;
if (win->quarter) {
hwscale = 1;
vwscale = 0;
} else {
hwscale = 2;
vwscale = 1;
}
ov_write(c, 0x17, HWSBASE + (win->x >> hwscale));
ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale));
ov_write(c, 0x19, VWSBASE + (win->y >> vwscale));
ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale));
return 0;
}
static int ov7x10_command(struct i2c_client *c, unsigned int cmd, void *arg)
{
switch (cmd) {
case OVCAMCHIP_CMD_S_CTRL:
return ov7x10_set_control(c, arg);
case OVCAMCHIP_CMD_G_CTRL:
return ov7x10_get_control(c, arg);
case OVCAMCHIP_CMD_S_MODE:
return ov7x10_set_window(c, arg);
default:
DDEBUG(2, &c->dev, "command not supported: %d", cmd);
return -ENOIOCTLCMD;
}
}
struct ovcamchip_ops ov7x10_ops = {
.init = ov7x10_init,
.free = ov7x10_free,
.command = ov7x10_command,
};
This diff is collapsed.
This diff is collapsed.
/* OmniVision* camera chip driver private definitions for core code and
* chip-specific code
*
* Copyright (c) 1999-2004 Mark McClelland
*
* 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. NO WARRANTY OF ANY KIND is expressed or implied.
*
* * OmniVision is a trademark of OmniVision Technologies, Inc. This driver
* is not sponsored or developed by them.
*/
#ifndef __LINUX_OVCAMCHIP_PRIV_H
#define __LINUX_OVCAMCHIP_PRIV_H
#include <media/ovcamchip.h>
#ifdef DEBUG
extern int ovcamchip_debug;
#endif
#define PDEBUG(level, fmt, args...) \
if (ovcamchip_debug >= (level)) pr_debug("[%s:%d] " fmt "\n", \
__FUNCTION__, __LINE__ , ## args)
#define DDEBUG(level, dev, fmt, args...) \
if (ovcamchip_debug >= (level)) dev_dbg(dev, "[%s:%d] " fmt "\n", \
__FUNCTION__, __LINE__ , ## args)
/* Number of times to retry chip detection. Increase this if you are getting
* "Failed to init camera chip" */
#define I2C_DETECT_RETRIES 10
struct ovcamchip_regvals {
unsigned char reg;
unsigned char val;
};
struct ovcamchip_ops {
int (*init)(struct i2c_client *);
int (*free)(struct i2c_client *);
int (*command)(struct i2c_client *, unsigned int, void *);
};
struct ovcamchip {
struct ovcamchip_ops *sops;
void *spriv; /* Private data for OV7x10.c etc... */
int subtype; /* = SEN_OV7610 etc... */
int mono; /* Monochrome chip? (invalid until init) */
int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */
};
/* --------------------------------- */
/* I2C I/O */
/* --------------------------------- */
static inline int ov_read(struct i2c_client *c, unsigned char reg,
unsigned char *value)
{
int rc;
rc = i2c_smbus_read_byte_data(c, reg);
*value = (unsigned char) rc;
return rc;
}
static inline int ov_write(struct i2c_client *c, unsigned char reg,
unsigned char value )
{
return i2c_smbus_write_byte_data(c, reg, value);
}
/* --------------------------------- */
/* FUNCTION PROTOTYPES */
/* --------------------------------- */
/* Functions in ovcamchip_core.c */
extern int ov_write_regvals(struct i2c_client *c,
struct ovcamchip_regvals *rvals);
extern int ov_write_mask(struct i2c_client *c, unsigned char reg,
unsigned char value, unsigned char mask);
#endif
/* OmniVision* camera chip driver API
*
* Copyright (c) 1999-2004 Mark McClelland
*
* 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. NO WARRANTY OF ANY KIND is expressed or implied.
*
* * OmniVision is a trademark of OmniVision Technologies, Inc. This driver
* is not sponsored or developed by them.
*/
#ifndef __LINUX_OVCAMCHIP_H
#define __LINUX_OVCAMCHIP_H
#include <linux/videodev.h>
#include <linux/i2c.h>
/* Remove these once they are officially defined */
#ifndef I2C_DRIVERID_OVCAMCHIP
#define I2C_DRIVERID_OVCAMCHIP 0xf00f
#endif
#ifndef I2C_HW_SMBUS_OV511
#define I2C_HW_SMBUS_OV511 0xfe
#endif
#ifndef I2C_HW_SMBUS_OV518
#define I2C_HW_SMBUS_OV518 0xff
#endif
#ifndef I2C_HW_SMBUS_OVFX2
#define I2C_HW_SMBUS_OVFX2 0xfd
#endif
/* --------------------------------- */
/* ENUMERATIONS */
/* --------------------------------- */
/* Controls */
enum {
OVCAMCHIP_CID_CONT, /* Contrast */
OVCAMCHIP_CID_BRIGHT, /* Brightness */
OVCAMCHIP_CID_SAT, /* Saturation */
OVCAMCHIP_CID_HUE, /* Hue */
OVCAMCHIP_CID_EXP, /* Exposure */
OVCAMCHIP_CID_FREQ, /* Light frequency */
OVCAMCHIP_CID_BANDFILT, /* Banding filter */
OVCAMCHIP_CID_AUTOBRIGHT, /* Auto brightness */
OVCAMCHIP_CID_AUTOEXP, /* Auto exposure */
OVCAMCHIP_CID_BACKLIGHT, /* Back light compensation */
OVCAMCHIP_CID_MIRROR, /* Mirror horizontally */
};
/* Chip types */
#define NUM_CC_TYPES 9
enum {
CC_UNKNOWN,
CC_OV76BE,
CC_OV7610,
CC_OV7620,
CC_OV7620AE,
CC_OV6620,
CC_OV6630,
CC_OV6630AE,
CC_OV6630AF,
};
/* --------------------------------- */
/* I2C ADDRESSES */
/* --------------------------------- */
#define OV7xx0_SID (0x42 >> 1)
#define OV6xx0_SID (0xC0 >> 1)
/* --------------------------------- */
/* API */
/* --------------------------------- */
struct ovcamchip_control {
__u32 id;
__s32 value;
};
struct ovcamchip_window {
int x;
int y;
int width;
int height;
int format;
int quarter; /* Scale width and height down 2x */
/* This stuff will be removed eventually */
int clockdiv; /* Clock divisor setting */
};
/* Commands */
#define OVCAMCHIP_CMD_Q_SUBTYPE _IOR (0x88, 0x00, int)
#define OVCAMCHIP_CMD_INITIALIZE _IOW (0x88, 0x01, int)
/* You must call OVCAMCHIP_CMD_INITIALIZE before any of commands below! */
#define OVCAMCHIP_CMD_S_CTRL _IOW (0x88, 0x02, struct ovcamchip_control)
#define OVCAMCHIP_CMD_G_CTRL _IOWR (0x88, 0x03, struct ovcamchip_control)
#define OVCAMCHIP_CMD_S_MODE _IOW (0x88, 0x04, struct ovcamchip_window)
#define OVCAMCHIP_MAX_CMD _IO (0x88, 0x3f)
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment