Commit 8a4c342b authored by Mark W. McClelland's avatar Mark W. McClelland Committed by Greg Kroah-Hartman

This updates the ov511 driver to the latest stable version (1.60).

  
  Summary of changes:
  - Remove palette conversion and related code
  - Full OV518 support (except for color), initial OV518+ support
  - Improved OV6620 quality (Credit: Alexandre F.)
  - ov51x_init_isoc() should exit if usb_submit_urb() fails
  - ISO completion handler should only issue warning if bad data is received
  - Added "mirror" module parameter; reverses image horizontally
  - Changed USB control transaction fields to match Windows driver
  - New decompression module locking (decomp_ops->owner), per Greg's advice
  - EXPORT_SYMBOL_NOVERS => EXPORT_SYMBOL
  - New devices: Lifeview USB Live TV PAL/SECAM and PAL D/K+B/G
  - Disabled buf_timeout feature. It was calling vfree() in interrupt
    context, which is illegal. Memory is deallocated immediately on close().
  - Most sensors thought to be OV7620AE were actually OV76BE. Changed code
    to reflect this, and emit a warning if an OV7620AE is encountered.
  - Miscellaneous cleanups and bug fixes
parent f5864419
...@@ -128,7 +128,7 @@ MODULE PARAMETERS: ...@@ -128,7 +128,7 @@ MODULE PARAMETERS:
programs that expect RGB data (e.g. gqcam) to work with this driver. If programs that expect RGB data (e.g. gqcam) to work with this driver. If
your colors look VERY wrong, you may want to change this. your colors look VERY wrong, you may want to change this.
NAME: buf_timeout NAME: buf_timeout (Temporarily disabled. Memory is deallocated immediately)
TYPE: integer TYPE: integer
DEFAULT: 5 (seconds) DEFAULT: 5 (seconds)
DESC: Number of seconds before unused frame buffers are deallocated. DESC: Number of seconds before unused frame buffers are deallocated.
...@@ -294,6 +294,13 @@ MODULE PARAMETERS: ...@@ -294,6 +294,13 @@ MODULE PARAMETERS:
camera cannot keep up with the speed of the USB bus (eg. at low frame camera cannot keep up with the speed of the USB bus (eg. at low frame
resolutions). This feature is always enabled when compression is on. resolutions). This feature is always enabled when compression is on.
NAME: mirror
TYPE: integer (Boolean)
DEFAULT: 0 (off)
DESC: Setting this to 1 will reverse ("mirror") the image horizontally. This
might be necessary if your camera has a custom lens assembly. This has
no effect with video capture devices.
WORKING FEATURES: WORKING FEATURES:
o Color streaming/capture at most widths and heights that are multiples of 8. o Color streaming/capture at most widths and heights that are multiples of 8.
o RGB24, RGB565, YUV420/YUV420P, YUV422/YUYV, and YUV422P color o RGB24, RGB565, YUV420/YUV420P, YUV422/YUYV, and YUV422P color
...@@ -305,6 +312,7 @@ WORKING FEATURES: ...@@ -305,6 +312,7 @@ WORKING FEATURES:
o Compression support o Compression support
EXPERIMENTAL FEATURES: EXPERIMENTAL FEATURES:
o OV518/OV518+ support
o OV6630 sensor support o OV6630 sensor support
o Banding filter o Banding filter
o SMP compatibility o SMP compatibility
...@@ -314,8 +322,6 @@ TO-DO: ...@@ -314,8 +322,6 @@ TO-DO:
o Setting of hue not working with OV7620 o Setting of hue not working with OV7620
o Setting of contrast and hue not working with OV7620AE o Setting of contrast and hue not working with OV7620AE
o OV8600 sensor support (Not used in anything yet) o OV8600 sensor support (Not used in anything yet)
o OV518/OV518+ support (all that's needed is the decompressor)
o cams >= 3 not working
HOW TO CONTACT ME: HOW TO CONTACT ME:
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
* Kernel I2C interface adapted from nt1003 driver * Kernel I2C interface adapted from nt1003 driver
* URB error messages from pwc driver by Nemosoft * URB error messages from pwc driver by Nemosoft
* generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox
* Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others
* *
* Based on the Linux CPiA driver written by Peter Pregler, * Based on the Linux CPiA driver written by Peter Pregler,
* Scott J. Bertin and Johannes Erdfelt. * Scott J. Bertin and Johannes Erdfelt.
...@@ -60,7 +61,7 @@ ...@@ -60,7 +61,7 @@
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v1.53 for Linux 2.5" #define DRIVER_VERSION "v1.60 for Linux 2.5"
#define EMAIL "mmcclell@bigfoot.com" #define EMAIL "mmcclell@bigfoot.com"
#define DRIVER_AUTHOR "Mark McClelland <mmcclell@bigfoot.com> & Bret Wallach \ #define DRIVER_AUTHOR "Mark McClelland <mmcclell@bigfoot.com> & Bret Wallach \
& Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \ & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \
...@@ -74,8 +75,9 @@ ...@@ -74,8 +75,9 @@
/* If you change this, you must also change the MODULE_PARM definition */ /* If you change this, you must also change the MODULE_PARM definition */
#define OV511_MAX_UNIT_VIDEO 16 #define OV511_MAX_UNIT_VIDEO 16
/* Pixel count * 3 bytes for RGB */ /* Pixel count * bytes per YUV420 pixel (1.5) */
#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3) #define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3 / 2)
#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval)) #define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))
/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */ /* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
...@@ -93,10 +95,7 @@ static int autobright = 1; ...@@ -93,10 +95,7 @@ static int autobright = 1;
static int autogain = 1; static int autogain = 1;
static int autoexp = 1; static int autoexp = 1;
static int debug; static int debug;
static int fix_rgb_offset;
static int snapshot; static int snapshot;
static int force_rgb;
static int buf_timeout = 5;
static int cams = 1; static int cams = 1;
static int compress; static int compress;
static int testpat; static int testpat;
...@@ -132,6 +131,7 @@ static int tuner = -1; ...@@ -132,6 +131,7 @@ static int tuner = -1;
static int backlight; static int backlight;
static int unit_video[OV511_MAX_UNIT_VIDEO]; static int unit_video[OV511_MAX_UNIT_VIDEO];
static int remove_zeros; static int remove_zeros;
static int mirror;
MODULE_PARM(autobright, "i"); MODULE_PARM(autobright, "i");
MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness"); MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
...@@ -142,15 +142,8 @@ MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure"); ...@@ -142,15 +142,8 @@ MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
MODULE_PARM(debug, "i"); MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, MODULE_PARM_DESC(debug,
"Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max"); "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
MODULE_PARM(fix_rgb_offset, "i");
MODULE_PARM_DESC(fix_rgb_offset,
"Fix vertical misalignment of red and blue at 640x480");
MODULE_PARM(snapshot, "i"); MODULE_PARM(snapshot, "i");
MODULE_PARM_DESC(snapshot, "Enable snapshot mode"); MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
MODULE_PARM(force_rgb, "i");
MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR");
MODULE_PARM(buf_timeout, "i");
MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation");
MODULE_PARM(cams, "i"); MODULE_PARM(cams, "i");
MODULE_PARM_DESC(cams, "Number of simultaneous cameras"); MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
MODULE_PARM(compress, "i"); MODULE_PARM(compress, "i");
...@@ -158,11 +151,6 @@ MODULE_PARM_DESC(compress, "Turn on compression (not reliable yet)"); ...@@ -158,11 +151,6 @@ MODULE_PARM_DESC(compress, "Turn on compression (not reliable yet)");
MODULE_PARM(testpat, "i"); MODULE_PARM(testpat, "i");
MODULE_PARM_DESC(testpat, MODULE_PARM_DESC(testpat,
"Replace image with vertical bar testpattern (only partially working)"); "Replace image with vertical bar testpattern (only partially working)");
// Temporarily removed (needs to be rewritten for new format conversion code)
// MODULE_PARM(sensor_gbr, "i");
// MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
MODULE_PARM(dumppix, "i"); MODULE_PARM(dumppix, "i");
MODULE_PARM_DESC(dumppix, "Dump raw pixel data"); MODULE_PARM_DESC(dumppix, "Dump raw pixel data");
MODULE_PARM(led, "i"); MODULE_PARM(led, "i");
...@@ -216,6 +204,8 @@ MODULE_PARM_DESC(unit_video, ...@@ -216,6 +204,8 @@ MODULE_PARM_DESC(unit_video,
MODULE_PARM(remove_zeros, "i"); MODULE_PARM(remove_zeros, "i");
MODULE_PARM_DESC(remove_zeros, MODULE_PARM_DESC(remove_zeros,
"Remove zero-padding from uncompressed incoming data"); "Remove zero-padding from uncompressed incoming data");
MODULE_PARM(mirror, "i");
MODULE_PARM_DESC(mirror, "Reverse image horizontally");
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
...@@ -268,11 +258,13 @@ static struct symbolic_list camlist[] = { ...@@ -268,11 +258,13 @@ static struct symbolic_list camlist[] = {
{ 5, "Puretek PT-6007" }, { 5, "Puretek PT-6007" },
{ 6, "Lifeview USB Life TV (NTSC)" }, { 6, "Lifeview USB Life TV (NTSC)" },
{ 21, "Creative Labs WebCam 3" }, { 21, "Creative Labs WebCam 3" },
{ 22, "Lifeview USB Life TV (PAL D/K+B/G)" },
{ 36, "Koala-Cam" }, { 36, "Koala-Cam" },
{ 38, "Lifeview USB Life TV" }, { 38, "Lifeview USB Life TV (PAL)" },
{ 41, "Samsung Anycam MPC-M10" }, { 41, "Samsung Anycam MPC-M10" },
{ 43, "Mtekvision Zeca MV402" }, { 43, "Mtekvision Zeca MV402" },
{ 46, "Suma eON" }, { 46, "Suma eON" },
{ 70, "Lifeview USB Life TV (PAL/SECAM)" },
{ 100, "Lifeview RoboCam" }, { 100, "Lifeview RoboCam" },
{ 102, "AverMedia InterCam Elite" }, { 102, "AverMedia InterCam Elite" },
{ 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */ { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */
...@@ -349,9 +341,7 @@ static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int, ...@@ -349,9 +341,7 @@ static int ov51x_control_ioctl(struct inode *, struct file *, unsigned int,
unsigned long); unsigned long);
/********************************************************************** /**********************************************************************
*
* Memory management * Memory management
*
**********************************************************************/ **********************************************************************/
/* Here we want the physical address of the memory. /* Here we want the physical address of the memory.
...@@ -452,8 +442,6 @@ ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof, ...@@ -452,8 +442,6 @@ ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof,
out += sprintf(out, "subcapture : %s\n", YES_NO(ov->sub_flag)); out += sprintf(out, "subcapture : %s\n", YES_NO(ov->sub_flag));
out += sprintf(out, "sub_size : %d %d %d %d\n", out += sprintf(out, "sub_size : %d %d %d %d\n",
ov->subx, ov->suby, ov->subw, ov->subh); ov->subx, ov->suby, ov->subw, ov->subh);
out += sprintf(out, "data_format : %s\n",
force_rgb ? "RGB" : "BGR");
out += sprintf(out, "brightness : %d\n", p.brightness >> 8); out += sprintf(out, "brightness : %d\n", p.brightness >> 8);
out += sprintf(out, "colour : %d\n", p.colour >> 8); out += sprintf(out, "colour : %d\n", p.colour >> 8);
out += sprintf(out, "contrast : %d\n", p.contrast >> 8); out += sprintf(out, "contrast : %d\n", p.contrast >> 8);
...@@ -675,8 +663,8 @@ reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value) ...@@ -675,8 +663,8 @@ reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value)
ov->cbuf[0] = value; ov->cbuf[0] = value;
rc = usb_control_msg(ov->dev, rc = usb_control_msg(ov->dev,
usb_sndctrlpipe(ov->dev, 0), usb_sndctrlpipe(ov->dev, 0),
2 /* REG_IO */, (ov->bclass == BCL_OV518)?1:2 /* REG_IO */,
USB_TYPE_CLASS | USB_RECIP_DEVICE, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, (__u16)reg, &ov->cbuf[0], 1, HZ); 0, (__u16)reg, &ov->cbuf[0], 1, HZ);
up(&ov->cbuf_lock); up(&ov->cbuf_lock);
...@@ -696,8 +684,8 @@ reg_r(struct usb_ov511 *ov, unsigned char reg) ...@@ -696,8 +684,8 @@ reg_r(struct usb_ov511 *ov, unsigned char reg)
down(&ov->cbuf_lock); down(&ov->cbuf_lock);
rc = usb_control_msg(ov->dev, rc = usb_control_msg(ov->dev,
usb_rcvctrlpipe(ov->dev, 0), usb_rcvctrlpipe(ov->dev, 0),
2 /* REG_IO */, (ov->bclass == BCL_OV518)?1:3 /* REG_IO */,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, (__u16)reg, &ov->cbuf[0], 1, HZ); 0, (__u16)reg, &ov->cbuf[0], 1, HZ);
PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]); PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]);
...@@ -756,8 +744,8 @@ ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n) ...@@ -756,8 +744,8 @@ ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n)
rc = usb_control_msg(ov->dev, rc = usb_control_msg(ov->dev,
usb_sndctrlpipe(ov->dev, 0), usb_sndctrlpipe(ov->dev, 0),
2 /* REG_IO */, 1 /* REG_IO */,
USB_TYPE_CLASS | USB_RECIP_DEVICE, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, (__u16)reg, ov->cbuf, n, HZ); 0, (__u16)reg, ov->cbuf, n, HZ);
up(&ov->cbuf_lock); up(&ov->cbuf_lock);
...@@ -875,7 +863,7 @@ ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type) ...@@ -875,7 +863,7 @@ ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type)
/********************************************************************** /**********************************************************************
* *
* I2C (sensor) I/O * Low-level I2C I/O functions
* *
**********************************************************************/ **********************************************************************/
...@@ -1135,6 +1123,25 @@ i2c_w_mask(struct usb_ov511 *ov, ...@@ -1135,6 +1123,25 @@ i2c_w_mask(struct usb_ov511 *ov,
return rc; return rc;
} }
/* Set the read and write slave IDs. The "slave" argument is the write slave,
* and the read slave will be set to (slave + 1). ov->i2c_lock should be held
* when calling this. This should not be called from outside the i2c I/O
* functions.
*/
static inline int
i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave)
{
int rc;
rc = reg_w(ov, R51x_I2C_W_SID, slave);
if (rc < 0) return rc;
rc = reg_w(ov, R51x_I2C_R_SID, slave + 1);
if (rc < 0) return rc;
return 0;
}
/* Write to a specific I2C slave ID and register, using the specified mask */ /* Write to a specific I2C slave ID and register, using the specified mask */
static int static int
i2c_w_slave(struct usb_ov511 *ov, i2c_w_slave(struct usb_ov511 *ov,
...@@ -1148,32 +1155,16 @@ i2c_w_slave(struct usb_ov511 *ov, ...@@ -1148,32 +1155,16 @@ i2c_w_slave(struct usb_ov511 *ov,
down(&ov->i2c_lock); down(&ov->i2c_lock);
/* Set new slave IDs */ /* Set new slave IDs */
if (reg_w(ov, R51x_I2C_W_SID, slave) < 0) { rc = i2c_set_slave_internal(ov, slave);
rc = -EIO; if (rc < 0) goto out;
goto out;
}
if (reg_w(ov, R51x_I2C_R_SID, slave + 1) < 0) {
rc = -EIO;
goto out;
}
rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask); rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask);
/* Don't bail out yet if error; IDs must be restored */
out:
/* Restore primary IDs */ /* Restore primary IDs */
slave = ov->primary_i2c_slave; if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0)
if (reg_w(ov, R51x_I2C_W_SID, slave) < 0) { err("Couldn't restore primary I2C slave");
rc = -EIO;
goto out;
}
if (reg_w(ov, R51x_I2C_R_SID, slave + 1) < 0) {
rc = -EIO;
goto out;
}
out:
up(&ov->i2c_lock); up(&ov->i2c_lock);
return rc; return rc;
} }
...@@ -1189,35 +1180,19 @@ i2c_r_slave(struct usb_ov511 *ov, ...@@ -1189,35 +1180,19 @@ i2c_r_slave(struct usb_ov511 *ov,
down(&ov->i2c_lock); down(&ov->i2c_lock);
/* Set new slave IDs */ /* Set new slave IDs */
if (reg_w(ov, R51x_I2C_W_SID, slave) < 0) { rc = i2c_set_slave_internal(ov, slave);
rc = -EIO; if (rc < 0) goto out;
goto out;
}
if (reg_w(ov, R51x_I2C_R_SID, slave + 1) < 0) {
rc = -EIO;
goto out;
}
if (ov->bclass == BCL_OV518) if (ov->bclass == BCL_OV518)
rc = ov518_i2c_read_internal(ov, reg); rc = ov518_i2c_read_internal(ov, reg);
else else
rc = ov511_i2c_read_internal(ov, reg); rc = ov511_i2c_read_internal(ov, reg);
/* Don't bail out yet if error; IDs must be restored */
out:
/* Restore primary IDs */ /* Restore primary IDs */
slave = ov->primary_i2c_slave; if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0)
if (reg_w(ov, R51x_I2C_W_SID, slave) < 0) { err("Couldn't restore primary I2C slave");
rc = -EIO;
goto out;
}
if (reg_w(ov, R51x_I2C_R_SID, slave + 1) < 0) {
rc = -EIO;
goto out;
}
out:
up(&ov->i2c_lock); up(&ov->i2c_lock);
return rc; return rc;
} }
...@@ -1226,20 +1201,20 @@ i2c_r_slave(struct usb_ov511 *ov, ...@@ -1226,20 +1201,20 @@ i2c_r_slave(struct usb_ov511 *ov,
static int static int
ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid) ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid)
{ {
down(&ov->i2c_lock); int rc;
if (reg_w(ov, R51x_I2C_W_SID, sid) < 0) down(&ov->i2c_lock);
return -EIO;
if (reg_w(ov, R51x_I2C_R_SID, sid + 1) < 0) rc = i2c_set_slave_internal(ov, sid);
return -EIO; if (rc < 0) goto out;
if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) // FIXME: Is this actually necessary?
return -EIO; rc = ov51x_reset(ov, OV511_RESET_NOREGS);
if (rc < 0) goto out;
out:
up(&ov->i2c_lock); up(&ov->i2c_lock);
return rc;
return 0;
} }
static int static int
...@@ -1272,7 +1247,7 @@ dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn) ...@@ -1272,7 +1247,7 @@ dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn)
for (i = reg1; i <= regn; i++) { for (i = reg1; i <= regn; i++) {
rc = i2c_r(ov, i); rc = i2c_r(ov, i);
info("OV7610[0x%X] = 0x%X", i, rc); info("Sensor[0x%X] = 0x%X", i, rc);
} }
} }
...@@ -1325,7 +1300,7 @@ ov511_dump_regs(struct usb_ov511 *ov) ...@@ -1325,7 +1300,7 @@ ov511_dump_regs(struct usb_ov511 *ov)
/********************************************************************** /**********************************************************************
* *
* Kernel I2C Interface * Kernel I2C Interface (not supported with OV518/OV518+)
* *
**********************************************************************/ **********************************************************************/
...@@ -1347,7 +1322,7 @@ ov51x_stop(struct usb_ov511 *ov) ...@@ -1347,7 +1322,7 @@ ov51x_stop(struct usb_ov511 *ov)
PDEBUG(4, "stopping"); PDEBUG(4, "stopping");
ov->stopped = 1; ov->stopped = 1;
if (ov->bclass == BCL_OV518) if (ov->bclass == BCL_OV518)
return (reg_w(ov, R51x_SYS_RESET, 0x3a)); return (reg_w_mask(ov, R51x_SYS_RESET, 0x3a, 0x3a));
else else
return (reg_w(ov, R51x_SYS_RESET, 0x3d)); return (reg_w(ov, R51x_SYS_RESET, 0x3d));
} }
...@@ -1448,7 +1423,7 @@ init_ov_sensor(struct usb_ov511 *ov) ...@@ -1448,7 +1423,7 @@ init_ov_sensor(struct usb_ov511 *ov)
} }
static int static int
ov51x_set_packet_size(struct usb_ov511 *ov, int size) ov511_set_packet_size(struct usb_ov511 *ov, int size)
{ {
int alt, mult; int alt, mult;
...@@ -1480,7 +1455,44 @@ ov51x_set_packet_size(struct usb_ov511 *ov, int size) ...@@ -1480,7 +1455,44 @@ ov51x_set_packet_size(struct usb_ov511 *ov, int size)
err("Set packet size: invalid size (%d)", size); err("Set packet size: invalid size (%d)", size);
return -EINVAL; return -EINVAL;
} }
} else if (ov->bclass == BCL_OV518) { } else {
err("Set packet size: Invalid bridge type");
return -EINVAL;
}
PDEBUG(3, "%d, mult=%d, alt=%d", size, mult, alt);
if (reg_w(ov, R51x_FIFO_PSIZE, mult) < 0)
return -EIO;
if (usb_set_interface(ov->dev, ov->iface, alt) < 0) {
err("Set packet size: set interface error");
return -EBUSY;
}
if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
return -EIO;
ov->packet_size = size;
if (ov51x_restart(ov) < 0)
return -EIO;
return 0;
}
/* Note: Unlike the OV511/OV511+, the size argument does NOT include the
* optional packet number byte. The actual size *is* stored in ov->packet_size,
* though. */
static int
ov518_set_packet_size(struct usb_ov511 *ov, int size)
{
int alt;
if (ov51x_stop(ov) < 0)
return -EIO;
if (ov->bclass == BCL_OV518) {
if (size == 0) alt = OV518_ALT_SIZE_0; if (size == 0) alt = OV518_ALT_SIZE_0;
else if (size == 128) alt = OV518_ALT_SIZE_128; else if (size == 128) alt = OV518_ALT_SIZE_128;
else if (size == 256) alt = OV518_ALT_SIZE_256; else if (size == 256) alt = OV518_ALT_SIZE_256;
...@@ -1498,14 +1510,15 @@ ov51x_set_packet_size(struct usb_ov511 *ov, int size) ...@@ -1498,14 +1510,15 @@ ov51x_set_packet_size(struct usb_ov511 *ov, int size)
return -EINVAL; return -EINVAL;
} }
PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt); PDEBUG(3, "%d, alt=%d", size, alt);
// FIXME: Don't know how to do this on OV518 yet ov->packet_size = size;
if (ov->bclass == BCL_OV511) { if (size > 0) {
if (reg_w(ov, R51x_FIFO_PSIZE, /* Program ISO FIFO size reg (packet number isn't included) */
mult) < 0) { ov518_reg_w32(ov, 0x30, size, 2);
return -EIO;
} if (ov->packet_numbering)
++ov->packet_size;
} }
if (usb_set_interface(ov->dev, ov->iface, alt) < 0) { if (usb_set_interface(ov->dev, ov->iface, alt) < 0) {
...@@ -1514,17 +1527,13 @@ ov51x_set_packet_size(struct usb_ov511 *ov, int size) ...@@ -1514,17 +1527,13 @@ ov51x_set_packet_size(struct usb_ov511 *ov, int size)
} }
/* Initialize the stream */ /* Initialize the stream */
if (ov->bclass == BCL_OV518)
if (reg_w(ov, 0x2f, 0x80) < 0) if (reg_w(ov, 0x2f, 0x80) < 0)
return -EIO; return -EIO;
// FIXME - Should we only reset the FIFO? if (ov51x_restart(ov) < 0)
if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
return -EIO; return -EIO;
ov->packet_size = size; if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
if (ov51x_restart(ov) < 0)
return -EIO; return -EIO;
return 0; return 0;
...@@ -1596,13 +1605,19 @@ sensor_set_contrast(struct usb_ov511 *ov, unsigned short val) ...@@ -1596,13 +1605,19 @@ sensor_set_contrast(struct usb_ov511 *ov, unsigned short val)
switch (ov->sensor) { switch (ov->sensor) {
case SEN_OV7610: case SEN_OV7610:
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630:
{ {
rc = i2c_w(ov, OV7610_REG_CNT, val >> 8); rc = i2c_w(ov, OV7610_REG_CNT, val >> 8);
if (rc < 0) if (rc < 0)
goto out; goto out;
break; break;
} }
case SEN_OV6630:
{
rc = i2c_w_mask(ov, OV7610_REG_CNT, val >> 12, 0x0f);
if (rc < 0)
goto out;
break;
}
case SEN_OV7620: case SEN_OV7620:
{ {
unsigned char ctab[] = { unsigned char ctab[] = {
...@@ -1649,13 +1664,19 @@ sensor_get_contrast(struct usb_ov511 *ov, unsigned short *val) ...@@ -1649,13 +1664,19 @@ sensor_get_contrast(struct usb_ov511 *ov, unsigned short *val)
switch (ov->sensor) { switch (ov->sensor) {
case SEN_OV7610: case SEN_OV7610:
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630:
rc = i2c_r(ov, OV7610_REG_CNT); rc = i2c_r(ov, OV7610_REG_CNT);
if (rc < 0) if (rc < 0)
return rc; return rc;
else else
*val = rc << 8; *val = rc << 8;
break; break;
case SEN_OV6630:
rc = i2c_r(ov, OV7610_REG_CNT);
if (rc < 0)
return rc;
else
*val = rc << 12;
break;
case SEN_OV7620: case SEN_OV7620:
/* Use Y gamma reg instead. Bit 0 is the enable bit. */ /* Use Y gamma reg instead. Bit 0 is the enable bit. */
rc = i2c_r(ov, 0x64); rc = i2c_r(ov, 0x64);
...@@ -1694,7 +1715,7 @@ sensor_set_brightness(struct usb_ov511 *ov, unsigned short val) ...@@ -1694,7 +1715,7 @@ sensor_set_brightness(struct usb_ov511 *ov, unsigned short val)
switch (ov->sensor) { switch (ov->sensor) {
case SEN_OV7610: case SEN_OV7610:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630: case SEN_OV6630:
rc = i2c_w(ov, OV7610_REG_BRT, val >> 8); rc = i2c_w(ov, OV7610_REG_BRT, val >> 8);
...@@ -1737,7 +1758,7 @@ sensor_get_brightness(struct usb_ov511 *ov, unsigned short *val) ...@@ -1737,7 +1758,7 @@ sensor_get_brightness(struct usb_ov511 *ov, unsigned short *val)
switch (ov->sensor) { switch (ov->sensor) {
case SEN_OV7610: case SEN_OV7610:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV7620: case SEN_OV7620:
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630: case SEN_OV6630:
...@@ -1777,7 +1798,7 @@ sensor_set_saturation(struct usb_ov511 *ov, unsigned short val) ...@@ -1777,7 +1798,7 @@ sensor_set_saturation(struct usb_ov511 *ov, unsigned short val)
switch (ov->sensor) { switch (ov->sensor) {
case SEN_OV7610: case SEN_OV7610:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630: case SEN_OV6630:
rc = i2c_w(ov, OV7610_REG_SAT, val >> 8); rc = i2c_w(ov, OV7610_REG_SAT, val >> 8);
...@@ -1821,7 +1842,7 @@ sensor_get_saturation(struct usb_ov511 *ov, unsigned short *val) ...@@ -1821,7 +1842,7 @@ sensor_get_saturation(struct usb_ov511 *ov, unsigned short *val)
switch (ov->sensor) { switch (ov->sensor) {
case SEN_OV7610: case SEN_OV7610:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630: case SEN_OV6630:
rc = i2c_r(ov, OV7610_REG_SAT); rc = i2c_r(ov, OV7610_REG_SAT);
...@@ -2020,6 +2041,7 @@ sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p) ...@@ -2020,6 +2041,7 @@ sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p)
return 0; return 0;
} }
#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
// FIXME: Exposure range is only 0x00-0x7f in interlace mode // FIXME: Exposure range is only 0x00-0x7f in interlace mode
/* Sets current exposure for sensor. This only has an effect if auto-exposure /* Sets current exposure for sensor. This only has an effect if auto-exposure
* is off */ * is off */
...@@ -2039,7 +2061,7 @@ sensor_set_exposure(struct usb_ov511 *ov, unsigned char val) ...@@ -2039,7 +2061,7 @@ sensor_set_exposure(struct usb_ov511 *ov, unsigned char val)
case SEN_OV6630: case SEN_OV6630:
case SEN_OV7610: case SEN_OV7610:
case SEN_OV7620: case SEN_OV7620:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV8600: case SEN_OV8600:
rc = i2c_w(ov, 0x10, val); rc = i2c_w(ov, 0x10, val);
if (rc < 0) if (rc < 0)
...@@ -2077,7 +2099,7 @@ sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val) ...@@ -2077,7 +2099,7 @@ sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val)
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630: case SEN_OV6630:
case SEN_OV7620: case SEN_OV7620:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV8600: case SEN_OV8600:
rc = i2c_r(ov, 0x10); rc = i2c_r(ov, 0x10);
if (rc < 0) if (rc < 0)
...@@ -2101,6 +2123,7 @@ sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val) ...@@ -2101,6 +2123,7 @@ sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val)
return 0; return 0;
} }
#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */ /* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
static inline void static inline void
...@@ -2121,7 +2144,7 @@ ov51x_led_control(struct usb_ov511 *ov, int enable) ...@@ -2121,7 +2144,7 @@ ov51x_led_control(struct usb_ov511 *ov, int enable)
* 50 - 50Hz, for European and Asian lighting * 50 - 50Hz, for European and Asian lighting
* 60 - 60Hz, for American lighting * 60 - 60Hz, for American lighting
* *
* Tested with: OV7610, OV7620, OV7620AE, OV6620 * Tested with: OV7610, OV7620, OV76BE, OV6620
* Unsupported: KS0127, KS0127B, SAA7111A * Unsupported: KS0127, KS0127B, SAA7111A
* Returns: 0 for success * Returns: 0 for success
*/ */
...@@ -2149,7 +2172,7 @@ sensor_set_light_freq(struct usb_ov511 *ov, int freq) ...@@ -2149,7 +2172,7 @@ sensor_set_light_freq(struct usb_ov511 *ov, int freq)
i2c_w_mask(ov, 0x13, 0x00, 0x10); i2c_w_mask(ov, 0x13, 0x00, 0x10);
break; break;
case SEN_OV7620: case SEN_OV7620:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV8600: case SEN_OV8600:
i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80); i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80);
i2c_w(ov, 0x2b, sixty?0x00:0xac); i2c_w(ov, 0x2b, sixty?0x00:0xac);
...@@ -2180,7 +2203,7 @@ sensor_set_light_freq(struct usb_ov511 *ov, int freq) ...@@ -2180,7 +2203,7 @@ sensor_set_light_freq(struct usb_ov511 *ov, int freq)
* caused by some (usually fluorescent) lighting. The light frequency must be * caused by some (usually fluorescent) lighting. The light frequency must be
* set either before or after enabling it with ov51x_set_light_freq(). * set either before or after enabling it with ov51x_set_light_freq().
* *
* Tested with: OV7610, OV7620, OV7620AE, OV6620. * Tested with: OV7610, OV7620, OV76BE, OV6620.
* Unsupported: KS0127, KS0127B, SAA7111A * Unsupported: KS0127, KS0127B, SAA7111A
* Returns: 0 for success * Returns: 0 for success
*/ */
...@@ -2251,7 +2274,7 @@ sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) ...@@ -2251,7 +2274,7 @@ sensor_set_auto_exposure(struct usb_ov511 *ov, int enable)
break; break;
case SEN_OV6620: case SEN_OV6620:
case SEN_OV7620: case SEN_OV7620:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV8600: case SEN_OV8600:
i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01); i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01);
break; break;
...@@ -2277,13 +2300,12 @@ sensor_set_auto_exposure(struct usb_ov511 *ov, int enable) ...@@ -2277,13 +2300,12 @@ sensor_set_auto_exposure(struct usb_ov511 *ov, int enable)
* that are illuminated from behind. * that are illuminated from behind.
* *
* Tested with: OV6620, OV7620 * Tested with: OV6620, OV7620
* Unsupported: OV7610, OV7620AE, KS0127, KS0127B, SAA7111A * Unsupported: OV7610, OV76BE, KS0127, KS0127B, SAA7111A
* Returns: 0 for success * Returns: 0 for success
*/ */
static int static int
sensor_set_backlight(struct usb_ov511 *ov, int enable) sensor_set_backlight(struct usb_ov511 *ov, int enable)
{ {
PDEBUG(4, " (%s)", enable ? "turn on" : "turn off"); PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
switch (ov->sensor) { switch (ov->sensor) {
...@@ -2304,7 +2326,7 @@ sensor_set_backlight(struct usb_ov511 *ov, int enable) ...@@ -2304,7 +2326,7 @@ sensor_set_backlight(struct usb_ov511 *ov, int enable)
i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02); i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02);
break; break;
case SEN_OV7610: case SEN_OV7610:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_KS0127: case SEN_KS0127:
case SEN_KS0127B: case SEN_KS0127B:
case SEN_SAA7111A: case SEN_SAA7111A:
...@@ -2320,6 +2342,35 @@ sensor_set_backlight(struct usb_ov511 *ov, int enable) ...@@ -2320,6 +2342,35 @@ sensor_set_backlight(struct usb_ov511 *ov, int enable)
return 0; return 0;
} }
static inline int
sensor_set_mirror(struct usb_ov511 *ov, int enable)
{
PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
switch (ov->sensor) {
case SEN_OV6620:
case SEN_OV6630:
case SEN_OV7610:
case SEN_OV7620:
case SEN_OV76BE:
case SEN_OV8600:
i2c_w_mask(ov, 0x12, enable?0x40:0x00, 0x40);
break;
case SEN_KS0127:
case SEN_KS0127B:
case SEN_SAA7111A:
PDEBUG(5, "Unsupported with this sensor");
return -EPERM;
default:
err("Sensor not supported for set_mirror");
return -EINVAL;
}
ov->mirror = enable;
return 0;
}
/* Returns number of bits per pixel (regardless of where they are located; /* Returns number of bits per pixel (regardless of where they are located;
* planar or not), or zero for unsupported format. * planar or not), or zero for unsupported format.
*/ */
...@@ -2330,11 +2381,6 @@ get_depth(int palette) ...@@ -2330,11 +2381,6 @@ get_depth(int palette)
case VIDEO_PALETTE_GREY: return 8; case VIDEO_PALETTE_GREY: return 8;
case VIDEO_PALETTE_YUV420: return 12; case VIDEO_PALETTE_YUV420: return 12;
case VIDEO_PALETTE_YUV420P: return 12; /* Planar */ case VIDEO_PALETTE_YUV420P: return 12; /* Planar */
case VIDEO_PALETTE_RGB565: return 16;
case VIDEO_PALETTE_RGB24: return 24;
case VIDEO_PALETTE_YUV422: return 16;
case VIDEO_PALETTE_YUYV: return 16;
case VIDEO_PALETTE_YUV422P: return 16; /* Planar */
default: return 0; /* Invalid format */ default: return 0; /* Invalid format */
} }
} }
...@@ -2380,7 +2426,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, ...@@ -2380,7 +2426,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height,
i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0); i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0);
i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20); i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20);
break; break;
case SEN_OV7620AE: case SEN_OV76BE:
// i2c_w(ov, 0x2b, 0x00); // i2c_w(ov, 0x2b, 0x00);
i2c_w(ov, 0x14, qvga?0xa4:0x84); i2c_w(ov, 0x14, qvga?0xa4:0x84);
// FIXME: Enable this once 7620AE uses 7620 initial settings // FIXME: Enable this once 7620AE uses 7620 initial settings
...@@ -2406,13 +2452,13 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, ...@@ -2406,13 +2452,13 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height,
/******** Palette-specific regs ********/ /******** Palette-specific regs ********/
if (mode == VIDEO_PALETTE_GREY) { if (mode == VIDEO_PALETTE_GREY) {
if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV7620AE) { if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
/* these aren't valid on the OV6620/OV7620/6630? */ /* these aren't valid on the OV6620/OV7620/6630? */
i2c_w_mask(ov, 0x0e, 0x40, 0x40); i2c_w_mask(ov, 0x0e, 0x40, 0x40);
} }
i2c_w_mask(ov, 0x13, 0x20, 0x20); i2c_w_mask(ov, 0x13, 0x20, 0x20);
} else { } else {
if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV7620AE) { if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
/* not valid on the OV6620/OV7620/6630? */ /* not valid on the OV6620/OV7620/6630? */
i2c_w_mask(ov, 0x0e, 0x00, 0x40); i2c_w_mask(ov, 0x0e, 0x00, 0x40);
} }
...@@ -2474,8 +2520,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, ...@@ -2474,8 +2520,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height,
if (framedrop >= 0) if (framedrop >= 0)
i2c_w(ov, 0x16, framedrop); i2c_w(ov, 0x16, framedrop);
/* We only have code to convert GBR -> RGB24 */ if (sensor_gbr)
if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
i2c_w_mask(ov, 0x12, 0x08, 0x08); i2c_w_mask(ov, 0x12, 0x08, 0x08);
else else
i2c_w_mask(ov, 0x12, 0x00, 0x08); i2c_w_mask(ov, 0x12, 0x00, 0x08);
...@@ -2492,7 +2537,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height, ...@@ -2492,7 +2537,7 @@ mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height,
// This will go away as soon as ov51x_mode_init_sensor_regs() // This will go away as soon as ov51x_mode_init_sensor_regs()
// is fully tested. // is fully tested.
/* 7620/6620/6630? don't have register 0x35, so play it safe */ /* 7620/6620/6630? don't have register 0x35, so play it safe */
if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV7620AE) { if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
if (width == 640 && height == 480) if (width == 640 && height == 480)
i2c_w(ov, 0x35, 0x9e); i2c_w(ov, 0x35, 0x9e);
else else
...@@ -2514,13 +2559,13 @@ set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode, ...@@ -2514,13 +2559,13 @@ set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode,
* IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */ * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */
switch (ov->sensor) { switch (ov->sensor) {
case SEN_OV7610: case SEN_OV7610:
case SEN_OV7620AE: case SEN_OV76BE:
hwsbase = 0x38; hwsbase = 0x38;
hwebase = 0x3a; hwebase = 0x3a;
vwsbase = vwebase = 0x05; vwsbase = vwebase = 0x05;
break; break;
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630: // FIXME: Is this right? case SEN_OV6630:
hwsbase = 0x38; hwsbase = 0x38;
hwebase = 0x3a; hwebase = 0x3a;
vwsbase = 0x05; vwsbase = 0x05;
...@@ -2537,7 +2582,9 @@ set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode, ...@@ -2537,7 +2582,9 @@ set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode,
} }
if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) { if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) {
if (width > 176 && height > 144) { /* CIF */ /* Note: OV518(+) does downsample on its own) */
if ((width > 176 && height > 144)
|| ov->bclass == BCL_OV518) { /* CIF */
ret = mode_init_ov_sensor_regs(ov, width, height, ret = mode_init_ov_sensor_regs(ov, width, height,
mode, sub_flag, 0); mode, sub_flag, 0);
if (ret < 0) if (ret < 0)
...@@ -2614,7 +2661,7 @@ static int ...@@ -2614,7 +2661,7 @@ static int
ov511_mode_init_regs(struct usb_ov511 *ov, ov511_mode_init_regs(struct usb_ov511 *ov,
int width, int height, int mode, int sub_flag) int width, int height, int mode, int sub_flag)
{ {
int lncnt, pxcnt, rc = 0; int hsegs, vsegs;
if (sub_flag) { if (sub_flag) {
width = ov->subw; width = ov->subw;
...@@ -2630,11 +2677,11 @@ ov511_mode_init_regs(struct usb_ov511 *ov, ...@@ -2630,11 +2677,11 @@ ov511_mode_init_regs(struct usb_ov511 *ov,
if (width == 320 && height == 240) { if (width == 320 && height == 240) {
/* No need to do anything special */ /* No need to do anything special */
} else if (width == 640 && height == 480) { } else if (width == 640 && height == 480) {
/* Set the OV511 up as 320x480, but keep the V4L /* Set the OV511 up as 320x480, but keep the
* resolution as 640x480 */ * V4L resolution as 640x480 */
width = 320; width = 320;
} else { } else {
err("SAA7111A only supports 320x240 or 640x480"); err("SAA7111A only allows 320x240 or 640x480");
return -EINVAL; return -EINVAL;
} }
} }
...@@ -2666,11 +2713,11 @@ ov511_mode_init_regs(struct usb_ov511 *ov, ...@@ -2666,11 +2713,11 @@ ov511_mode_init_regs(struct usb_ov511 *ov,
/* Here I'm assuming that snapshot size == image size. /* Here I'm assuming that snapshot size == image size.
* I hope that's always true. --claudio * I hope that's always true. --claudio
*/ */
pxcnt = (width >> 3) - 1; hsegs = (width >> 3) - 1;
lncnt = (height >> 3) - 1; vsegs = (height >> 3) - 1;
reg_w(ov, R511_CAM_PXCNT, pxcnt); reg_w(ov, R511_CAM_PXCNT, hsegs);
reg_w(ov, R511_CAM_LNCNT, lncnt); reg_w(ov, R511_CAM_LNCNT, vsegs);
reg_w(ov, R511_CAM_PXDIV, 0x00); reg_w(ov, R511_CAM_PXDIV, 0x00);
reg_w(ov, R511_CAM_LNDIV, 0x00); reg_w(ov, R511_CAM_LNDIV, 0x00);
...@@ -2678,8 +2725,8 @@ ov511_mode_init_regs(struct usb_ov511 *ov, ...@@ -2678,8 +2725,8 @@ ov511_mode_init_regs(struct usb_ov511 *ov,
reg_w(ov, R511_CAM_OPTS, 0x03); reg_w(ov, R511_CAM_OPTS, 0x03);
/* Snapshot additions */ /* Snapshot additions */
reg_w(ov, R511_SNAP_PXCNT, pxcnt); reg_w(ov, R511_SNAP_PXCNT, hsegs);
reg_w(ov, R511_SNAP_LNCNT, lncnt); reg_w(ov, R511_SNAP_LNCNT, vsegs);
reg_w(ov, R511_SNAP_PXDIV, 0x00); reg_w(ov, R511_SNAP_PXDIV, 0x00);
reg_w(ov, R511_SNAP_LNDIV, 0x00); reg_w(ov, R511_SNAP_LNDIV, 0x00);
...@@ -2689,27 +2736,17 @@ ov511_mode_init_regs(struct usb_ov511 *ov, ...@@ -2689,27 +2736,17 @@ ov511_mode_init_regs(struct usb_ov511 *ov,
reg_w(ov, R511_COMP_LUT_EN, 0x03); reg_w(ov, R511_COMP_LUT_EN, 0x03);
ov51x_reset(ov, OV511_RESET_OMNICE); ov51x_reset(ov, OV511_RESET_OMNICE);
} }
//out:
if (ov51x_restart(ov) < 0) if (ov51x_restart(ov) < 0)
return -EIO; return -EIO;
return rc; return 0;
} }
static struct mode_list_518 mlist518[] = {
/* W H reg28 reg29 reg2a reg2c reg2e reg24 reg25 */
{ 352, 288, 0x00, 0x16, 0x48, 0x00, 0x00, 0x9f, 0x90 },
{ 320, 240, 0x00, 0x14, 0x3c, 0x10, 0x18, 0x9f, 0x90 },
{ 176, 144, 0x05, 0x0b, 0x24, 0x00, 0x00, 0xff, 0xf0 },
{ 160, 120, 0x05, 0x0a, 0x1e, 0x08, 0x0c, 0xff, 0xf0 },
{ 0, 0 }
};
/* Sets up the OV518/OV518+ with the given image parameters /* Sets up the OV518/OV518+ with the given image parameters
* *
* OV518 needs a completely different approach, until we can figure out what * OV518 needs a completely different approach, until we can figure out what
* the individual registers do. Many register ops are commented out until we * the individual registers do. Also, only 15 FPS is supported now.
* can find out if they are still valid. Also, only 15 FPS is supported now.
* *
* Do not put any sensor-specific code in here (including I2C I/O functions) * Do not put any sensor-specific code in here (including I2C I/O functions)
*/ */
...@@ -2717,56 +2754,60 @@ static int ...@@ -2717,56 +2754,60 @@ static int
ov518_mode_init_regs(struct usb_ov511 *ov, ov518_mode_init_regs(struct usb_ov511 *ov,
int width, int height, int mode, int sub_flag) int width, int height, int mode, int sub_flag)
{ {
int i; int hsegs, vsegs, hi_res;
if (sub_flag) {
width = ov->subw;
height = ov->subh;
}
PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
width, height, mode, sub_flag); width, height, mode, sub_flag);
if (width % 16 || height % 8) {
err("Invalid size (%d, %d)", width, height);
return -EINVAL;
}
if (width < ov->minwidth || height < ov->minheight) {
err("Requested dimensions are too small");
return -EINVAL;
}
if (width >= 320 && height >= 240) {
hi_res = 1;
} else if (width >= 320 || height >= 240) {
err("Invalid width/height combination (%d, %d)", width, height);
return -EINVAL;
} else {
hi_res = 0;
}
if (ov51x_stop(ov) < 0) if (ov51x_stop(ov) < 0)
return -EIO; return -EIO;
for (i = 0; mlist518[i].width; i++) { /******** Set the mode ********/
// int lncnt, pxcnt;
if (width != mlist518[i].width || height != mlist518[i].height) reg_w(ov, 0x2b, 0);
continue; reg_w(ov, 0x2c, 0);
reg_w(ov, 0x2d, 0);
reg_w(ov, 0x2e, 0);
reg_w(ov, 0x3b, 0);
reg_w(ov, 0x3c, 0);
reg_w(ov, 0x3d, 0);
reg_w(ov, 0x3e, 0);
// FIXME: Subcapture won't be possible until we know what the registers do reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
// FIXME: We can't handle anything but YUV420 so far reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
// /* Here I'm assuming that snapshot size == image size. hsegs = width / 16;
// * I hope that's always true. --claudio vsegs = height / 4;
// */
// pxcnt = sub_flag ? (ov->subw >> 3) - 1 : mlist[i].pxcnt;
// lncnt = sub_flag ? (ov->subh >> 3) - 1 : mlist[i].lncnt;
//
// reg_w(ov, 0x12, pxcnt);
// reg_w(ov, 0x13, lncnt);
/******** Set the mode ********/ reg_w(ov, 0x29, hsegs);
reg_w(ov, 0x2a, vsegs);
/* Mode independent regs */ reg_w(ov, 0x39, hsegs);
reg_w(ov, 0x2b, 0x00); reg_w(ov, 0x3a, vsegs);
reg_w(ov, 0x2d, 0x00);
reg_w(ov, 0x3b, 0x00);
reg_w(ov, 0x3d, 0x00);
/* Mode dependent regs. Regs 38 - 3e are always the same as
* regs 28 - 2e */
reg_w_mask(ov, 0x28, mlist518[i].reg28
| (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
reg_w(ov, 0x29, mlist518[i].reg29);
reg_w(ov, 0x2a, mlist518[i].reg2a);
reg_w(ov, 0x2c, mlist518[i].reg2c);
reg_w(ov, 0x2e, mlist518[i].reg2e);
reg_w_mask(ov, 0x38, mlist518[i].reg28
| (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
reg_w(ov, 0x39, mlist518[i].reg29);
reg_w(ov, 0x3a, mlist518[i].reg2a);
reg_w(ov, 0x3c, mlist518[i].reg2c);
reg_w(ov, 0x3e, mlist518[i].reg2e);
reg_w(ov, 0x24, mlist518[i].reg24);
reg_w(ov, 0x25, mlist518[i].reg25);
/* Windows driver does this here; who knows why */ /* Windows driver does this here; who knows why */
reg_w(ov, 0x2f, 0x80); reg_w(ov, 0x2f, 0x80);
...@@ -2774,10 +2815,13 @@ ov518_mode_init_regs(struct usb_ov511 *ov, ...@@ -2774,10 +2815,13 @@ ov518_mode_init_regs(struct usb_ov511 *ov,
/******** Set the framerate (to 15 FPS) ********/ /******** Set the framerate (to 15 FPS) ********/
/* Mode independent, but framerate dependent, regs */ /* Mode independent, but framerate dependent, regs */
/* These are for 15 FPS only */ reg_w(ov, 0x51, 0x02); /* Clock divider; lower==faster */
reg_w(ov, 0x51, 0x08);
reg_w(ov, 0x22, 0x18); reg_w(ov, 0x22, 0x18);
reg_w(ov, 0x23, 0xff); reg_w(ov, 0x23, 0xff);
if (ov->bridge == BRG_OV518PLUS)
reg_w(ov, 0x21, 0x19);
else
reg_w(ov, 0x71, 0x19); /* Compression-related? */ reg_w(ov, 0x71, 0x19); /* Compression-related? */
// FIXME: Sensor-specific // FIXME: Sensor-specific
...@@ -2786,11 +2830,21 @@ ov518_mode_init_regs(struct usb_ov511 *ov, ...@@ -2786,11 +2830,21 @@ ov518_mode_init_regs(struct usb_ov511 *ov,
reg_w(ov, 0x2f, 0x80); reg_w(ov, 0x2f, 0x80);
/* Mode dependent regs */ if (ov->bridge == BRG_OV518PLUS) {
if ((width == 352 && height == 288) || reg_w(ov, 0x24, 0x94);
(width == 320 && height == 240)) { reg_w(ov, 0x25, 0x90);
/* 640 (280h) byte iso packets */ ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */
ov518_reg_w32(ov, 0x30, 640, 2); /* 280h */ ov518_reg_w32(ov, 0xc6, 540, 2); /* 21ch */
ov518_reg_w32(ov, 0xc7, 540, 2); /* 21ch */
ov518_reg_w32(ov, 0xc8, 108, 2); /* 6ch */
ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */
ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */
ov518_reg_w32(ov, 0xcc, 2400, 2); /* 960h */
ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */
ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */
} else {
reg_w(ov, 0x24, 0x9f);
reg_w(ov, 0x25, 0x90);
ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */ ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */
ov518_reg_w32(ov, 0xc6, 500, 2); /* 1f4h */ ov518_reg_w32(ov, 0xc6, 500, 2); /* 1f4h */
ov518_reg_w32(ov, 0xc7, 500, 2); /* 1f4h */ ov518_reg_w32(ov, 0xc7, 500, 2); /* 1f4h */
...@@ -2800,29 +2854,10 @@ ov518_mode_init_regs(struct usb_ov511 *ov, ...@@ -2800,29 +2854,10 @@ ov518_mode_init_regs(struct usb_ov511 *ov,
ov518_reg_w32(ov, 0xcc, 2000, 2); /* 7d0h */ ov518_reg_w32(ov, 0xcc, 2000, 2); /* 7d0h */
ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */ ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */
ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */ ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */
} else if ((width == 176 && height == 144) ||
(width == 160 && height == 120)) {
/* 384 (180h) byte iso packets */
ov518_reg_w32(ov, 0x30, 384, 2); /* 180h */
ov518_reg_w32(ov, 0xc4, 200, 2); /* c8h */
ov518_reg_w32(ov, 0xc6, 320, 2); /* 140h */
ov518_reg_w32(ov, 0xc7, 320, 2); /* 140h */
ov518_reg_w32(ov, 0xc8, 96, 2); /* 60h */
ov518_reg_w32(ov, 0xca, 78607, 3); /* 1330fh */
ov518_reg_w32(ov, 0xcb, 320, 2); /* 140h */
ov518_reg_w32(ov, 0xcc, 1260, 2); /* 4ech */
ov518_reg_w32(ov, 0xcd, 19, 2); /* 13h */
ov518_reg_w32(ov, 0xce, 365, 2); /* 16dh */
} else {
/* Can't happen, since we already handled this case */
err("ov518_mode_init_regs(): **** logic error ****");
} }
reg_w(ov, 0x2f, 0x80); reg_w(ov, 0x2f, 0x80);
break;
}
if (ov51x_restart(ov) < 0) if (ov51x_restart(ov) < 0)
return -EIO; return -EIO;
...@@ -2830,11 +2865,6 @@ ov518_mode_init_regs(struct usb_ov511 *ov, ...@@ -2830,11 +2865,6 @@ ov518_mode_init_regs(struct usb_ov511 *ov,
if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0) if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
return -EIO; return -EIO;
if (mlist518[i].width == 0) {
err("Unknown mode (%d, %d): %d", width, height, mode);
return -EINVAL;
}
return 0; return 0;
} }
...@@ -2860,7 +2890,7 @@ mode_init_regs(struct usb_ov511 *ov, ...@@ -2860,7 +2890,7 @@ mode_init_regs(struct usb_ov511 *ov,
switch (ov->sensor) { switch (ov->sensor) {
case SEN_OV7610: case SEN_OV7610:
case SEN_OV7620: case SEN_OV7620:
case SEN_OV7620AE: case SEN_OV76BE:
case SEN_OV8600: case SEN_OV8600:
case SEN_OV6620: case SEN_OV6620:
case SEN_OV6630: case SEN_OV6630:
...@@ -2908,19 +2938,21 @@ mode_init_regs(struct usb_ov511 *ov, ...@@ -2908,19 +2938,21 @@ mode_init_regs(struct usb_ov511 *ov,
if (FATAL_ERROR(rc)) if (FATAL_ERROR(rc))
return rc; return rc;
rc = sensor_set_mirror(ov, ov->mirror);
if (FATAL_ERROR(rc))
return rc;
return 0; return 0;
} }
/* This sets the default image parameters (Size = max, RGB24). This is /* This sets the default image parameters. This is useful for apps that use
* useful for apps that use read() and do not set these. * read() and do not set these.
*/ */
static int static int
ov51x_set_default_params(struct usb_ov511 *ov) ov51x_set_default_params(struct usb_ov511 *ov)
{ {
int i; int i;
PDEBUG(3, "%dx%d, RGB24", ov->maxwidth, ov->maxheight);
/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
* (using read() instead). */ * (using read() instead). */
for (i = 0; i < OV511_NUMFRAMES; i++) { for (i = 0; i < OV511_NUMFRAMES; i++) {
...@@ -2930,11 +2962,14 @@ ov51x_set_default_params(struct usb_ov511 *ov) ...@@ -2930,11 +2962,14 @@ ov51x_set_default_params(struct usb_ov511 *ov)
if (force_palette) if (force_palette)
ov->frame[i].format = force_palette; ov->frame[i].format = force_palette;
else else
ov->frame[i].format = VIDEO_PALETTE_RGB24; ov->frame[i].format = VIDEO_PALETTE_YUV420;
ov->frame[i].depth = get_depth(ov->frame[i].format); ov->frame[i].depth = get_depth(ov->frame[i].format);
} }
/* Initialize to max width/height, RGB24 */ PDEBUG(3, "%dx%d, %s", ov->maxwidth, ov->maxheight,
symbolic(v4l1_plist, ov->frame[0].format));
/* Initialize to max width/height, YUV420 or RGB24 (if supported) */
if (mode_init_regs(ov, ov->maxwidth, ov->maxheight, if (mode_init_regs(ov, ov->maxwidth, ov->maxheight,
ov->frame[0].format, 0) < 0) ov->frame[0].format, 0) < 0)
return -EINVAL; return -EINVAL;
...@@ -3031,107 +3066,6 @@ decoder_set_norm(struct usb_ov511 *ov, int norm) ...@@ -3031,107 +3066,6 @@ decoder_set_norm(struct usb_ov511 *ov, int norm)
return 0; return 0;
} }
/**********************************************************************
*
* Color correction functions
*
**********************************************************************/
/*
* Turn a YUV4:2:0 block into an RGB block
*
* Video4Linux seems to use the blue, green, red channel
* order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
*
* Color space conversion coefficients taken from the excellent
* http://www.inforamp.net/~poynton/ColorFAQ.html
* In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
* Y values are given for all 4 pixels, but the U (Pb)
* and V (Pr) are assumed constant over the 2x2 block.
*
* To avoid floating point arithmetic, the color conversion
* coefficients are scaled into 16.16 fixed-point integers.
* They were determined as follows:
*
* double brightness = 1.0; (0->black; 1->full scale)
* double saturation = 1.0; (0->greyscale; 1->full color)
* double fixScale = brightness * 256 * 256;
* int rvScale = (int)(1.402 * saturation * fixScale);
* int guScale = (int)(-0.344136 * saturation * fixScale);
* int gvScale = (int)(-0.714136 * saturation * fixScale);
* int buScale = (int)(1.772 * saturation * fixScale);
* int yScale = (int)(fixScale);
*/
/* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
static inline void
move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
int rowPixels, unsigned char * rgb, int bits)
{
const int rvScale = 91881;
const int guScale = -22553;
const int gvScale = -46801;
const int buScale = 116129;
const int yScale = 65536;
int r, g, b;
g = guScale * u + gvScale * v;
if (force_rgb) {
r = buScale * u;
b = rvScale * v;
} else {
r = rvScale * v;
b = buScale * u;
}
yTL *= yScale; yTR *= yScale;
yBL *= yScale; yBR *= yScale;
if (bits == 24) {
/* Write out top two pixels */
rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
rgb[2] = LIMIT(r+yTL);
rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
rgb[5] = LIMIT(r+yTR);
/* Skip down to next line to write out bottom two pixels */
rgb += 3 * rowPixels;
rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
rgb[2] = LIMIT(r+yBL);
rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
rgb[5] = LIMIT(r+yBR);
} else if (bits == 16) {
/* Write out top two pixels */
rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F)
| ((LIMIT(g+yTL) << 3) & 0xE0);
rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07)
| (LIMIT(r+yTL) & 0xF8);
rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F)
| ((LIMIT(g+yTR) << 3) & 0xE0);
rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07)
| (LIMIT(r+yTR) & 0xF8);
/* Skip down to next line to write out bottom two pixels */
rgb += 2 * rowPixels;
rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F)
| ((LIMIT(g+yBL) << 3) & 0xE0);
rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07)
| (LIMIT(r+yBL) & 0xF8);
rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F)
| ((LIMIT(g+yBR) << 3) & 0xE0);
rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07)
| (LIMIT(r+yBR) & 0xF8);
}
}
/********************************************************************** /**********************************************************************
* *
* Raw data parsing * Raw data parsing
...@@ -3266,45 +3200,6 @@ yuv420raw_to_yuv420p(struct ov511_frame *frame, ...@@ -3266,45 +3200,6 @@ yuv420raw_to_yuv420p(struct ov511_frame *frame,
} }
} }
/*
* fixFrameRGBoffset--
* My camera seems to return the red channel about 1 pixel
* low, and the blue channel about 1 pixel high. After YUV->RGB
* conversion, we can correct this easily. OSL 2/24/2000.
*/
static void
fixFrameRGBoffset(struct ov511_frame *frame)
{
int x, y;
int rowBytes = frame->width*3, w = frame->width;
unsigned char *rgb = frame->data;
const int shift = 1; /* Distance to shift pixels by, vertically */
/* Don't bother with little images */
if (frame->width < 400)
return;
/* This only works with RGB24 */
if (frame->format != VIDEO_PALETTE_RGB24)
return;
/* Shift red channel up */
for (y = shift; y < frame->height; y++) {
int lp = (y-shift)*rowBytes; /* Previous line offset */
int lc = y*rowBytes; /* Current line offset */
for (x = 0; x < w; x++)
rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
}
/* Shift blue channel down */
for (y = frame->height-shift-1; y >= 0; y--) {
int ln = (y + shift) * rowBytes; /* Next line offset */
int lc = y * rowBytes; /* Current line offset */
for (x = 0; x < w; x++)
rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
}
}
/********************************************************************** /**********************************************************************
* *
* Decompression * Decompression
...@@ -3354,17 +3249,17 @@ request_decompressor(struct usb_ov511 *ov) ...@@ -3354,17 +3249,17 @@ request_decompressor(struct usb_ov511 *ov)
} }
if (ov->decomp_ops) { if (ov->decomp_ops) {
if (!ov->decomp_ops->decomp_lock) { if (!ov->decomp_ops->owner) {
ov->decomp_ops = NULL; ov->decomp_ops = NULL;
unlock_kernel(); unlock_kernel();
return -ENOSYS; return -ENOSYS;
} }
ov->decomp_ops->decomp_lock(); __MOD_INC_USE_COUNT(ov->decomp_ops->owner);
unlock_kernel(); unlock_kernel();
return 0; return 0;
} else { } else {
unlock_kernel(); unlock_kernel();
return -ENXIO; return -ENOSYS;
} }
} }
...@@ -3381,8 +3276,8 @@ release_decompressor(struct usb_ov511 *ov) ...@@ -3381,8 +3276,8 @@ release_decompressor(struct usb_ov511 *ov)
lock_kernel(); lock_kernel();
if (ov->decomp_ops && ov->decomp_ops->decomp_unlock) { if (ov->decomp_ops && ov->decomp_ops->owner) {
ov->decomp_ops->decomp_unlock(); __MOD_DEC_USE_COUNT(ov->decomp_ops->owner);
released = 1; released = 1;
} }
...@@ -3414,7 +3309,8 @@ decompress(struct usb_ov511 *ov, struct ov511_frame *frame, ...@@ -3414,7 +3309,8 @@ decompress(struct usb_ov511 *ov, struct ov511_frame *frame,
frame->rawheight, frame->rawheight,
frame->bytes_recvd); frame->bytes_recvd);
PDEBUG(4, "DEBUG: decomp_400 returned %d", ret); PDEBUG(4, "DEBUG: decomp_400 returned %d", ret);
} else if (ov->decomp_ops->decomp_420) { } else if (frame->format != VIDEO_PALETTE_GREY
&& ov->decomp_ops->decomp_420) {
int ret = ov->decomp_ops->decomp_420( int ret = ov->decomp_ops->decomp_420(
pIn0, pIn0,
pOut0, pOut0,
...@@ -3428,112 +3324,6 @@ decompress(struct usb_ov511 *ov, struct ov511_frame *frame, ...@@ -3428,112 +3324,6 @@ decompress(struct usb_ov511 *ov, struct ov511_frame *frame,
} }
} }
/**********************************************************************
*
* Format conversion
*
**********************************************************************/
/* Converts from planar YUV420 to RGB24. */
static void
yuv420p_to_rgb(struct ov511_frame *frame,
unsigned char *pIn0, unsigned char *pOut0, int bits)
{
const int numpix = frame->width * frame->height;
const int bytes = bits >> 3;
int i, j, y00, y01, y10, y11, u, v;
unsigned char *pY = pIn0;
unsigned char *pU = pY + numpix;
unsigned char *pV = pU + numpix / 4;
unsigned char *pOut = pOut0;
for (j = 0; j <= frame->height - 2; j += 2) {
for (i = 0; i <= frame->width - 2; i += 2) {
y00 = *pY;
y01 = *(pY + 1);
y10 = *(pY + frame->width);
y11 = *(pY + frame->width + 1);
u = (*pU++) - 128;
v = (*pV++) - 128;
move_420_block(y00, y01, y10, y11, u, v,
frame->width, pOut, bits);
pY += 2;
pOut += 2 * bytes;
}
pY += frame->width;
pOut += frame->width * bytes;
}
}
/* Converts from planar YUV420 to YUV422 (YUYV). */
static void
yuv420p_to_yuv422(struct ov511_frame *frame,
unsigned char *pIn0, unsigned char *pOut0)
{
const int numpix = frame->width * frame->height;
int i, j;
unsigned char *pY = pIn0;
unsigned char *pU = pY + numpix;
unsigned char *pV = pU + numpix / 4;
unsigned char *pOut = pOut0;
for (i = 0; i < numpix; i++) {
*pOut = *(pY + i);
pOut += 2;
}
pOut = pOut0 + 1;
for (j = 0; j <= frame->height - 2 ; j += 2) {
for (i = 0; i <= frame->width - 2; i += 2) {
int u = *pU++;
int v = *pV++;
*pOut = u;
*(pOut+2) = v;
*(pOut+frame->width*2) = u;
*(pOut+frame->width*2+2) = v;
pOut += 4;
}
pOut += (frame->width * 2);
}
}
/* Converts pData from planar YUV420 to planar YUV422 **in place**. */
static void
yuv420p_to_yuv422p(struct ov511_frame *frame, unsigned char *pData)
{
const int numpix = frame->width * frame->height;
const int w = frame->width;
int j;
unsigned char *pIn, *pOut;
/* Clear U and V */
memset(pData + numpix + numpix / 2, 127, numpix / 2);
/* Convert V starting from beginning and working forward */
pIn = pData + numpix + numpix / 4;
pOut = pData + numpix +numpix / 2;
for (j = 0; j <= frame->height - 2; j += 2) {
memmove(pOut, pIn, w/2);
memmove(pOut + w/2, pIn, w/2);
pIn += w/2;
pOut += w;
}
/* Convert U, starting from end and working backward */
pIn = pData + numpix + numpix / 4;
pOut = pData + numpix + numpix / 2;
for (j = 0; j <= frame->height - 2; j += 2) {
pIn -= w/2;
pOut -= w;
memmove(pOut, pIn, w/2);
memmove(pOut + w/2, pIn, w/2);
}
}
/* Fuses even and odd fields together, and doubles width. /* Fuses even and odd fields together, and doubles width.
* INPUT: an odd field followed by an even field at pIn0, in YUV planar format * INPUT: an odd field followed by an even field at pIn0, in YUV planar format
* OUTPUT: a normal YUV planar image, with correct aspect ratio * OUTPUT: a normal YUV planar image, with correct aspect ratio
...@@ -3605,27 +3395,11 @@ deinterlace(struct ov511_frame *frame, int rawformat, ...@@ -3605,27 +3395,11 @@ deinterlace(struct ov511_frame *frame, int rawformat,
} }
} }
/* Post-processes the specified frame. This consists of:
* 1. Decompress frame, if necessary
* 2. Deinterlace frame and scale to proper size, if necessary
* 3. Convert from YUV planar to destination format, if necessary
* 4. Fix the RGB offset, if necessary
*/
static void static void
ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame) ov51x_postprocess_grey(struct usb_ov511 *ov, struct ov511_frame *frame)
{ {
if (dumppix) {
memset(frame->data, 0,
MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
memcpy(frame->data, frame->rawdata, frame->bytes_recvd);
return;
}
/* YUV400 must be handled separately */
if (frame->format == VIDEO_PALETTE_GREY) {
/* Deinterlace frame, if necessary */ /* Deinterlace frame, if necessary */
if (ov->sensor == SEN_SAA7111A && frame->rawheight == 480) { if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) {
if (frame->compressed) if (frame->compressed)
decompress(ov, frame, frame->rawdata, decompress(ov, frame, frame->rawdata,
frame->tempdata); frame->tempdata);
...@@ -3643,63 +3417,59 @@ ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame) ...@@ -3643,63 +3417,59 @@ ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame)
yuv400raw_to_yuv400p(frame, frame->rawdata, yuv400raw_to_yuv400p(frame, frame->rawdata,
frame->data); frame->data);
} }
}
return; /* Process raw YUV420 data into standard YUV420P */
} static void
ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame)
/* Process frame->data to frame->rawdata */ {
/* Deinterlace frame, if necessary */
if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) {
if (frame->compressed) if (frame->compressed)
decompress(ov, frame, frame->rawdata, frame->tempdata); decompress(ov, frame, frame->rawdata, frame->tempdata);
else else
yuv420raw_to_yuv420p(frame, frame->rawdata, frame->tempdata); yuv420raw_to_yuv420p(frame, frame->rawdata,
/* Deinterlace frame, if necessary */
if (ov->sensor == SEN_SAA7111A && frame->rawheight == 480) {
memcpy(frame->rawdata, frame->tempdata,
MAX_RAW_DATA_SIZE(frame->width, frame->height));
deinterlace(frame, RAWFMT_YUV420, frame->rawdata,
frame->tempdata); frame->tempdata);
}
/* Frame should be (width x height) and not (rawwidth x rawheight) at
* this point. */
#if 0 deinterlace(frame, RAWFMT_YUV420, frame->tempdata,
/* Clear output buffer for testing purposes */ frame->data);
memset(frame->data, 0, MAX_DATA_SIZE(frame->width, frame->height)); } else {
#endif if (frame->compressed)
decompress(ov, frame, frame->rawdata, frame->data);
else
yuv420raw_to_yuv420p(frame, frame->rawdata,
frame->data);
}
}
/* Process frame->tempdata to frame->data */ /* Post-processes the specified frame. This consists of:
* 1. Decompress frame, if necessary
* 2. Deinterlace frame and scale to proper size, if necessary
* 3. Convert from YUV planar to destination format, if necessary
* 4. Fix the RGB offset, if necessary
*/
static void
ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame)
{
if (dumppix) {
memset(frame->data, 0,
MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
memcpy(frame->data, frame->rawdata, frame->bytes_recvd);
} else {
switch (frame->format) { switch (frame->format) {
case VIDEO_PALETTE_RGB565: case VIDEO_PALETTE_GREY:
yuv420p_to_rgb(frame, frame->tempdata, frame->data, 16); ov51x_postprocess_grey(ov, frame);
break;
case VIDEO_PALETTE_RGB24:
yuv420p_to_rgb(frame, frame->tempdata, frame->data, 24);
break;
case VIDEO_PALETTE_YUV422:
case VIDEO_PALETTE_YUYV:
yuv420p_to_yuv422(frame, frame->tempdata, frame->data);
break; break;
case VIDEO_PALETTE_YUV420: case VIDEO_PALETTE_YUV420:
case VIDEO_PALETTE_YUV420P: case VIDEO_PALETTE_YUV420P:
memcpy(frame->data, frame->tempdata, ov51x_postprocess_yuv420(ov, frame);
MAX_RAW_DATA_SIZE(frame->width, frame->height));
break;
case VIDEO_PALETTE_YUV422P:
/* Data is converted in place, so copy it in advance */
memcpy(frame->data, frame->tempdata,
MAX_RAW_DATA_SIZE(frame->width, frame->height));
yuv420p_to_yuv422p(frame, frame->data);
break; break;
default: default:
err("Cannot convert data to %s", err("Cannot convert data to %s",
symbolic(v4l1_plist, frame->format)); symbolic(v4l1_plist, frame->format));
} }
}
if (fix_rgb_offset)
fixFrameRGBoffset(frame);
} }
/********************************************************************** /**********************************************************************
...@@ -3889,15 +3659,18 @@ ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n) ...@@ -3889,15 +3659,18 @@ ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n)
struct ov511_frame *frame = &ov->frame[ov->curframe]; struct ov511_frame *frame = &ov->frame[ov->curframe];
struct timeval *ts; struct timeval *ts;
if (printph) { /* Don't copy the packet number byte */
info("ph: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", if (ov->packet_numbering)
in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7], --n;
in[8], in[9], in[10], in[11]);
}
/* A false positive here is likely, until OVT gives me /* A false positive here is likely, until OVT gives me
* the definitive SOF/EOF format */ * the definitive SOF/EOF format */
if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) { if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) {
if (printph) {
info("ph: %2x %2x %2x %2x %2x %2x %2x %2x", in[0],
in[1], in[2], in[3], in[4], in[5], in[6], in[7]);
}
if (frame->scanstate == STATE_LINES) { if (frame->scanstate == STATE_LINES) {
PDEBUG(4, "Detected frame end/start"); PDEBUG(4, "Detected frame end/start");
goto eof; goto eof;
...@@ -3976,7 +3749,7 @@ ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n) ...@@ -3976,7 +3749,7 @@ ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n)
#endif #endif
frame->scanstate = STATE_LINES; frame->scanstate = STATE_LINES;
frame->bytes_recvd = 0; frame->bytes_recvd = 0;
// frame->compressed = 1; frame->compressed = 1;
check_middle: check_middle:
/* Are we in a frame? */ /* Are we in a frame? */
...@@ -3999,8 +3772,8 @@ ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n) ...@@ -3999,8 +3772,8 @@ ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n)
* zero-segments allow the OV518 to mainain a constant data rate * zero-segments allow the OV518 to mainain a constant data rate
* regardless of the effectiveness of the compression. Segments * regardless of the effectiveness of the compression. Segments
* are aligned relative to the beginning of each isochronous * are aligned relative to the beginning of each isochronous
* packet. The first segment is a header (the decompressor * packet. The first segment in each image is a header (the
* skips it later). * decompressor skips it later).
*/ */
int b, read = 0, allzero, copied = 0; int b, read = 0, allzero, copied = 0;
...@@ -4066,7 +3839,6 @@ ov51x_isoc_irq(struct urb *urb) ...@@ -4066,7 +3839,6 @@ ov51x_isoc_irq(struct urb *urb)
if (urb->status != -EINPROGRESS && urb->status != 0) { if (urb->status != -EINPROGRESS && urb->status != 0) {
err("ERROR: urb->status=%d: %s", urb->status, err("ERROR: urb->status=%d: %s", urb->status,
symbolic(urb_errlist, urb->status)); symbolic(urb_errlist, urb->status));
return;
} }
/* Copy the data received into our frame buffer */ /* Copy the data received into our frame buffer */
...@@ -4159,15 +3931,21 @@ ov51x_init_isoc(struct usb_ov511 *ov) ...@@ -4159,15 +3931,21 @@ ov51x_init_isoc(struct usb_ov511 *ov)
return -1; return -1;
} }
if (packetsize == -1) {
// FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now // FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now
if (ov->bclass == BCL_OV518) if (ov->bclass == BCL_OV518) {
ov51x_set_packet_size(ov, 640); if (packetsize == -1) {
else ov518_set_packet_size(ov, 640);
ov51x_set_packet_size(ov, size);
} else { } else {
info("Forcing packet size to %d", packetsize); info("Forcing packet size to %d", packetsize);
ov51x_set_packet_size(ov, packetsize); ov518_set_packet_size(ov, packetsize);
}
} else {
if (packetsize == -1) {
ov511_set_packet_size(ov, size);
} else {
info("Forcing packet size to %d", packetsize);
ov511_set_packet_size(ov, packetsize);
}
} }
for (n = 0; n < OV511_NUMSBUF; n++) { for (n = 0; n < OV511_NUMSBUF; n++) {
...@@ -4200,8 +3978,10 @@ ov51x_init_isoc(struct usb_ov511 *ov) ...@@ -4200,8 +3978,10 @@ ov51x_init_isoc(struct usb_ov511 *ov)
for (n = 0; n < OV511_NUMSBUF; n++) { for (n = 0; n < OV511_NUMSBUF; n++) {
ov->sbuf[n].urb->dev = ov->dev; ov->sbuf[n].urb->dev = ov->dev;
err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL); err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL);
if (err) if (err) {
err("init isoc: usb_submit_urb(%d) ret %d", n, err); err("init isoc: usb_submit_urb(%d) ret %d", n, err);
return err;
}
} }
return 0; return 0;
...@@ -4230,7 +4010,10 @@ ov51x_stop_isoc(struct usb_ov511 *ov) ...@@ -4230,7 +4010,10 @@ ov51x_stop_isoc(struct usb_ov511 *ov)
PDEBUG(3, "*** Stopping capture ***"); PDEBUG(3, "*** Stopping capture ***");
ov51x_set_packet_size(ov, 0); if (ov->bclass == BCL_OV518)
ov518_set_packet_size(ov, 0);
else
ov511_set_packet_size(ov, 0);
ov->streaming = 0; ov->streaming = 0;
...@@ -4409,44 +4192,12 @@ ov51x_alloc(struct usb_ov511 *ov) ...@@ -4409,44 +4192,12 @@ ov51x_alloc(struct usb_ov511 *ov)
return -ENOMEM; return -ENOMEM;
} }
static void
ov51x_buf_callback(unsigned long data)
{
struct usb_ov511 *ov = (struct usb_ov511 *)data;
PDEBUG(4, "entered");
down(&ov->buf_lock);
if (ov->buf_state == BUF_PEND_DEALLOC)
ov51x_do_dealloc(ov);
up(&ov->buf_lock);
PDEBUG(4, "leaving");
}
static void static void
ov51x_dealloc(struct usb_ov511 *ov, int now) ov51x_dealloc(struct usb_ov511 *ov, int now)
{ {
struct timer_list *bt = &(ov->buf_timer);
PDEBUG(4, "entered"); PDEBUG(4, "entered");
down(&ov->buf_lock); down(&ov->buf_lock);
PDEBUG(4, "deallocating buffer memory %s", now ? "now" : "later");
if (ov->buf_state == BUF_PEND_DEALLOC) {
ov->buf_state = BUF_ALLOCATED;
del_timer(bt);
}
if (now)
ov51x_do_dealloc(ov); ov51x_do_dealloc(ov);
else {
ov->buf_state = BUF_PEND_DEALLOC;
init_timer(bt);
bt->function = ov51x_buf_callback;
bt->data = (unsigned long)ov;
bt->expires = jiffies + buf_timeout * HZ;
add_timer(bt);
}
up(&ov->buf_lock); up(&ov->buf_lock);
PDEBUG(4, "leaving"); PDEBUG(4, "leaving");
} }
...@@ -5303,12 +5054,11 @@ static int ...@@ -5303,12 +5054,11 @@ static int
ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long ularg) unsigned long ularg)
{ {
struct proc_dir_entry *pde; struct proc_dir_entry *pde = PDE(inode);
struct usb_ov511 *ov; struct usb_ov511 *ov;
void *arg = (void *) ularg; void *arg = (void *) ularg;
int rc; int rc;
pde = PDE(inode);
if (!pde) if (!pde)
return -ENOENT; return -ENOENT;
...@@ -5521,7 +5271,7 @@ ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -5521,7 +5271,7 @@ ov51x_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
* *
***************************************************************************/ ***************************************************************************/
/* This initializes the OV7610, OV7620, or OV7620AE sensor. The OV7620AE uses /* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
* the same register settings as the OV7610, since they are very similar. * the same register settings as the OV7610, since they are very similar.
*/ */
static int static int
...@@ -5692,20 +5442,23 @@ ov7xx0_configure(struct usb_ov511 *ov) ...@@ -5692,20 +5442,23 @@ ov7xx0_configure(struct usb_ov511 *ov)
info("Sensor is an OV7610"); info("Sensor is an OV7610");
ov->sensor = SEN_OV7610; ov->sensor = SEN_OV7610;
} else if ((rc & 3) == 1) { } else if ((rc & 3) == 1) {
/* I don't know what's different about the 76BE yet */ /* I don't know what's different about the 76BE yet. */
if (i2c_r(ov, 0x15) & 1) if (i2c_r(ov, 0x15) & 1) {
info("Sensor is an OV7620AE"); info("Sensor is an OV7620AE");
else info("PLEASE REPORT THE EXISTENCE OF THIS SENSOR TO");
info("THE DRIVER AUTHOR");
} else {
info("Sensor is an OV76BE"); info("Sensor is an OV76BE");
}
/* OV511+ will return all zero isoc data unless we /* OV511+ will return all zero isoc data unless we
* configure the sensor as a 7620. Someone needs to * configure the sensor as a 7620. Someone needs to
* find the exact reg. setting that causes this. */ * find the exact reg. setting that causes this. */
if (ov->bridge == BRG_OV511PLUS) { if (ov->bridge == BRG_OV511PLUS) {
info("Enabling 511+/7620AE workaround"); info("Enabling 511+/76BE workaround");
ov->sensor = SEN_OV7620; ov->sensor = SEN_OV7620;
} else { } else {
ov->sensor = SEN_OV7620AE; ov->sensor = SEN_OV76BE;
} }
} else if ((rc & 3) == 0) { } else if ((rc & 3) == 0) {
info("Sensor is an OV7620"); info("Sensor is an OV7620");
...@@ -5755,6 +5508,8 @@ ov6xx0_configure(struct usb_ov511 *ov) ...@@ -5755,6 +5508,8 @@ ov6xx0_configure(struct usb_ov511 *ov)
/* The ratio of 0x0c and 0x0d controls the white point */ /* The ratio of 0x0c and 0x0d controls the white point */
{ OV511_I2C_BUS, 0x0c, 0x24 }, { OV511_I2C_BUS, 0x0c, 0x24 },
{ OV511_I2C_BUS, 0x0d, 0x24 }, { OV511_I2C_BUS, 0x0d, 0x24 },
{ OV511_I2C_BUS, 0x0f, 0x15 }, /* COMS */
{ OV511_I2C_BUS, 0x10, 0x75 }, /* AEC Exposure time */
{ OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */ { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
{ OV511_I2C_BUS, 0x14, 0x04 }, { OV511_I2C_BUS, 0x14, 0x04 },
/* 0x16: 0x06 helps frame stability with moving objects */ /* 0x16: 0x06 helps frame stability with moving objects */
...@@ -5766,6 +5521,7 @@ ov6xx0_configure(struct usb_ov511 *ov) ...@@ -5766,6 +5521,7 @@ ov6xx0_configure(struct usb_ov511 *ov)
{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
// { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */ // { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
{ OV511_I2C_BUS, 0x2d, 0x99 }, { OV511_I2C_BUS, 0x2d, 0x99 },
{ OV511_I2C_BUS, 0x33, 0xa0 }, /* Color Procesing Parameter */
{ OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */ { OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */
{ OV511_I2C_BUS, 0x38, 0x8b }, { OV511_I2C_BUS, 0x38, 0x8b },
{ OV511_I2C_BUS, 0x39, 0x40 }, { OV511_I2C_BUS, 0x39, 0x40 },
...@@ -6023,9 +5779,15 @@ saa7111a_configure(struct usb_ov511 *ov) ...@@ -6023,9 +5779,15 @@ saa7111a_configure(struct usb_ov511 *ov)
} }
#endif #endif
/* Set sensor-specific vars */ /* 640x480 not supported with PAL */
if (ov->pal) {
ov->maxwidth = 320;
ov->maxheight = 240; /* Even field only */
} else {
ov->maxwidth = 640; ov->maxwidth = 640;
ov->maxheight = 480; /* Even/Odd fields */ ov->maxheight = 480; /* Even/Odd fields */
}
ov->minwidth = 320; ov->minwidth = 320;
ov->minheight = 240; /* Even field only */ ov->minheight = 240; /* Even field only */
...@@ -6126,6 +5888,9 @@ ov511_configure(struct usb_ov511 *ov) ...@@ -6126,6 +5888,9 @@ ov511_configure(struct usb_ov511 *ov)
if (ov->customid == 6) { /* USB Life TV (NTSC) */ if (ov->customid == 6) { /* USB Life TV (NTSC) */
ov->tuner_type = 8; /* Temic 4036FY5 3X 1981 */ ov->tuner_type = 8; /* Temic 4036FY5 3X 1981 */
} else if (ov->customid == 70) { /* USB Life TV (PAL/SECAM) */
ov->tuner_type = 3; /* Philips FI1216MF */
ov->pal = 1;
} }
if (write_regvals(ov, aRegvalsInit511)) goto error; if (write_regvals(ov, aRegvalsInit511)) goto error;
...@@ -6145,7 +5910,8 @@ ov511_configure(struct usb_ov511 *ov) ...@@ -6145,7 +5910,8 @@ ov511_configure(struct usb_ov511 *ov)
if (ov511_init_compression(ov)) goto error; if (ov511_init_compression(ov)) goto error;
ov51x_set_packet_size(ov, 0); ov->packet_numbering = 1;
ov511_set_packet_size(ov, 0);
ov->snap_enabled = snapshot; ov->snap_enabled = snapshot;
...@@ -6166,21 +5932,21 @@ ov511_configure(struct usb_ov511 *ov) ...@@ -6166,21 +5932,21 @@ ov511_configure(struct usb_ov511 *ov)
/* Test for 8xx0 */ /* Test for 8xx0 */
PDEBUG(3, "Testing for 0V8xx0"); PDEBUG(3, "Testing for 0V8xx0");
ov->primary_i2c_slave = OV8xx0_SID; ov->primary_i2c_slave = OV8xx0_SID;
if (ov51x_set_slave_ids(ov, OV8xx0_SID)) if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0)
goto error; goto error;
if (i2c_w(ov, 0x12, 0x80) < 0) { if (i2c_w(ov, 0x12, 0x80) < 0) {
/* Test for SAA7111A */ /* Test for SAA7111A */
PDEBUG(3, "Testing for SAA7111A"); PDEBUG(3, "Testing for SAA7111A");
ov->primary_i2c_slave = SAA7111A_SID; ov->primary_i2c_slave = SAA7111A_SID;
if (ov51x_set_slave_ids(ov, SAA7111A_SID)) if (ov51x_set_slave_ids(ov, SAA7111A_SID) < 0)
goto error; goto error;
if (i2c_w(ov, 0x0d, 0x00) < 0) { if (i2c_w(ov, 0x0d, 0x00) < 0) {
/* Test for KS0127 */ /* Test for KS0127 */
PDEBUG(3, "Testing for KS0127"); PDEBUG(3, "Testing for KS0127");
ov->primary_i2c_slave = KS0127_SID; ov->primary_i2c_slave = KS0127_SID;
if (ov51x_set_slave_ids(ov, KS0127_SID)) if (ov51x_set_slave_ids(ov, KS0127_SID) < 0)
goto error; goto error;
if (i2c_w(ov, 0x10, 0x00) < 0) { if (i2c_w(ov, 0x10, 0x00) < 0) {
...@@ -6227,6 +5993,7 @@ ov511_configure(struct usb_ov511 *ov) ...@@ -6227,6 +5993,7 @@ ov511_configure(struct usb_ov511 *ov)
static int static int
ov518_configure(struct usb_ov511 *ov) ov518_configure(struct usb_ov511 *ov)
{ {
/* For 518 and 518+ */
static struct ov511_regvals aRegvalsInit518[] = { static struct ov511_regvals aRegvalsInit518[] = {
{ OV511_REG_BUS, R51x_SYS_RESET, 0x40 }, { OV511_REG_BUS, R51x_SYS_RESET, 0x40 },
{ OV511_REG_BUS, R51x_SYS_INIT, 0xe1 }, { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 },
...@@ -6239,8 +6006,6 @@ ov518_configure(struct usb_ov511 *ov) ...@@ -6239,8 +6006,6 @@ ov518_configure(struct usb_ov511 *ov)
{ OV511_DONE_BUS, 0x0, 0x00}, { OV511_DONE_BUS, 0x0, 0x00},
}; };
/* New values, based on Windows driver. Since what they do is not
* known yet, this may be incorrect. */
static struct ov511_regvals aRegvalsNorm518[] = { static struct ov511_regvals aRegvalsNorm518[] = {
{ OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */
{ OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */ { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */
...@@ -6254,11 +6019,33 @@ ov518_configure(struct usb_ov511 *ov) ...@@ -6254,11 +6019,33 @@ ov518_configure(struct usb_ov511 *ov)
{ OV511_DONE_BUS, 0x0, 0x00 }, { OV511_DONE_BUS, 0x0, 0x00 },
}; };
static struct ov511_regvals aRegvalsNorm518Plus[] = {
{ OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */
{ OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */
{ OV511_REG_BUS, 0x31, 0x0f },
{ OV511_REG_BUS, 0x5d, 0x03 },
{ OV511_REG_BUS, 0x24, 0x9f },
{ OV511_REG_BUS, 0x25, 0x90 },
{ OV511_REG_BUS, 0x20, 0x60 }, /* Was 0x08 */
{ OV511_REG_BUS, 0x51, 0x02 },
{ OV511_REG_BUS, 0x71, 0x19 },
{ OV511_REG_BUS, 0x40, 0xff },
{ OV511_REG_BUS, 0x41, 0x42 },
{ OV511_REG_BUS, 0x46, 0x00 },
{ OV511_REG_BUS, 0x33, 0x04 },
{ OV511_REG_BUS, 0x21, 0x19 },
{ OV511_REG_BUS, 0x3f, 0x10 },
{ OV511_DONE_BUS, 0x0, 0x00 },
};
PDEBUG(4, ""); PDEBUG(4, "");
/* First 5 bits of custom ID reg are a revision ID on OV518 */ /* First 5 bits of custom ID reg are a revision ID on OV518 */
info("Device revision %d", 0x1F & reg_r(ov, R511_SYS_CUST_ID)); info("Device revision %d", 0x1F & reg_r(ov, R511_SYS_CUST_ID));
/* Give it the default description */
ov->desc = symbolic(camlist, 0);
if (write_regvals(ov, aRegvalsInit518)) goto error; if (write_regvals(ov, aRegvalsInit518)) goto error;
/* Set LED GPIO pin to output mode */ /* Set LED GPIO pin to output mode */
...@@ -6277,13 +6064,25 @@ ov518_configure(struct usb_ov511 *ov) ...@@ -6277,13 +6064,25 @@ ov518_configure(struct usb_ov511 *ov)
warn("Compression required with OV518...enabling"); warn("Compression required with OV518...enabling");
} }
if (ov->bridge == BRG_OV518) {
if (write_regvals(ov, aRegvalsNorm518)) goto error; if (write_regvals(ov, aRegvalsNorm518)) goto error;
} else if (ov->bridge == BRG_OV518PLUS) {
if (write_regvals(ov, aRegvalsNorm518Plus)) goto error;
} else {
err("Invalid bridge");
}
if (reg_w(ov, 0x2f, 0x80) < 0) goto error; if (reg_w(ov, 0x2f, 0x80) < 0) goto error;
if (ov518_init_compression(ov)) goto error; if (ov518_init_compression(ov)) goto error;
ov51x_set_packet_size(ov, 0); /* OV518+ has packet numbering turned on by default */
if (ov->bridge == BRG_OV518)
ov->packet_numbering = 0;
else
ov->packet_numbering = 1;
ov518_set_packet_size(ov, 0);
ov->snap_enabled = snapshot; ov->snap_enabled = snapshot;
...@@ -6328,9 +6127,8 @@ ov518_configure(struct usb_ov511 *ov) ...@@ -6328,9 +6127,8 @@ ov518_configure(struct usb_ov511 *ov)
} }
} }
// FIXME: Sizes > 320x240 are not working yet ov->maxwidth = 352;
ov->maxwidth = 320; ov->maxheight = 288;
ov->maxheight = 240;
// The OV518 cannot go as low as the sensor can // The OV518 cannot go as low as the sensor can
ov->minwidth = 160; ov->minwidth = 160;
...@@ -6390,7 +6188,7 @@ ov51x_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -6390,7 +6188,7 @@ ov51x_probe(struct usb_device *dev, unsigned int ifnum,
ov->stop_during_set = !fastset; ov->stop_during_set = !fastset;
ov->tuner_type = tuner; ov->tuner_type = tuner;
ov->backlight = backlight; ov->backlight = backlight;
ov->mirror = mirror;
ov->auto_brt = autobright; ov->auto_brt = autobright;
ov->auto_gain = autogain; ov->auto_gain = autogain;
ov->auto_exp = autoexp; ov->auto_exp = autoexp;
...@@ -6425,11 +6223,6 @@ ov51x_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -6425,11 +6223,6 @@ ov51x_probe(struct usb_device *dev, unsigned int ifnum,
info("USB %s video device found", symbolic(brglist, ov->bridge)); info("USB %s video device found", symbolic(brglist, ov->bridge));
/* Workaround for some applications that want data in RGB
* instead of BGR. */
if (force_rgb)
info("data format set to RGB");
init_waitqueue_head(&ov->wq); init_waitqueue_head(&ov->wq);
init_MUTEX(&ov->lock); /* to 1 == available */ init_MUTEX(&ov->lock); /* to 1 == available */
...@@ -6528,7 +6321,6 @@ ov51x_probe(struct usb_device *dev, unsigned int ifnum, ...@@ -6528,7 +6321,6 @@ ov51x_probe(struct usb_device *dev, unsigned int ifnum,
return NULL; return NULL;
} }
static void static void
ov51x_disconnect(struct usb_device *dev, void *ptr) ov51x_disconnect(struct usb_device *dev, void *ptr)
{ {
...@@ -6537,7 +6329,6 @@ ov51x_disconnect(struct usb_device *dev, void *ptr) ...@@ -6537,7 +6329,6 @@ ov51x_disconnect(struct usb_device *dev, void *ptr)
PDEBUG(3, ""); PDEBUG(3, "");
/* We don't want people trying to open up the device */
video_unregister_device(&ov->vdev); video_unregister_device(&ov->vdev);
if (ov->user) if (ov->user)
PDEBUG(3, "Device open...deferring video_unregister_device"); PDEBUG(3, "Device open...deferring video_unregister_device");
...@@ -6682,9 +6473,6 @@ usb_ov511_init(void) ...@@ -6682,9 +6473,6 @@ usb_ov511_init(void)
if (usb_register(&ov511_driver) < 0) if (usb_register(&ov511_driver) < 0)
return -1; return -1;
// FIXME: Don't know how to determine this yet
ov51x_mmx_available = 0;
#if defined (__i386__) #if defined (__i386__)
if (test_bit(X86_FEATURE_MMX, &boot_cpu_data.x86_capability)) if (test_bit(X86_FEATURE_MMX, &boot_cpu_data.x86_capability))
ov51x_mmx_available = 1; ov51x_mmx_available = 1;
...@@ -6709,6 +6497,5 @@ usb_ov511_exit(void) ...@@ -6709,6 +6497,5 @@ usb_ov511_exit(void)
module_init(usb_ov511_init); module_init(usb_ov511_init);
module_exit(usb_ov511_exit); module_exit(usb_ov511_exit);
/* No version, for compatibility with binary-only modules */ EXPORT_SYMBOL(ov511_register_decomp_module);
EXPORT_SYMBOL_NOVERS(ov511_register_decomp_module); EXPORT_SYMBOL(ov511_deregister_decomp_module);
EXPORT_SYMBOL_NOVERS(ov511_deregister_decomp_module);
...@@ -444,7 +444,7 @@ struct ov511_frame { ...@@ -444,7 +444,7 @@ struct ov511_frame {
int snapshot; /* True if frame was a snapshot */ int snapshot; /* True if frame was a snapshot */
}; };
#define DECOMP_INTERFACE_VER 3 #define DECOMP_INTERFACE_VER 4
/* Compression module operations */ /* Compression module operations */
struct ov51x_decomp_ops { struct ov51x_decomp_ops {
...@@ -454,8 +454,7 @@ struct ov51x_decomp_ops { ...@@ -454,8 +454,7 @@ struct ov51x_decomp_ops {
int, int, int); int, int, int);
int (*decomp_422)(unsigned char *, unsigned char *, unsigned char *, int (*decomp_422)(unsigned char *, unsigned char *, unsigned char *,
int, int, int); int, int, int);
void (*decomp_lock)(void); struct module *owner;
void (*decomp_unlock)(void);
}; };
struct usb_ov511 { struct usb_ov511 {
...@@ -484,6 +483,7 @@ struct usb_ov511 { ...@@ -484,6 +483,7 @@ struct usb_ov511 {
int auto_gain; /* Auto gain control enabled flag */ int auto_gain; /* Auto gain control enabled flag */
int auto_exp; /* Auto exposure enabled flag */ int auto_exp; /* Auto exposure enabled flag */
int backlight; /* Backlight exposure algorithm flag */ int backlight; /* Backlight exposure algorithm flag */
int mirror; /* Image is reversed horizontally */
int led_policy; /* LED: off|on|auto; OV511+ only */ int led_policy; /* LED: off|on|auto; OV511+ only */
...@@ -522,9 +522,9 @@ struct usb_ov511 { ...@@ -522,9 +522,9 @@ struct usb_ov511 {
int bclass; /* Class of bridge (BCL_*) */ int bclass; /* Class of bridge (BCL_*) */
int sensor; /* Type of image sensor chip (SEN_*) */ int sensor; /* Type of image sensor chip (SEN_*) */
int sclass; /* Type of image sensor chip (SCL_*) */ int sclass; /* Type of image sensor chip (SCL_*) */
int tuner; /* Type of TV tuner */
int packet_size; /* Frame size per isoc desc */ int packet_size; /* Frame size per isoc desc */
int packet_numbering; /* Is ISO frame numbering enabled? */
struct semaphore param_lock; /* params lock for this camera */ struct semaphore param_lock; /* params lock for this camera */
...@@ -555,10 +555,13 @@ struct usb_ov511 { ...@@ -555,10 +555,13 @@ struct usb_ov511 {
int has_audio_proc; /* Device has an audio processor */ int has_audio_proc; /* Device has an audio processor */
int freq; /* Current tuner frequency */ int freq; /* Current tuner frequency */
int tuner_type; /* Specific tuner model */ int tuner_type; /* Specific tuner model */
int pal; /* Device is designed for PAL resolution */
/* I2C interface to kernel */ /* I2C interface to kernel */
struct semaphore i2c_lock; /* Protect I2C controller regs */ struct semaphore i2c_lock; /* Protect I2C controller regs */
unsigned char primary_i2c_slave; /* I2C write id of sensor */ unsigned char primary_i2c_slave; /* I2C write id of sensor */
unsigned char tuner_i2c_slave; /* I2C write id of tuner */
unsigned char audio_i2c_slave; /* I2C write id of audio processor */
/* Control transaction stuff */ /* Control transaction stuff */
unsigned char *cbuf; /* Buffer for payload */ unsigned char *cbuf; /* Buffer for payload */
...@@ -588,18 +591,6 @@ symbolic(struct symbolic_list list[], int num) ...@@ -588,18 +591,6 @@ symbolic(struct symbolic_list list[], int num)
return (NOT_DEFINED_STR); return (NOT_DEFINED_STR);
} }
struct mode_list_518 {
int width;
int height;
u8 reg28;
u8 reg29;
u8 reg2a;
u8 reg2c;
u8 reg2e;
u8 reg24;
u8 reg25;
};
/* Compression stuff */ /* Compression stuff */
#define OV511_QUANTABLESIZE 64 #define OV511_QUANTABLESIZE 64
......
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