2005-04-16 15:20:36 -07:00
/*
* OmniVision OV511 Camera - to - USB Bridge Driver
*
* Copyright ( c ) 1999 - 2003 Mark W . McClelland
* Original decompression code Copyright 1998 - 2000 OmniVision Technologies
* Many improvements by Bret Wallach < bwallac1 @ san . rr . com >
* Color fixes by by Orion Sky Lawlor < olawlor @ acm . org > ( 2 / 26 / 2000 )
* Snapshot code by Kevin Moore
* OV7620 fixes by Charl P . Botha < cpbotha @ ieee . org >
* Changes by Claudio Matsuoka < claudio @ conectiva . com >
* Original SAA7111A code by Dave Perks < dperks @ ibm . net >
* URB error messages from pwc driver by Nemosoft
* 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 ,
* Scott J . Bertin and Johannes Erdfelt .
*
* Please see the file : Documentation / usb / ov511 . txt
* and the website at : http : //alpha.dyndns.org/ov511
* for more info .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/vmalloc.h>
# include <linux/slab.h>
# include <linux/ctype.h>
# include <linux/pagemap.h>
# include <asm/semaphore.h>
# include <asm/processor.h>
# include <linux/mm.h>
# include <linux/device.h>
# if defined (__i386__)
# include <asm/cpufeature.h>
# endif
# include "ov511.h"
/*
* Version Information
*/
# define DRIVER_VERSION "v1.64 for Linux 2.5"
# define EMAIL "mark@alpha.dyndns.org"
# define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org> & Bret Wallach \
& Orion Sky Lawlor < olawlor @ acm . org > & Kevin Moore & Charl P . Botha \
< cpbotha @ ieee . org > & Claudio Matsuoka < claudio @ conectiva . com > "
# define DRIVER_DESC "ov511 USB Camera Driver"
# define OV511_I2C_RETRIES 3
# define ENABLE_Y_QUANTABLE 1
# define ENABLE_UV_QUANTABLE 1
# define OV511_MAX_UNIT_VIDEO 16
/* Pixel count * bytes per YUV420 pixel (1.5) */
# define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3 / 2)
# 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 */
# define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024)
# define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
/**********************************************************************
* Module Parameters
* ( See ov511 . txt for detailed descriptions of these )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* These variables (and all static globals) default to zero */
static int autobright = 1 ;
static int autogain = 1 ;
static int autoexp = 1 ;
static int debug ;
static int snapshot ;
static int cams = 1 ;
static int compress ;
static int testpat ;
static int dumppix ;
static int led = 1 ;
static int dump_bridge ;
static int dump_sensor ;
static int printph ;
static int phy = 0x1f ;
static int phuv = 0x05 ;
static int pvy = 0x06 ;
static int pvuv = 0x06 ;
static int qhy = 0x14 ;
static int qhuv = 0x03 ;
static int qvy = 0x04 ;
static int qvuv = 0x04 ;
static int lightfreq ;
static int bandingfilter ;
static int clockdiv = - 1 ;
static int packetsize = - 1 ;
static int framedrop = - 1 ;
static int fastset ;
static int force_palette ;
static int backlight ;
static int unit_video [ OV511_MAX_UNIT_VIDEO ] ;
static int remove_zeros ;
static int mirror ;
static int ov518_color ;
module_param ( autobright , int , 0 ) ;
MODULE_PARM_DESC ( autobright , " Sensor automatically changes brightness " ) ;
module_param ( autogain , int , 0 ) ;
MODULE_PARM_DESC ( autogain , " Sensor automatically changes gain " ) ;
module_param ( autoexp , int , 0 ) ;
MODULE_PARM_DESC ( autoexp , " Sensor automatically changes exposure " ) ;
module_param ( debug , int , 0 ) ;
MODULE_PARM_DESC ( debug ,
" Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max " ) ;
module_param ( snapshot , int , 0 ) ;
MODULE_PARM_DESC ( snapshot , " Enable snapshot mode " ) ;
module_param ( cams , int , 0 ) ;
MODULE_PARM_DESC ( cams , " Number of simultaneous cameras " ) ;
module_param ( compress , int , 0 ) ;
MODULE_PARM_DESC ( compress , " Turn on compression " ) ;
module_param ( testpat , int , 0 ) ;
MODULE_PARM_DESC ( testpat ,
" Replace image with vertical bar testpattern (only partially working) " ) ;
module_param ( dumppix , int , 0 ) ;
MODULE_PARM_DESC ( dumppix , " Dump raw pixel data " ) ;
module_param ( led , int , 0 ) ;
MODULE_PARM_DESC ( led ,
" LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open) " ) ;
module_param ( dump_bridge , int , 0 ) ;
MODULE_PARM_DESC ( dump_bridge , " Dump the bridge registers " ) ;
module_param ( dump_sensor , int , 0 ) ;
MODULE_PARM_DESC ( dump_sensor , " Dump the sensor registers " ) ;
module_param ( printph , int , 0 ) ;
MODULE_PARM_DESC ( printph , " Print frame start/end headers " ) ;
module_param ( phy , int , 0 ) ;
MODULE_PARM_DESC ( phy , " Prediction range (horiz. Y) " ) ;
module_param ( phuv , int , 0 ) ;
MODULE_PARM_DESC ( phuv , " Prediction range (horiz. UV) " ) ;
module_param ( pvy , int , 0 ) ;
MODULE_PARM_DESC ( pvy , " Prediction range (vert. Y) " ) ;
module_param ( pvuv , int , 0 ) ;
MODULE_PARM_DESC ( pvuv , " Prediction range (vert. UV) " ) ;
module_param ( qhy , int , 0 ) ;
MODULE_PARM_DESC ( qhy , " Quantization threshold (horiz. Y) " ) ;
module_param ( qhuv , int , 0 ) ;
MODULE_PARM_DESC ( qhuv , " Quantization threshold (horiz. UV) " ) ;
module_param ( qvy , int , 0 ) ;
MODULE_PARM_DESC ( qvy , " Quantization threshold (vert. Y) " ) ;
module_param ( qvuv , int , 0 ) ;
MODULE_PARM_DESC ( qvuv , " Quantization threshold (vert. UV) " ) ;
module_param ( lightfreq , int , 0 ) ;
MODULE_PARM_DESC ( lightfreq ,
" Light frequency. Set to 50 or 60 Hz, or zero for default settings " ) ;
module_param ( bandingfilter , int , 0 ) ;
MODULE_PARM_DESC ( bandingfilter ,
" Enable banding filter (to reduce effects of fluorescent lighting) " ) ;
module_param ( clockdiv , int , 0 ) ;
MODULE_PARM_DESC ( clockdiv , " Force pixel clock divisor to a specific value " ) ;
module_param ( packetsize , int , 0 ) ;
MODULE_PARM_DESC ( packetsize , " Force a specific isoc packet size " ) ;
module_param ( framedrop , int , 0 ) ;
MODULE_PARM_DESC ( framedrop , " Force a specific frame drop register setting " ) ;
module_param ( fastset , int , 0 ) ;
MODULE_PARM_DESC ( fastset , " Allows picture settings to take effect immediately " ) ;
module_param ( force_palette , int , 0 ) ;
MODULE_PARM_DESC ( force_palette , " Force the palette to a specific value " ) ;
module_param ( backlight , int , 0 ) ;
MODULE_PARM_DESC ( backlight , " For objects that are lit from behind " ) ;
static int num_uv ;
module_param_array ( unit_video , int , & num_uv , 0 ) ;
MODULE_PARM_DESC ( unit_video ,
" Force use of specific minor number(s). 0 is not allowed. " ) ;
module_param ( remove_zeros , int , 0 ) ;
MODULE_PARM_DESC ( remove_zeros ,
" Remove zero-padding from uncompressed incoming data " ) ;
module_param ( mirror , int , 0 ) ;
MODULE_PARM_DESC ( mirror , " Reverse image horizontally " ) ;
module_param ( ov518_color , int , 0 ) ;
MODULE_PARM_DESC ( ov518_color , " Enable OV518 color (experimental) " ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
/**********************************************************************
* Miscellaneous Globals
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct usb_driver ov511_driver ;
static struct ov51x_decomp_ops * ov511_decomp_ops ;
static struct ov51x_decomp_ops * ov511_mmx_decomp_ops ;
static struct ov51x_decomp_ops * ov518_decomp_ops ;
static struct ov51x_decomp_ops * ov518_mmx_decomp_ops ;
/* Number of times to retry a failed I2C transaction. Increase this if you
* are getting " Failed to read sensor ID... " */
static int i2c_detect_tries = 5 ;
/* MMX support is present in kernel and CPU. Checked upon decomp module load. */
# if defined(__i386__) || defined(__x86_64__)
# define ov51x_mmx_available (cpu_has_mmx)
# else
# define ov51x_mmx_available (0)
# endif
static struct usb_device_id device_table [ ] = {
{ USB_DEVICE ( VEND_OMNIVISION , PROD_OV511 ) } ,
{ USB_DEVICE ( VEND_OMNIVISION , PROD_OV511PLUS ) } ,
{ USB_DEVICE ( VEND_OMNIVISION , PROD_OV518 ) } ,
{ USB_DEVICE ( VEND_OMNIVISION , PROD_OV518PLUS ) } ,
{ USB_DEVICE ( VEND_MATTEL , PROD_ME2CAM ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , device_table ) ;
static unsigned char yQuanTable511 [ ] = OV511_YQUANTABLE ;
static unsigned char uvQuanTable511 [ ] = OV511_UVQUANTABLE ;
static unsigned char yQuanTable518 [ ] = OV518_YQUANTABLE ;
static unsigned char uvQuanTable518 [ ] = OV518_UVQUANTABLE ;
/**********************************************************************
* Symbolic Names
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Known OV511-based cameras */
static struct symbolic_list camlist [ ] = {
{ 0 , " Generic Camera (no ID) " } ,
{ 1 , " Mustek WCam 3X " } ,
{ 3 , " D-Link DSB-C300 " } ,
{ 4 , " Generic OV511/OV7610 " } ,
{ 5 , " Puretek PT-6007 " } ,
{ 6 , " Lifeview USB Life TV (NTSC) " } ,
{ 21 , " Creative Labs WebCam 3 " } ,
{ 22 , " Lifeview USB Life TV (PAL D/K+B/G) " } ,
{ 36 , " Koala-Cam " } ,
{ 38 , " Lifeview USB Life TV (PAL) " } ,
{ 41 , " Samsung Anycam MPC-M10 " } ,
{ 43 , " Mtekvision Zeca MV402 " } ,
{ 46 , " Suma eON " } ,
{ 70 , " Lifeview USB Life TV (PAL/SECAM) " } ,
{ 100 , " Lifeview RoboCam " } ,
{ 102 , " AverMedia InterCam Elite " } ,
{ 112 , " MediaForte MV300 " } , /* or OV7110 evaluation kit */
{ 134 , " Ezonics EZCam II " } ,
{ 192 , " Webeye 2000B " } ,
{ 253 , " Alpha Vision Tech. AlphaCam SE " } ,
{ - 1 , NULL }
} ;
/* Video4Linux1 Palettes */
static struct symbolic_list v4l1_plist [ ] = {
{ VIDEO_PALETTE_GREY , " GREY " } ,
{ VIDEO_PALETTE_HI240 , " HI240 " } ,
{ VIDEO_PALETTE_RGB565 , " RGB565 " } ,
{ VIDEO_PALETTE_RGB24 , " RGB24 " } ,
{ VIDEO_PALETTE_RGB32 , " RGB32 " } ,
{ VIDEO_PALETTE_RGB555 , " RGB555 " } ,
{ VIDEO_PALETTE_YUV422 , " YUV422 " } ,
{ VIDEO_PALETTE_YUYV , " YUYV " } ,
{ VIDEO_PALETTE_UYVY , " UYVY " } ,
{ VIDEO_PALETTE_YUV420 , " YUV420 " } ,
{ VIDEO_PALETTE_YUV411 , " YUV411 " } ,
{ VIDEO_PALETTE_RAW , " RAW " } ,
{ VIDEO_PALETTE_YUV422P , " YUV422P " } ,
{ VIDEO_PALETTE_YUV411P , " YUV411P " } ,
{ VIDEO_PALETTE_YUV420P , " YUV420P " } ,
{ VIDEO_PALETTE_YUV410P , " YUV410P " } ,
{ - 1 , NULL }
} ;
static struct symbolic_list brglist [ ] = {
{ BRG_OV511 , " OV511 " } ,
{ BRG_OV511PLUS , " OV511+ " } ,
{ BRG_OV518 , " OV518 " } ,
{ BRG_OV518PLUS , " OV518+ " } ,
{ - 1 , NULL }
} ;
static struct symbolic_list senlist [ ] = {
{ SEN_OV76BE , " OV76BE " } ,
{ SEN_OV7610 , " OV7610 " } ,
{ SEN_OV7620 , " OV7620 " } ,
{ SEN_OV7620AE , " OV7620AE " } ,
{ SEN_OV6620 , " OV6620 " } ,
{ SEN_OV6630 , " OV6630 " } ,
{ SEN_OV6630AE , " OV6630AE " } ,
{ SEN_OV6630AF , " OV6630AF " } ,
{ SEN_OV8600 , " OV8600 " } ,
{ SEN_KS0127 , " KS0127 " } ,
{ SEN_KS0127B , " KS0127B " } ,
{ SEN_SAA7111A , " SAA7111A " } ,
{ - 1 , NULL }
} ;
/* URB error codes: */
static struct symbolic_list urb_errlist [ ] = {
{ - ENOSR , " Buffer error (overrun) " } ,
{ - EPIPE , " Stalled (device not responding) " } ,
{ - EOVERFLOW , " Babble (bad cable?) " } ,
{ - EPROTO , " Bit-stuff error (bad cable?) " } ,
{ - EILSEQ , " CRC/Timeout " } ,
{ - ETIMEDOUT , " NAK (device does not respond) " } ,
{ - 1 , NULL }
} ;
/**********************************************************************
* Memory management
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void *
rvmalloc ( unsigned long size )
{
void * mem ;
unsigned long adr ;
size = PAGE_ALIGN ( size ) ;
mem = vmalloc_32 ( size ) ;
if ( ! mem )
return NULL ;
memset ( mem , 0 , size ) ; /* Clear the ram out, no junk to the user */
adr = ( unsigned long ) mem ;
while ( size > 0 ) {
SetPageReserved ( vmalloc_to_page ( ( void * ) adr ) ) ;
adr + = PAGE_SIZE ;
size - = PAGE_SIZE ;
}
return mem ;
}
static void
rvfree ( void * mem , unsigned long size )
{
unsigned long adr ;
if ( ! mem )
return ;
adr = ( unsigned long ) mem ;
while ( ( long ) size > 0 ) {
ClearPageReserved ( vmalloc_to_page ( ( void * ) adr ) ) ;
adr + = PAGE_SIZE ;
size - = PAGE_SIZE ;
}
vfree ( mem ) ;
}
/**********************************************************************
*
* Register I / O
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Write an OV51x register */
static int
reg_w ( struct usb_ov511 * ov , unsigned char reg , unsigned char value )
{
int rc ;
PDEBUG ( 5 , " 0x%02X:0x%02X " , reg , value ) ;
down ( & ov - > cbuf_lock ) ;
ov - > cbuf [ 0 ] = value ;
rc = usb_control_msg ( ov - > dev ,
usb_sndctrlpipe ( ov - > dev , 0 ) ,
( ov - > bclass = = BCL_OV518 ) ? 1 : 2 /* REG_IO */ ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , ( __u16 ) reg , & ov - > cbuf [ 0 ] , 1 , 1000 ) ;
up ( & ov - > cbuf_lock ) ;
if ( rc < 0 )
err ( " reg write: error %d: %s " , rc , symbolic ( urb_errlist , rc ) ) ;
return rc ;
}
/* Read from an OV51x register */
/* returns: negative is error, pos or zero is data */
static int
reg_r ( struct usb_ov511 * ov , unsigned char reg )
{
int rc ;
down ( & ov - > cbuf_lock ) ;
rc = usb_control_msg ( ov - > dev ,
usb_rcvctrlpipe ( ov - > dev , 0 ) ,
( ov - > bclass = = BCL_OV518 ) ? 1 : 3 /* REG_IO */ ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , ( __u16 ) reg , & ov - > cbuf [ 0 ] , 1 , 1000 ) ;
if ( rc < 0 ) {
err ( " reg read: error %d: %s " , rc , symbolic ( urb_errlist , rc ) ) ;
} else {
rc = ov - > cbuf [ 0 ] ;
PDEBUG ( 5 , " 0x%02X:0x%02X " , reg , ov - > cbuf [ 0 ] ) ;
}
up ( & ov - > cbuf_lock ) ;
return rc ;
}
/*
* Writes bits at positions specified by mask to an OV51x reg . Bits that are in
* the same position as 1 ' s in " mask " are cleared and set to " value " . Bits
* that are in the same position as 0 ' s in " mask " are preserved , regardless
* of their respective state in " value " .
*/
static int
reg_w_mask ( struct usb_ov511 * ov ,
unsigned char reg ,
unsigned char value ,
unsigned char mask )
{
int ret ;
unsigned char oldval , newval ;
ret = reg_r ( ov , reg ) ;
if ( ret < 0 )
return ret ;
oldval = ( unsigned char ) ret ;
oldval & = ( ~ mask ) ; /* Clear the masked bits */
value & = mask ; /* Enforce mask on value */
newval = oldval | value ; /* Set the desired bits */
return ( reg_w ( ov , reg , newval ) ) ;
}
/*
* Writes multiple ( n ) byte value to a single register . Only valid with certain
* registers ( 0x30 and 0xc4 - 0xce ) .
*/
static int
ov518_reg_w32 ( struct usb_ov511 * ov , unsigned char reg , u32 val , int n )
{
int rc ;
PDEBUG ( 5 , " 0x%02X:%7d, n=%d " , reg , val , n ) ;
down ( & ov - > cbuf_lock ) ;
* ( ( __le32 * ) ov - > cbuf ) = __cpu_to_le32 ( val ) ;
rc = usb_control_msg ( ov - > dev ,
usb_sndctrlpipe ( ov - > dev , 0 ) ,
1 /* REG_IO */ ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , ( __u16 ) reg , ov - > cbuf , n , 1000 ) ;
up ( & ov - > cbuf_lock ) ;
if ( rc < 0 )
err ( " reg write multiple: error %d: %s " , rc ,
symbolic ( urb_errlist , rc ) ) ;
return rc ;
}
static int
ov511_upload_quan_tables ( struct usb_ov511 * ov )
{
unsigned char * pYTable = yQuanTable511 ;
unsigned char * pUVTable = uvQuanTable511 ;
unsigned char val0 , val1 ;
int i , rc , reg = R511_COMP_LUT_BEGIN ;
PDEBUG ( 4 , " Uploading quantization tables " ) ;
for ( i = 0 ; i < OV511_QUANTABLESIZE / 2 ; i + + ) {
if ( ENABLE_Y_QUANTABLE ) {
val0 = * pYTable + + ;
val1 = * pYTable + + ;
val0 & = 0x0f ;
val1 & = 0x0f ;
val0 | = val1 < < 4 ;
rc = reg_w ( ov , reg , val0 ) ;
if ( rc < 0 )
return rc ;
}
if ( ENABLE_UV_QUANTABLE ) {
val0 = * pUVTable + + ;
val1 = * pUVTable + + ;
val0 & = 0x0f ;
val1 & = 0x0f ;
val0 | = val1 < < 4 ;
rc = reg_w ( ov , reg + OV511_QUANTABLESIZE / 2 , val0 ) ;
if ( rc < 0 )
return rc ;
}
reg + + ;
}
return 0 ;
}
/* OV518 quantization tables are 8x4 (instead of 8x8) */
static int
ov518_upload_quan_tables ( struct usb_ov511 * ov )
{
unsigned char * pYTable = yQuanTable518 ;
unsigned char * pUVTable = uvQuanTable518 ;
unsigned char val0 , val1 ;
int i , rc , reg = R511_COMP_LUT_BEGIN ;
PDEBUG ( 4 , " Uploading quantization tables " ) ;
for ( i = 0 ; i < OV518_QUANTABLESIZE / 2 ; i + + ) {
if ( ENABLE_Y_QUANTABLE ) {
val0 = * pYTable + + ;
val1 = * pYTable + + ;
val0 & = 0x0f ;
val1 & = 0x0f ;
val0 | = val1 < < 4 ;
rc = reg_w ( ov , reg , val0 ) ;
if ( rc < 0 )
return rc ;
}
if ( ENABLE_UV_QUANTABLE ) {
val0 = * pUVTable + + ;
val1 = * pUVTable + + ;
val0 & = 0x0f ;
val1 & = 0x0f ;
val0 | = val1 < < 4 ;
rc = reg_w ( ov , reg + OV518_QUANTABLESIZE / 2 , val0 ) ;
if ( rc < 0 )
return rc ;
}
reg + + ;
}
return 0 ;
}
static int
ov51x_reset ( struct usb_ov511 * ov , unsigned char reset_type )
{
int rc ;
/* Setting bit 0 not allowed on 518/518Plus */
if ( ov - > bclass = = BCL_OV518 )
reset_type & = 0xfe ;
PDEBUG ( 4 , " Reset: type=0x%02X " , reset_type ) ;
rc = reg_w ( ov , R51x_SYS_RESET , reset_type ) ;
rc = reg_w ( ov , R51x_SYS_RESET , 0 ) ;
if ( rc < 0 )
err ( " reset: command failed " ) ;
return rc ;
}
/**********************************************************************
*
* Low - level I2C I / O functions
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* NOTE: Do not call this function directly!
* The OV518 I2C I / O procedure is different , hence , this function .
* This is normally only called from i2c_w ( ) . Note that this function
* always succeeds regardless of whether the sensor is present and working .
*/
static int
ov518_i2c_write_internal ( struct usb_ov511 * ov ,
unsigned char reg ,
unsigned char value )
{
int rc ;
PDEBUG ( 5 , " 0x%02X:0x%02X " , reg , value ) ;
/* Select camera register */
rc = reg_w ( ov , R51x_I2C_SADDR_3 , reg ) ;
if ( rc < 0 )
return rc ;
/* Write "value" to I2C data port of OV511 */
rc = reg_w ( ov , R51x_I2C_DATA , value ) ;
if ( rc < 0 )
return rc ;
/* Initiate 3-byte write cycle */
rc = reg_w ( ov , R518_I2C_CTL , 0x01 ) ;
if ( rc < 0 )
return rc ;
return 0 ;
}
/* NOTE: Do not call this function directly! */
static int
ov511_i2c_write_internal ( struct usb_ov511 * ov ,
unsigned char reg ,
unsigned char value )
{
int rc , retries ;
PDEBUG ( 5 , " 0x%02X:0x%02X " , reg , value ) ;
/* Three byte write cycle */
for ( retries = OV511_I2C_RETRIES ; ; ) {
/* Select camera register */
rc = reg_w ( ov , R51x_I2C_SADDR_3 , reg ) ;
if ( rc < 0 )
break ;
/* Write "value" to I2C data port of OV511 */
rc = reg_w ( ov , R51x_I2C_DATA , value ) ;
if ( rc < 0 )
break ;
/* Initiate 3-byte write cycle */
rc = reg_w ( ov , R511_I2C_CTL , 0x01 ) ;
if ( rc < 0 )
break ;
/* Retry until idle */
do
rc = reg_r ( ov , R511_I2C_CTL ) ;
while ( rc > 0 & & ( ( rc & 1 ) = = 0 ) ) ;
if ( rc < 0 )
break ;
/* Ack? */
if ( ( rc & 2 ) = = 0 ) {
rc = 0 ;
break ;
}
#if 0
/* I2C abort */
reg_w ( ov , R511_I2C_CTL , 0x10 ) ;
# endif
if ( - - retries < 0 ) {
err ( " i2c write retries exhausted " ) ;
rc = - 1 ;
break ;
}
}
return rc ;
}
/* NOTE: Do not call this function directly!
* The OV518 I2C I / O procedure is different , hence , this function .
* This is normally only called from i2c_r ( ) . Note that this function
* always succeeds regardless of whether the sensor is present and working .
*/
static int
ov518_i2c_read_internal ( struct usb_ov511 * ov , unsigned char reg )
{
int rc , value ;
/* Select camera register */
rc = reg_w ( ov , R51x_I2C_SADDR_2 , reg ) ;
if ( rc < 0 )
return rc ;
/* Initiate 2-byte write cycle */
rc = reg_w ( ov , R518_I2C_CTL , 0x03 ) ;
if ( rc < 0 )
return rc ;
/* Initiate 2-byte read cycle */
rc = reg_w ( ov , R518_I2C_CTL , 0x05 ) ;
if ( rc < 0 )
return rc ;
value = reg_r ( ov , R51x_I2C_DATA ) ;
PDEBUG ( 5 , " 0x%02X:0x%02X " , reg , value ) ;
return value ;
}
/* NOTE: Do not call this function directly!
* returns : negative is error , pos or zero is data */
static int
ov511_i2c_read_internal ( struct usb_ov511 * ov , unsigned char reg )
{
int rc , value , retries ;
/* Two byte write cycle */
for ( retries = OV511_I2C_RETRIES ; ; ) {
/* Select camera register */
rc = reg_w ( ov , R51x_I2C_SADDR_2 , reg ) ;
if ( rc < 0 )
return rc ;
/* Initiate 2-byte write cycle */
rc = reg_w ( ov , R511_I2C_CTL , 0x03 ) ;
if ( rc < 0 )
return rc ;
/* Retry until idle */
do
rc = reg_r ( ov , R511_I2C_CTL ) ;
while ( rc > 0 & & ( ( rc & 1 ) = = 0 ) ) ;
if ( rc < 0 )
return rc ;
if ( ( rc & 2 ) = = 0 ) /* Ack? */
break ;
/* I2C abort */
reg_w ( ov , R511_I2C_CTL , 0x10 ) ;
if ( - - retries < 0 ) {
err ( " i2c write retries exhausted " ) ;
return - 1 ;
}
}
/* Two byte read cycle */
for ( retries = OV511_I2C_RETRIES ; ; ) {
/* Initiate 2-byte read cycle */
rc = reg_w ( ov , R511_I2C_CTL , 0x05 ) ;
if ( rc < 0 )
return rc ;
/* Retry until idle */
do
rc = reg_r ( ov , R511_I2C_CTL ) ;
while ( rc > 0 & & ( ( rc & 1 ) = = 0 ) ) ;
if ( rc < 0 )
return rc ;
if ( ( rc & 2 ) = = 0 ) /* Ack? */
break ;
/* I2C abort */
rc = reg_w ( ov , R511_I2C_CTL , 0x10 ) ;
if ( rc < 0 )
return rc ;
if ( - - retries < 0 ) {
err ( " i2c read retries exhausted " ) ;
return - 1 ;
}
}
value = reg_r ( ov , R51x_I2C_DATA ) ;
PDEBUG ( 5 , " 0x%02X:0x%02X " , reg , value ) ;
/* This is needed to make i2c_w() work */
rc = reg_w ( ov , R511_I2C_CTL , 0x05 ) ;
if ( rc < 0 )
return rc ;
return value ;
}
/* returns: negative is error, pos or zero is data */
static int
i2c_r ( struct usb_ov511 * ov , unsigned char reg )
{
int rc ;
down ( & ov - > i2c_lock ) ;
if ( ov - > bclass = = BCL_OV518 )
rc = ov518_i2c_read_internal ( ov , reg ) ;
else
rc = ov511_i2c_read_internal ( ov , reg ) ;
up ( & ov - > i2c_lock ) ;
return rc ;
}
static int
i2c_w ( struct usb_ov511 * ov , unsigned char reg , unsigned char value )
{
int rc ;
down ( & ov - > i2c_lock ) ;
if ( ov - > bclass = = BCL_OV518 )
rc = ov518_i2c_write_internal ( ov , reg , value ) ;
else
rc = ov511_i2c_write_internal ( ov , reg , value ) ;
up ( & ov - > i2c_lock ) ;
return rc ;
}
/* Do not call this function directly! */
static int
ov51x_i2c_write_mask_internal ( struct usb_ov511 * ov ,
unsigned char reg ,
unsigned char value ,
unsigned char mask )
{
int rc ;
unsigned char oldval , newval ;
if ( mask = = 0xff ) {
newval = value ;
} else {
if ( ov - > bclass = = BCL_OV518 )
rc = ov518_i2c_read_internal ( ov , reg ) ;
else
rc = ov511_i2c_read_internal ( ov , reg ) ;
if ( rc < 0 )
return rc ;
oldval = ( unsigned char ) rc ;
oldval & = ( ~ mask ) ; /* Clear the masked bits */
value & = mask ; /* Enforce mask on value */
newval = oldval | value ; /* Set the desired bits */
}
if ( ov - > bclass = = BCL_OV518 )
return ( ov518_i2c_write_internal ( ov , reg , newval ) ) ;
else
return ( ov511_i2c_write_internal ( ov , reg , newval ) ) ;
}
/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
* the same position as 1 ' s in " mask " are cleared and set to " value " . Bits
* that are in the same position as 0 ' s in " mask " are preserved , regardless
* of their respective state in " value " .
*/
static int
i2c_w_mask ( struct usb_ov511 * ov ,
unsigned char reg ,
unsigned char value ,
unsigned char mask )
{
int rc ;
down ( & ov - > i2c_lock ) ;
rc = ov51x_i2c_write_mask_internal ( ov , reg , value , mask ) ;
up ( & ov - > i2c_lock ) ;
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 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 */
static int
i2c_w_slave ( struct usb_ov511 * ov ,
unsigned char slave ,
unsigned char reg ,
unsigned char value ,
unsigned char mask )
{
int rc = 0 ;
down ( & ov - > i2c_lock ) ;
/* Set new slave IDs */
rc = i2c_set_slave_internal ( ov , slave ) ;
if ( rc < 0 )
goto out ;
rc = ov51x_i2c_write_mask_internal ( ov , reg , value , mask ) ;
out :
/* Restore primary IDs */
if ( i2c_set_slave_internal ( ov , ov - > primary_i2c_slave ) < 0 )
err ( " Couldn't restore primary I2C slave " ) ;
up ( & ov - > i2c_lock ) ;
return rc ;
}
/* Read from a specific I2C slave ID and register */
static int
i2c_r_slave ( struct usb_ov511 * ov ,
unsigned char slave ,
unsigned char reg )
{
int rc ;
down ( & ov - > i2c_lock ) ;
/* Set new slave IDs */
rc = i2c_set_slave_internal ( ov , slave ) ;
if ( rc < 0 )
goto out ;
if ( ov - > bclass = = BCL_OV518 )
rc = ov518_i2c_read_internal ( ov , reg ) ;
else
rc = ov511_i2c_read_internal ( ov , reg ) ;
out :
/* Restore primary IDs */
if ( i2c_set_slave_internal ( ov , ov - > primary_i2c_slave ) < 0 )
err ( " Couldn't restore primary I2C slave " ) ;
up ( & ov - > i2c_lock ) ;
return rc ;
}
/* Sets I2C read and write slave IDs. Returns <0 for error */
static int
ov51x_set_slave_ids ( struct usb_ov511 * ov , unsigned char sid )
{
int rc ;
down ( & ov - > i2c_lock ) ;
rc = i2c_set_slave_internal ( ov , sid ) ;
if ( rc < 0 )
goto out ;
// FIXME: Is this actually necessary?
rc = ov51x_reset ( ov , OV511_RESET_NOREGS ) ;
out :
up ( & ov - > i2c_lock ) ;
return rc ;
}
static int
write_regvals ( struct usb_ov511 * ov , struct ov511_regvals * pRegvals )
{
int rc ;
while ( pRegvals - > bus ! = OV511_DONE_BUS ) {
if ( pRegvals - > bus = = OV511_REG_BUS ) {
if ( ( rc = reg_w ( ov , pRegvals - > reg , pRegvals - > val ) ) < 0 )
return rc ;
} else if ( pRegvals - > bus = = OV511_I2C_BUS ) {
if ( ( rc = i2c_w ( ov , pRegvals - > reg , pRegvals - > val ) ) < 0 )
return rc ;
} else {
err ( " Bad regval array " ) ;
return - 1 ;
}
pRegvals + + ;
}
return 0 ;
}
# ifdef OV511_DEBUG
static void
dump_i2c_range ( struct usb_ov511 * ov , int reg1 , int regn )
{
int i , rc ;
for ( i = reg1 ; i < = regn ; i + + ) {
rc = i2c_r ( ov , i ) ;
info ( " Sensor[0x%02X] = 0x%02X " , i , rc ) ;
}
}
static void
dump_i2c_regs ( struct usb_ov511 * ov )
{
info ( " I2C REGS " ) ;
dump_i2c_range ( ov , 0x00 , 0x7C ) ;
}
static void
dump_reg_range ( struct usb_ov511 * ov , int reg1 , int regn )
{
int i , rc ;
for ( i = reg1 ; i < = regn ; i + + ) {
rc = reg_r ( ov , i ) ;
info ( " OV511[0x%02X] = 0x%02X " , i , rc ) ;
}
}
static void
ov511_dump_regs ( struct usb_ov511 * ov )
{
info ( " CAMERA INTERFACE REGS " ) ;
dump_reg_range ( ov , 0x10 , 0x1f ) ;
info ( " DRAM INTERFACE REGS " ) ;
dump_reg_range ( ov , 0x20 , 0x23 ) ;
info ( " ISO FIFO REGS " ) ;
dump_reg_range ( ov , 0x30 , 0x31 ) ;
info ( " PIO REGS " ) ;
dump_reg_range ( ov , 0x38 , 0x39 ) ;
dump_reg_range ( ov , 0x3e , 0x3e ) ;
info ( " I2C REGS " ) ;
dump_reg_range ( ov , 0x40 , 0x49 ) ;
info ( " SYSTEM CONTROL REGS " ) ;
dump_reg_range ( ov , 0x50 , 0x55 ) ;
dump_reg_range ( ov , 0x5e , 0x5f ) ;
info ( " OmniCE REGS " ) ;
dump_reg_range ( ov , 0x70 , 0x79 ) ;
/* NOTE: Quantization tables are not readable. You will get the value
* in reg . 0x79 for every table register */
dump_reg_range ( ov , 0x80 , 0x9f ) ;
dump_reg_range ( ov , 0xa0 , 0xbf ) ;
}
static void
ov518_dump_regs ( struct usb_ov511 * ov )
{
info ( " VIDEO MODE REGS " ) ;
dump_reg_range ( ov , 0x20 , 0x2f ) ;
info ( " DATA PUMP AND SNAPSHOT REGS " ) ;
dump_reg_range ( ov , 0x30 , 0x3f ) ;
info ( " I2C REGS " ) ;
dump_reg_range ( ov , 0x40 , 0x4f ) ;
info ( " SYSTEM CONTROL AND VENDOR REGS " ) ;
dump_reg_range ( ov , 0x50 , 0x5f ) ;
info ( " 60 - 6F " ) ;
dump_reg_range ( ov , 0x60 , 0x6f ) ;
info ( " 70 - 7F " ) ;
dump_reg_range ( ov , 0x70 , 0x7f ) ;
info ( " Y QUANTIZATION TABLE " ) ;
dump_reg_range ( ov , 0x80 , 0x8f ) ;
info ( " UV QUANTIZATION TABLE " ) ;
dump_reg_range ( ov , 0x90 , 0x9f ) ;
info ( " A0 - BF " ) ;
dump_reg_range ( ov , 0xa0 , 0xbf ) ;
info ( " CBR " ) ;
dump_reg_range ( ov , 0xc0 , 0xcf ) ;
}
# endif
/*****************************************************************************/
/* Temporarily stops OV511 from functioning. Must do this before changing
* registers while the camera is streaming */
static inline int
ov51x_stop ( struct usb_ov511 * ov )
{
PDEBUG ( 4 , " stopping " ) ;
ov - > stopped = 1 ;
if ( ov - > bclass = = BCL_OV518 )
return ( reg_w_mask ( ov , R51x_SYS_RESET , 0x3a , 0x3a ) ) ;
else
return ( reg_w ( ov , R51x_SYS_RESET , 0x3d ) ) ;
}
/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
* actually stopped ( for performance ) . */
static inline int
ov51x_restart ( struct usb_ov511 * ov )
{
if ( ov - > stopped ) {
PDEBUG ( 4 , " restarting " ) ;
ov - > stopped = 0 ;
/* Reinitialize the stream */
if ( ov - > bclass = = BCL_OV518 )
reg_w ( ov , 0x2f , 0x80 ) ;
return ( reg_w ( ov , R51x_SYS_RESET , 0x00 ) ) ;
}
return 0 ;
}
/* Sleeps until no frames are active. Returns !0 if got signal */
static int
ov51x_wait_frames_inactive ( struct usb_ov511 * ov )
{
return wait_event_interruptible ( ov - > wq , ov - > curframe < 0 ) ;
}
/* Resets the hardware snapshot button */
static void
ov51x_clear_snapshot ( struct usb_ov511 * ov )
{
if ( ov - > bclass = = BCL_OV511 ) {
reg_w ( ov , R51x_SYS_SNAP , 0x00 ) ;
reg_w ( ov , R51x_SYS_SNAP , 0x02 ) ;
reg_w ( ov , R51x_SYS_SNAP , 0x00 ) ;
} else if ( ov - > bclass = = BCL_OV518 ) {
warn ( " snapshot reset not supported yet on OV518(+) " ) ;
} else {
err ( " clear snap: invalid bridge type " ) ;
}
}
#if 0
/* Checks the status of the snapshot button. Returns 1 if it was pressed since
* it was last cleared , and zero in all other cases ( including errors ) */
static int
ov51x_check_snapshot ( struct usb_ov511 * ov )
{
int ret , status = 0 ;
if ( ov - > bclass = = BCL_OV511 ) {
ret = reg_r ( ov , R51x_SYS_SNAP ) ;
if ( ret < 0 ) {
err ( " Error checking snspshot status (%d) " , ret ) ;
} else if ( ret & 0x08 ) {
status = 1 ;
}
} else if ( ov - > bclass = = BCL_OV518 ) {
warn ( " snapshot check not supported yet on OV518(+) " ) ;
} else {
err ( " check snap: invalid bridge type " ) ;
}
return status ;
}
# endif
/* This does an initial reset of an OmniVision sensor and ensures that I2C
* is synchronized . Returns < 0 for failure .
*/
static int
init_ov_sensor ( struct usb_ov511 * ov )
{
int i , success ;
/* Reset the sensor */
if ( i2c_w ( ov , 0x12 , 0x80 ) < 0 )
return - EIO ;
/* Wait for it to initialize */
msleep ( 150 ) ;
for ( i = 0 , success = 0 ; i < i2c_detect_tries & & ! success ; i + + ) {
if ( ( i2c_r ( ov , OV7610_REG_ID_HIGH ) = = 0x7F ) & &
( i2c_r ( ov , OV7610_REG_ID_LOW ) = = 0xA2 ) ) {
success = 1 ;
continue ;
}
/* Reset the sensor */
if ( i2c_w ( ov , 0x12 , 0x80 ) < 0 )
return - EIO ;
/* Wait for it to initialize */
msleep ( 150 ) ;
/* Dummy read to sync I2C */
if ( i2c_r ( ov , 0x00 ) < 0 )
return - EIO ;
}
if ( ! success )
return - EIO ;
PDEBUG ( 1 , " I2C synced in %d attempt(s) " , i ) ;
return 0 ;
}
static int
ov511_set_packet_size ( struct usb_ov511 * ov , int size )
{
int alt , mult ;
if ( ov51x_stop ( ov ) < 0 )
return - EIO ;
mult = size > > 5 ;
if ( ov - > bridge = = BRG_OV511 ) {
if ( size = = 0 )
alt = OV511_ALT_SIZE_0 ;
else if ( size = = 257 )
alt = OV511_ALT_SIZE_257 ;
else if ( size = = 513 )
alt = OV511_ALT_SIZE_513 ;
else if ( size = = 769 )
alt = OV511_ALT_SIZE_769 ;
else if ( size = = 993 )
alt = OV511_ALT_SIZE_993 ;
else {
err ( " Set packet size: invalid size (%d) " , size ) ;
return - EINVAL ;
}
} else if ( ov - > bridge = = BRG_OV511PLUS ) {
if ( size = = 0 )
alt = OV511PLUS_ALT_SIZE_0 ;
else if ( size = = 33 )
alt = OV511PLUS_ALT_SIZE_33 ;
else if ( size = = 129 )
alt = OV511PLUS_ALT_SIZE_129 ;
else if ( size = = 257 )
alt = OV511PLUS_ALT_SIZE_257 ;
else if ( size = = 385 )
alt = OV511PLUS_ALT_SIZE_385 ;
else if ( size = = 513 )
alt = OV511PLUS_ALT_SIZE_513 ;
else if ( size = = 769 )
alt = OV511PLUS_ALT_SIZE_769 ;
else if ( size = = 961 )
alt = OV511PLUS_ALT_SIZE_961 ;
else {
err ( " Set packet size: invalid size (%d) " , size ) ;
return - EINVAL ;
}
} 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 ;
else if ( size = = 128 )
alt = OV518_ALT_SIZE_128 ;
else if ( size = = 256 )
alt = OV518_ALT_SIZE_256 ;
else if ( size = = 384 )
alt = OV518_ALT_SIZE_384 ;
else if ( size = = 512 )
alt = OV518_ALT_SIZE_512 ;
else if ( size = = 640 )
alt = OV518_ALT_SIZE_640 ;
else if ( size = = 768 )
alt = OV518_ALT_SIZE_768 ;
else if ( size = = 896 )
alt = OV518_ALT_SIZE_896 ;
else {
err ( " Set packet size: invalid size (%d) " , size ) ;
return - EINVAL ;
}
} else {
err ( " Set packet size: Invalid bridge type " ) ;
return - EINVAL ;
}
PDEBUG ( 3 , " %d, alt=%d " , size , alt ) ;
ov - > packet_size = size ;
if ( size > 0 ) {
/* Program ISO FIFO size reg (packet number isn't included) */
ov518_reg_w32 ( ov , 0x30 , size , 2 ) ;
if ( ov - > packet_numbering )
+ + ov - > packet_size ;
}
if ( usb_set_interface ( ov - > dev , ov - > iface , alt ) < 0 ) {
err ( " Set packet size: set interface error " ) ;
return - EBUSY ;
}
/* Initialize the stream */
if ( reg_w ( ov , 0x2f , 0x80 ) < 0 )
return - EIO ;
if ( ov51x_restart ( ov ) < 0 )
return - EIO ;
if ( ov51x_reset ( ov , OV511_RESET_NOREGS ) < 0 )
return - EIO ;
return 0 ;
}
/* Upload compression params and quantization tables. Returns 0 for success. */
static int
ov511_init_compression ( struct usb_ov511 * ov )
{
int rc = 0 ;
if ( ! ov - > compress_inited ) {
reg_w ( ov , 0x70 , phy ) ;
reg_w ( ov , 0x71 , phuv ) ;
reg_w ( ov , 0x72 , pvy ) ;
reg_w ( ov , 0x73 , pvuv ) ;
reg_w ( ov , 0x74 , qhy ) ;
reg_w ( ov , 0x75 , qhuv ) ;
reg_w ( ov , 0x76 , qvy ) ;
reg_w ( ov , 0x77 , qvuv ) ;
if ( ov511_upload_quan_tables ( ov ) < 0 ) {
err ( " Error uploading quantization tables " ) ;
rc = - EIO ;
goto out ;
}
}
ov - > compress_inited = 1 ;
out :
return rc ;
}
/* Upload compression params and quantization tables. Returns 0 for success. */
static int
ov518_init_compression ( struct usb_ov511 * ov )
{
int rc = 0 ;
if ( ! ov - > compress_inited ) {
if ( ov518_upload_quan_tables ( ov ) < 0 ) {
err ( " Error uploading quantization tables " ) ;
rc = - EIO ;
goto out ;
}
}
ov - > compress_inited = 1 ;
out :
return rc ;
}
/* -------------------------------------------------------------------------- */
/* Sets sensor's contrast setting to "val" */
static int
sensor_set_contrast ( struct usb_ov511 * ov , unsigned short val )
{
int rc ;
PDEBUG ( 3 , " %d " , val ) ;
if ( ov - > stop_during_set )
if ( ov51x_stop ( ov ) < 0 )
return - EIO ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV6620 :
{
rc = i2c_w ( ov , OV7610_REG_CNT , val > > 8 ) ;
if ( rc < 0 )
goto out ;
break ;
}
case SEN_OV6630 :
{
rc = i2c_w_mask ( ov , OV7610_REG_CNT , val > > 12 , 0x0f ) ;
if ( rc < 0 )
goto out ;
break ;
}
case SEN_OV7620 :
{
unsigned char ctab [ ] = {
0x01 , 0x05 , 0x09 , 0x11 , 0x15 , 0x35 , 0x37 , 0x57 ,
0x5b , 0xa5 , 0xa7 , 0xc7 , 0xc9 , 0xcf , 0xef , 0xff
} ;
/* Use Y gamma control instead. Bit 0 enables it. */
rc = i2c_w ( ov , 0x64 , ctab [ val > > 12 ] ) ;
if ( rc < 0 )
goto out ;
break ;
}
case SEN_SAA7111A :
{
rc = i2c_w ( ov , 0x0b , val > > 9 ) ;
if ( rc < 0 )
goto out ;
break ;
}
default :
{
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
rc = - EPERM ;
goto out ;
}
}
rc = 0 ; /* Success */
ov - > contrast = val ;
out :
if ( ov51x_restart ( ov ) < 0 )
return - EIO ;
return rc ;
}
/* Gets sensor's contrast setting */
static int
sensor_get_contrast ( struct usb_ov511 * ov , unsigned short * val )
{
int rc ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV6620 :
rc = i2c_r ( ov , OV7610_REG_CNT ) ;
if ( rc < 0 )
return rc ;
else
* val = rc < < 8 ;
break ;
case SEN_OV6630 :
rc = i2c_r ( ov , OV7610_REG_CNT ) ;
if ( rc < 0 )
return rc ;
else
* val = rc < < 12 ;
break ;
case SEN_OV7620 :
/* Use Y gamma reg instead. Bit 0 is the enable bit. */
rc = i2c_r ( ov , 0x64 ) ;
if ( rc < 0 )
return rc ;
else
* val = ( rc & 0xfe ) < < 8 ;
break ;
case SEN_SAA7111A :
* val = ov - > contrast ;
break ;
default :
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
return - EPERM ;
}
PDEBUG ( 3 , " %d " , * val ) ;
ov - > contrast = * val ;
return 0 ;
}
/* -------------------------------------------------------------------------- */
/* Sets sensor's brightness setting to "val" */
static int
sensor_set_brightness ( struct usb_ov511 * ov , unsigned short val )
{
int rc ;
PDEBUG ( 4 , " %d " , val ) ;
if ( ov - > stop_during_set )
if ( ov51x_stop ( ov ) < 0 )
return - EIO ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV76BE :
case SEN_OV6620 :
case SEN_OV6630 :
rc = i2c_w ( ov , OV7610_REG_BRT , val > > 8 ) ;
if ( rc < 0 )
goto out ;
break ;
case SEN_OV7620 :
/* 7620 doesn't like manual changes when in auto mode */
if ( ! ov - > auto_brt ) {
rc = i2c_w ( ov , OV7610_REG_BRT , val > > 8 ) ;
if ( rc < 0 )
goto out ;
}
break ;
case SEN_SAA7111A :
rc = i2c_w ( ov , 0x0a , val > > 8 ) ;
if ( rc < 0 )
goto out ;
break ;
default :
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
rc = - EPERM ;
goto out ;
}
rc = 0 ; /* Success */
ov - > brightness = val ;
out :
if ( ov51x_restart ( ov ) < 0 )
return - EIO ;
return rc ;
}
/* Gets sensor's brightness setting */
static int
sensor_get_brightness ( struct usb_ov511 * ov , unsigned short * val )
{
int rc ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV76BE :
case SEN_OV7620 :
case SEN_OV6620 :
case SEN_OV6630 :
rc = i2c_r ( ov , OV7610_REG_BRT ) ;
if ( rc < 0 )
return rc ;
else
* val = rc < < 8 ;
break ;
case SEN_SAA7111A :
* val = ov - > brightness ;
break ;
default :
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
return - EPERM ;
}
PDEBUG ( 3 , " %d " , * val ) ;
ov - > brightness = * val ;
return 0 ;
}
/* -------------------------------------------------------------------------- */
/* Sets sensor's saturation (color intensity) setting to "val" */
static int
sensor_set_saturation ( struct usb_ov511 * ov , unsigned short val )
{
int rc ;
PDEBUG ( 3 , " %d " , val ) ;
if ( ov - > stop_during_set )
if ( ov51x_stop ( ov ) < 0 )
return - EIO ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV76BE :
case SEN_OV6620 :
case SEN_OV6630 :
rc = i2c_w ( ov , OV7610_REG_SAT , val > > 8 ) ;
if ( rc < 0 )
goto out ;
break ;
case SEN_OV7620 :
// /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
// rc = ov_i2c_write(ov->dev, 0x62, (val >> 9) & 0x7e);
// if (rc < 0)
// goto out;
rc = i2c_w ( ov , OV7610_REG_SAT , val > > 8 ) ;
if ( rc < 0 )
goto out ;
break ;
case SEN_SAA7111A :
rc = i2c_w ( ov , 0x0c , val > > 9 ) ;
if ( rc < 0 )
goto out ;
break ;
default :
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
rc = - EPERM ;
goto out ;
}
rc = 0 ; /* Success */
ov - > colour = val ;
out :
if ( ov51x_restart ( ov ) < 0 )
return - EIO ;
return rc ;
}
/* Gets sensor's saturation (color intensity) setting */
static int
sensor_get_saturation ( struct usb_ov511 * ov , unsigned short * val )
{
int rc ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV76BE :
case SEN_OV6620 :
case SEN_OV6630 :
rc = i2c_r ( ov , OV7610_REG_SAT ) ;
if ( rc < 0 )
return rc ;
else
* val = rc < < 8 ;
break ;
case SEN_OV7620 :
// /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */
// rc = i2c_r(ov, 0x62);
// if (rc < 0)
// return rc;
// else
// *val = (rc & 0x7e) << 9;
rc = i2c_r ( ov , OV7610_REG_SAT ) ;
if ( rc < 0 )
return rc ;
else
* val = rc < < 8 ;
break ;
case SEN_SAA7111A :
* val = ov - > colour ;
break ;
default :
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
return - EPERM ;
}
PDEBUG ( 3 , " %d " , * val ) ;
ov - > colour = * val ;
return 0 ;
}
/* -------------------------------------------------------------------------- */
/* Sets sensor's hue (red/blue balance) setting to "val" */
static int
sensor_set_hue ( struct usb_ov511 * ov , unsigned short val )
{
int rc ;
PDEBUG ( 3 , " %d " , val ) ;
if ( ov - > stop_during_set )
if ( ov51x_stop ( ov ) < 0 )
return - EIO ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV6620 :
case SEN_OV6630 :
rc = i2c_w ( ov , OV7610_REG_RED , 0xFF - ( val > > 8 ) ) ;
if ( rc < 0 )
goto out ;
rc = i2c_w ( ov , OV7610_REG_BLUE , val > > 8 ) ;
if ( rc < 0 )
goto out ;
break ;
case SEN_OV7620 :
// Hue control is causing problems. I will enable it once it's fixed.
#if 0
rc = i2c_w ( ov , 0x7a , ( unsigned char ) ( val > > 8 ) + 0xb ) ;
if ( rc < 0 )
goto out ;
rc = i2c_w ( ov , 0x79 , ( unsigned char ) ( val > > 8 ) + 0xb ) ;
if ( rc < 0 )
goto out ;
# endif
break ;
case SEN_SAA7111A :
rc = i2c_w ( ov , 0x0d , ( val + 32768 ) > > 8 ) ;
if ( rc < 0 )
goto out ;
break ;
default :
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
rc = - EPERM ;
goto out ;
}
rc = 0 ; /* Success */
ov - > hue = val ;
out :
if ( ov51x_restart ( ov ) < 0 )
return - EIO ;
return rc ;
}
/* Gets sensor's hue (red/blue balance) setting */
static int
sensor_get_hue ( struct usb_ov511 * ov , unsigned short * val )
{
int rc ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV6620 :
case SEN_OV6630 :
rc = i2c_r ( ov , OV7610_REG_BLUE ) ;
if ( rc < 0 )
return rc ;
else
* val = rc < < 8 ;
break ;
case SEN_OV7620 :
rc = i2c_r ( ov , 0x7a ) ;
if ( rc < 0 )
return rc ;
else
* val = rc < < 8 ;
break ;
case SEN_SAA7111A :
* val = ov - > hue ;
break ;
default :
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
return - EPERM ;
}
PDEBUG ( 3 , " %d " , * val ) ;
ov - > hue = * val ;
return 0 ;
}
/* -------------------------------------------------------------------------- */
static int
sensor_set_picture ( struct usb_ov511 * ov , struct video_picture * p )
{
int rc ;
PDEBUG ( 4 , " sensor_set_picture " ) ;
ov - > whiteness = p - > whiteness ;
/* Don't return error if a setting is unsupported, or rest of settings
* will not be performed */
rc = sensor_set_contrast ( ov , p - > contrast ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_set_brightness ( ov , p - > brightness ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_set_saturation ( ov , p - > colour ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_set_hue ( ov , p - > hue ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
return 0 ;
}
static int
sensor_get_picture ( struct usb_ov511 * ov , struct video_picture * p )
{
int rc ;
PDEBUG ( 4 , " sensor_get_picture " ) ;
/* Don't return error if a setting is unsupported, or rest of settings
* will not be performed */
rc = sensor_get_contrast ( ov , & ( p - > contrast ) ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_get_brightness ( ov , & ( p - > brightness ) ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_get_saturation ( ov , & ( p - > colour ) ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_get_hue ( ov , & ( p - > hue ) ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
p - > whiteness = 105 < < 8 ;
return 0 ;
}
#if 0
// FIXME: Exposure range is only 0x00-0x7f in interlace mode
/* Sets current exposure for sensor. This only has an effect if auto-exposure
* is off */
static inline int
sensor_set_exposure ( struct usb_ov511 * ov , unsigned char val )
{
int rc ;
PDEBUG ( 3 , " %d " , val ) ;
if ( ov - > stop_during_set )
if ( ov51x_stop ( ov ) < 0 )
return - EIO ;
switch ( ov - > sensor ) {
case SEN_OV6620 :
case SEN_OV6630 :
case SEN_OV7610 :
case SEN_OV7620 :
case SEN_OV76BE :
case SEN_OV8600 :
rc = i2c_w ( ov , 0x10 , val ) ;
if ( rc < 0 )
goto out ;
break ;
case SEN_KS0127 :
case SEN_KS0127B :
case SEN_SAA7111A :
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
return - EPERM ;
default :
err ( " Sensor not supported for set_exposure " ) ;
return - EINVAL ;
}
rc = 0 ; /* Success */
ov - > exposure = val ;
out :
if ( ov51x_restart ( ov ) < 0 )
return - EIO ;
return rc ;
}
# endif
/* Gets current exposure level from sensor, regardless of whether it is under
* manual control . */
static int
sensor_get_exposure ( struct usb_ov511 * ov , unsigned char * val )
{
int rc ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV6620 :
case SEN_OV6630 :
case SEN_OV7620 :
case SEN_OV76BE :
case SEN_OV8600 :
rc = i2c_r ( ov , 0x10 ) ;
if ( rc < 0 )
return rc ;
else
* val = rc ;
break ;
case SEN_KS0127 :
case SEN_KS0127B :
case SEN_SAA7111A :
val = NULL ;
PDEBUG ( 3 , " Unsupported with this sensor " ) ;
return - EPERM ;
default :
err ( " Sensor not supported for get_exposure " ) ;
return - EINVAL ;
}
PDEBUG ( 3 , " %d " , * val ) ;
ov - > exposure = * val ;
return 0 ;
}
/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
static void
ov51x_led_control ( struct usb_ov511 * ov , int enable )
{
PDEBUG ( 4 , " (%s) " , enable ? " turn on " : " turn off " ) ;
if ( ov - > bridge = = BRG_OV511PLUS )
reg_w ( ov , R511_SYS_LED_CTL , enable ? 1 : 0 ) ;
else if ( ov - > bclass = = BCL_OV518 )
reg_w_mask ( ov , R518_GPIO_OUT , enable ? 0x02 : 0x00 , 0x02 ) ;
return ;
}
/* Matches the sensor's internal frame rate to the lighting frequency.
* Valid frequencies are :
* 50 - 50 Hz , for European and Asian lighting
* 60 - 60 Hz , for American lighting
*
* Tested with : OV7610 , OV7620 , OV76BE , OV6620
* Unsupported : KS0127 , KS0127B , SAA7111A
* Returns : 0 for success
*/
static int
sensor_set_light_freq ( struct usb_ov511 * ov , int freq )
{
int sixty ;
PDEBUG ( 4 , " %d Hz " , freq ) ;
if ( freq = = 60 )
sixty = 1 ;
else if ( freq = = 50 )
sixty = 0 ;
else {
err ( " Invalid light freq (%d Hz) " , freq ) ;
return - EINVAL ;
}
switch ( ov - > sensor ) {
case SEN_OV7610 :
i2c_w_mask ( ov , 0x2a , sixty ? 0x00 : 0x80 , 0x80 ) ;
i2c_w ( ov , 0x2b , sixty ? 0x00 : 0xac ) ;
i2c_w_mask ( ov , 0x13 , 0x10 , 0x10 ) ;
i2c_w_mask ( ov , 0x13 , 0x00 , 0x10 ) ;
break ;
case SEN_OV7620 :
case SEN_OV76BE :
case SEN_OV8600 :
i2c_w_mask ( ov , 0x2a , sixty ? 0x00 : 0x80 , 0x80 ) ;
i2c_w ( ov , 0x2b , sixty ? 0x00 : 0xac ) ;
i2c_w_mask ( ov , 0x76 , 0x01 , 0x01 ) ;
break ;
case SEN_OV6620 :
case SEN_OV6630 :
i2c_w ( ov , 0x2b , sixty ? 0xa8 : 0x28 ) ;
i2c_w ( ov , 0x2a , sixty ? 0x84 : 0xa4 ) ;
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_light_freq " ) ;
return - EINVAL ;
}
ov - > lightfreq = freq ;
return 0 ;
}
/* If enable is true, turn on the sensor's banding filter, otherwise turn it
* off . This filter tries to reduce the pattern of horizontal light / dark bands
* caused by some ( usually fluorescent ) lighting . The light frequency must be
* set either before or after enabling it with ov51x_set_light_freq ( ) .
*
* Tested with : OV7610 , OV7620 , OV76BE , OV6620 .
* Unsupported : KS0127 , KS0127B , SAA7111A
* Returns : 0 for success
*/
static int
sensor_set_banding_filter ( struct usb_ov511 * ov , int enable )
{
int rc ;
PDEBUG ( 4 , " (%s) " , enable ? " turn on " : " turn off " ) ;
if ( ov - > sensor = = SEN_KS0127 | | ov - > sensor = = SEN_KS0127B
| | ov - > sensor = = SEN_SAA7111A ) {
PDEBUG ( 5 , " Unsupported with this sensor " ) ;
return - EPERM ;
}
rc = i2c_w_mask ( ov , 0x2d , enable ? 0x04 : 0x00 , 0x04 ) ;
if ( rc < 0 )
return rc ;
ov - > bandfilt = enable ;
return 0 ;
}
/* If enable is true, turn on the sensor's auto brightness control, otherwise
* turn it off .
*
* Unsupported : KS0127 , KS0127B , SAA7111A
* Returns : 0 for success
*/
static int
sensor_set_auto_brightness ( struct usb_ov511 * ov , int enable )
{
int rc ;
PDEBUG ( 4 , " (%s) " , enable ? " turn on " : " turn off " ) ;
if ( ov - > sensor = = SEN_KS0127 | | ov - > sensor = = SEN_KS0127B
| | ov - > sensor = = SEN_SAA7111A ) {
PDEBUG ( 5 , " Unsupported with this sensor " ) ;
return - EPERM ;
}
rc = i2c_w_mask ( ov , 0x2d , enable ? 0x10 : 0x00 , 0x10 ) ;
if ( rc < 0 )
return rc ;
ov - > auto_brt = enable ;
return 0 ;
}
/* If enable is true, turn on the sensor's auto exposure control, otherwise
* turn it off .
*
* Unsupported : KS0127 , KS0127B , SAA7111A
* Returns : 0 for success
*/
static int
sensor_set_auto_exposure ( struct usb_ov511 * ov , int enable )
{
PDEBUG ( 4 , " (%s) " , enable ? " turn on " : " turn off " ) ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
i2c_w_mask ( ov , 0x29 , enable ? 0x00 : 0x80 , 0x80 ) ;
break ;
case SEN_OV6620 :
case SEN_OV7620 :
case SEN_OV76BE :
case SEN_OV8600 :
i2c_w_mask ( ov , 0x13 , enable ? 0x01 : 0x00 , 0x01 ) ;
break ;
case SEN_OV6630 :
i2c_w_mask ( ov , 0x28 , enable ? 0x00 : 0x10 , 0x10 ) ;
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_auto_exposure " ) ;
return - EINVAL ;
}
ov - > auto_exp = enable ;
return 0 ;
}
/* Modifies the sensor's exposure algorithm to allow proper exposure of objects
* that are illuminated from behind .
*
* Tested with : OV6620 , OV7620
* Unsupported : OV7610 , OV76BE , KS0127 , KS0127B , SAA7111A
* Returns : 0 for success
*/
static int
sensor_set_backlight ( struct usb_ov511 * ov , int enable )
{
PDEBUG ( 4 , " (%s) " , enable ? " turn on " : " turn off " ) ;
switch ( ov - > sensor ) {
case SEN_OV7620 :
case SEN_OV8600 :
i2c_w_mask ( ov , 0x68 , enable ? 0xe0 : 0xc0 , 0xe0 ) ;
i2c_w_mask ( ov , 0x29 , enable ? 0x08 : 0x00 , 0x08 ) ;
i2c_w_mask ( ov , 0x28 , enable ? 0x02 : 0x00 , 0x02 ) ;
break ;
case SEN_OV6620 :
i2c_w_mask ( ov , 0x4e , enable ? 0xe0 : 0xc0 , 0xe0 ) ;
i2c_w_mask ( ov , 0x29 , enable ? 0x08 : 0x00 , 0x08 ) ;
i2c_w_mask ( ov , 0x0e , enable ? 0x80 : 0x00 , 0x80 ) ;
break ;
case SEN_OV6630 :
i2c_w_mask ( ov , 0x4e , enable ? 0x80 : 0x60 , 0xe0 ) ;
i2c_w_mask ( ov , 0x29 , enable ? 0x08 : 0x00 , 0x08 ) ;
i2c_w_mask ( ov , 0x28 , enable ? 0x02 : 0x00 , 0x02 ) ;
break ;
case SEN_OV7610 :
case SEN_OV76BE :
case SEN_KS0127 :
case SEN_KS0127B :
case SEN_SAA7111A :
PDEBUG ( 5 , " Unsupported with this sensor " ) ;
return - EPERM ;
default :
err ( " Sensor not supported for set_backlight " ) ;
return - EINVAL ;
}
ov - > backlight = enable ;
return 0 ;
}
static 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;
* planar or not ) , or zero for unsupported format .
*/
static inline int
get_depth ( int palette )
{
switch ( palette ) {
case VIDEO_PALETTE_GREY : return 8 ;
case VIDEO_PALETTE_YUV420 : return 12 ;
case VIDEO_PALETTE_YUV420P : return 12 ; /* Planar */
default : return 0 ; /* Invalid format */
}
}
/* Bytes per frame. Used by read(). Return of 0 indicates error */
static inline long int
get_frame_length ( struct ov511_frame * frame )
{
if ( ! frame )
return 0 ;
else
return ( ( frame - > width * frame - > height
* get_depth ( frame - > format ) ) > > 3 ) ;
}
static int
mode_init_ov_sensor_regs ( struct usb_ov511 * ov , int width , int height ,
int mode , int sub_flag , int qvga )
{
int clock ;
/******** Mode (VGA/QVGA) and sensor specific regs ********/
switch ( ov - > sensor ) {
case SEN_OV7610 :
i2c_w ( ov , 0x14 , qvga ? 0x24 : 0x04 ) ;
// FIXME: Does this improve the image quality or frame rate?
#if 0
i2c_w_mask ( ov , 0x28 , qvga ? 0x00 : 0x20 , 0x20 ) ;
i2c_w ( ov , 0x24 , 0x10 ) ;
i2c_w ( ov , 0x25 , qvga ? 0x40 : 0x8a ) ;
i2c_w ( ov , 0x2f , qvga ? 0x30 : 0xb0 ) ;
i2c_w ( ov , 0x35 , qvga ? 0x1c : 0x9c ) ;
# endif
break ;
case SEN_OV7620 :
// i2c_w(ov, 0x2b, 0x00);
i2c_w ( ov , 0x14 , qvga ? 0xa4 : 0x84 ) ;
i2c_w_mask ( ov , 0x28 , qvga ? 0x00 : 0x20 , 0x20 ) ;
i2c_w ( ov , 0x24 , qvga ? 0x20 : 0x3a ) ;
i2c_w ( ov , 0x25 , qvga ? 0x30 : 0x60 ) ;
i2c_w_mask ( ov , 0x2d , qvga ? 0x40 : 0x00 , 0x40 ) ;
i2c_w_mask ( ov , 0x67 , qvga ? 0xf0 : 0x90 , 0xf0 ) ;
i2c_w_mask ( ov , 0x74 , qvga ? 0x20 : 0x00 , 0x20 ) ;
break ;
case SEN_OV76BE :
// i2c_w(ov, 0x2b, 0x00);
i2c_w ( ov , 0x14 , qvga ? 0xa4 : 0x84 ) ;
// FIXME: Enable this once 7620AE uses 7620 initial settings
#if 0
i2c_w_mask ( ov , 0x28 , qvga ? 0x00 : 0x20 , 0x20 ) ;
i2c_w ( ov , 0x24 , qvga ? 0x20 : 0x3a ) ;
i2c_w ( ov , 0x25 , qvga ? 0x30 : 0x60 ) ;
i2c_w_mask ( ov , 0x2d , qvga ? 0x40 : 0x00 , 0x40 ) ;
i2c_w_mask ( ov , 0x67 , qvga ? 0xb0 : 0x90 , 0xf0 ) ;
i2c_w_mask ( ov , 0x74 , qvga ? 0x20 : 0x00 , 0x20 ) ;
# endif
break ;
case SEN_OV6620 :
i2c_w ( ov , 0x14 , qvga ? 0x24 : 0x04 ) ;
break ;
case SEN_OV6630 :
i2c_w ( ov , 0x14 , qvga ? 0xa0 : 0x80 ) ;
break ;
default :
err ( " Invalid sensor " ) ;
return - EINVAL ;
}
/******** Palette-specific regs ********/
if ( mode = = VIDEO_PALETTE_GREY ) {
if ( ov - > sensor = = SEN_OV7610 | | ov - > sensor = = SEN_OV76BE ) {
/* these aren't valid on the OV6620/OV7620/6630? */
i2c_w_mask ( ov , 0x0e , 0x40 , 0x40 ) ;
}
if ( ov - > sensor = = SEN_OV6630 & & ov - > bridge = = BRG_OV518
& & ov518_color ) {
i2c_w_mask ( ov , 0x12 , 0x00 , 0x10 ) ;
i2c_w_mask ( ov , 0x13 , 0x00 , 0x20 ) ;
} else {
i2c_w_mask ( ov , 0x13 , 0x20 , 0x20 ) ;
}
} else {
if ( ov - > sensor = = SEN_OV7610 | | ov - > sensor = = SEN_OV76BE ) {
/* not valid on the OV6620/OV7620/6630? */
i2c_w_mask ( ov , 0x0e , 0x00 , 0x40 ) ;
}
/* 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 ( ov - > sensor = = SEN_OV6630 & & ov - > bridge = = BRG_OV518
& & ov518_color ) {
i2c_w_mask ( ov , 0x12 , 0x10 , 0x10 ) ;
i2c_w_mask ( ov , 0x13 , 0x20 , 0x20 ) ;
} else {
i2c_w_mask ( ov , 0x13 , 0x00 , 0x20 ) ;
}
}
/******** Clock programming ********/
/* The OV6620 needs special handling. This prevents the
* severe banding that normally occurs */
if ( ov - > sensor = = SEN_OV6620 | | ov - > sensor = = SEN_OV6630 )
{
/* Clock down */
i2c_w ( ov , 0x2a , 0x04 ) ;
if ( ov - > compress ) {
// clock = 0; /* This ensures the highest frame rate */
clock = 3 ;
} else if ( clockdiv = = - 1 ) { /* If user didn't override it */
clock = 3 ; /* Gives better exposure time */
} else {
clock = clockdiv ;
}
PDEBUG ( 4 , " Setting clock divisor to %d " , clock ) ;
i2c_w ( ov , 0x11 , clock ) ;
i2c_w ( ov , 0x2a , 0x84 ) ;
/* This next setting is critical. It seems to improve
* the gain or the contrast . The " reserved " bits seem
* to have some effect in this case . */
i2c_w ( ov , 0x2d , 0x85 ) ;
}
else
{
if ( ov - > compress ) {
clock = 1 ; /* This ensures the highest frame rate */
} else if ( clockdiv = = - 1 ) { /* If user didn't override it */
/* Calculate and set the clock divisor */
clock = ( ( sub_flag ? ov - > subw * ov - > subh
: width * height )
* ( mode = = VIDEO_PALETTE_GREY ? 2 : 3 ) / 2 )
/ 66000 ;
} else {
clock = clockdiv ;
}
PDEBUG ( 4 , " Setting clock divisor to %d " , clock ) ;
i2c_w ( ov , 0x11 , clock ) ;
}
/******** Special Features ********/
if ( framedrop > = 0 )
i2c_w ( ov , 0x16 , framedrop ) ;
/* Test Pattern */
i2c_w_mask ( ov , 0x12 , ( testpat ? 0x02 : 0x00 ) , 0x02 ) ;
/* Enable auto white balance */
i2c_w_mask ( ov , 0x12 , 0x04 , 0x04 ) ;
// This will go away as soon as ov51x_mode_init_sensor_regs()
// is fully tested.
/* 7620/6620/6630? don't have register 0x35, so play it safe */
if ( ov - > sensor = = SEN_OV7610 | | ov - > sensor = = SEN_OV76BE ) {
if ( width = = 640 & & height = = 480 )
i2c_w ( ov , 0x35 , 0x9e ) ;
else
i2c_w ( ov , 0x35 , 0x1e ) ;
}
return 0 ;
}
static int
set_ov_sensor_window ( struct usb_ov511 * ov , int width , int height , int mode ,
int sub_flag )
{
int ret ;
int hwsbase , hwebase , vwsbase , vwebase , hwsize , vwsize ;
int hoffset , voffset , hwscale = 0 , vwscale = 0 ;
/* The different sensor ICs handle setting up of window differently.
* IF YOU SET IT WRONG , YOU WILL GET ALL ZERO ISOC DATA FROM OV51x ! ! ! */
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV76BE :
hwsbase = 0x38 ;
hwebase = 0x3a ;
vwsbase = vwebase = 0x05 ;
break ;
case SEN_OV6620 :
case SEN_OV6630 :
hwsbase = 0x38 ;
hwebase = 0x3a ;
vwsbase = 0x05 ;
vwebase = 0x06 ;
break ;
case SEN_OV7620 :
hwsbase = 0x2f ; /* From 7620.SET (spec is wrong) */
hwebase = 0x2f ;
vwsbase = vwebase = 0x05 ;
break ;
default :
err ( " Invalid sensor " ) ;
return - EINVAL ;
}
if ( ov - > sensor = = SEN_OV6620 | | ov - > sensor = = SEN_OV6630 ) {
/* 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 ,
mode , sub_flag , 0 ) ;
if ( ret < 0 )
return ret ;
hwscale = 1 ;
vwscale = 1 ; /* The datasheet says 0; it's wrong */
hwsize = 352 ;
vwsize = 288 ;
} else if ( width > 176 | | height > 144 ) {
err ( " Illegal dimensions " ) ;
return - EINVAL ;
} else { /* QCIF */
ret = mode_init_ov_sensor_regs ( ov , width , height ,
mode , sub_flag , 1 ) ;
if ( ret < 0 )
return ret ;
hwsize = 176 ;
vwsize = 144 ;
}
} else {
if ( width > 320 & & height > 240 ) { /* VGA */
ret = mode_init_ov_sensor_regs ( ov , width , height ,
mode , sub_flag , 0 ) ;
if ( ret < 0 )
return ret ;
hwscale = 2 ;
vwscale = 1 ;
hwsize = 640 ;
vwsize = 480 ;
} else if ( width > 320 | | height > 240 ) {
err ( " Illegal dimensions " ) ;
return - EINVAL ;
} else { /* QVGA */
ret = mode_init_ov_sensor_regs ( ov , width , height ,
mode , sub_flag , 1 ) ;
if ( ret < 0 )
return ret ;
hwscale = 1 ;
hwsize = 320 ;
vwsize = 240 ;
}
}
/* Center the window */
hoffset = ( ( hwsize - width ) / 2 ) > > hwscale ;
voffset = ( ( vwsize - height ) / 2 ) > > vwscale ;
/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
if ( sub_flag ) {
i2c_w ( ov , 0x17 , hwsbase + ( ov - > subx > > hwscale ) ) ;
i2c_w ( ov , 0x18 , hwebase + ( ( ov - > subx + ov - > subw ) > > hwscale ) ) ;
i2c_w ( ov , 0x19 , vwsbase + ( ov - > suby > > vwscale ) ) ;
i2c_w ( ov , 0x1a , vwebase + ( ( ov - > suby + ov - > subh ) > > vwscale ) ) ;
} else {
i2c_w ( ov , 0x17 , hwsbase + hoffset ) ;
i2c_w ( ov , 0x18 , hwebase + hoffset + ( hwsize > > hwscale ) ) ;
i2c_w ( ov , 0x19 , vwsbase + voffset ) ;
i2c_w ( ov , 0x1a , vwebase + voffset + ( vwsize > > vwscale ) ) ;
}
# ifdef OV511_DEBUG
if ( dump_sensor )
dump_i2c_regs ( ov ) ;
# endif
return 0 ;
}
/* Set up the OV511/OV511+ with the given image parameters.
*
* Do not put any sensor - specific code in here ( including I2C I / O functions )
*/
static int
ov511_mode_init_regs ( struct usb_ov511 * ov ,
int width , int height , int mode , int sub_flag )
{
int hsegs , vsegs ;
if ( sub_flag ) {
width = ov - > subw ;
height = ov - > subh ;
}
PDEBUG ( 3 , " width:%d, height:%d, mode:%d, sub:%d " ,
width , height , mode , sub_flag ) ;
// FIXME: This should be moved to a 7111a-specific function once
// subcapture is dealt with properly
if ( ov - > sensor = = SEN_SAA7111A ) {
if ( width = = 320 & & height = = 240 ) {
/* No need to do anything special */
} else if ( width = = 640 & & height = = 480 ) {
/* Set the OV511 up as 320x480, but keep the
* V4L resolution as 640 x480 */
width = 320 ;
} else {
err ( " SAA7111A only allows 320x240 or 640x480 " ) ;
return - EINVAL ;
}
}
/* Make sure width and height are a multiple of 8 */
if ( width % 8 | | height % 8 ) {
err ( " Invalid size (%d, %d) (mode = %d) " , width , height , mode ) ;
return - EINVAL ;
}
if ( width < ov - > minwidth | | height < ov - > minheight ) {
err ( " Requested dimensions are too small " ) ;
return - EINVAL ;
}
if ( ov51x_stop ( ov ) < 0 )
return - EIO ;
if ( mode = = VIDEO_PALETTE_GREY ) {
reg_w ( ov , R511_CAM_UV_EN , 0x00 ) ;
reg_w ( ov , R511_SNAP_UV_EN , 0x00 ) ;
reg_w ( ov , R511_SNAP_OPTS , 0x01 ) ;
} else {
reg_w ( ov , R511_CAM_UV_EN , 0x01 ) ;
reg_w ( ov , R511_SNAP_UV_EN , 0x01 ) ;
reg_w ( ov , R511_SNAP_OPTS , 0x03 ) ;
}
/* Here I'm assuming that snapshot size == image size.
* I hope that ' s always true . - - claudio
*/
hsegs = ( width > > 3 ) - 1 ;
vsegs = ( height > > 3 ) - 1 ;
reg_w ( ov , R511_CAM_PXCNT , hsegs ) ;
reg_w ( ov , R511_CAM_LNCNT , vsegs ) ;
reg_w ( ov , R511_CAM_PXDIV , 0x00 ) ;
reg_w ( ov , R511_CAM_LNDIV , 0x00 ) ;
/* YUV420, low pass filter on */
reg_w ( ov , R511_CAM_OPTS , 0x03 ) ;
/* Snapshot additions */
reg_w ( ov , R511_SNAP_PXCNT , hsegs ) ;
reg_w ( ov , R511_SNAP_LNCNT , vsegs ) ;
reg_w ( ov , R511_SNAP_PXDIV , 0x00 ) ;
reg_w ( ov , R511_SNAP_LNDIV , 0x00 ) ;
if ( ov - > compress ) {
/* Enable Y and UV quantization and compression */
reg_w ( ov , R511_COMP_EN , 0x07 ) ;
reg_w ( ov , R511_COMP_LUT_EN , 0x03 ) ;
ov51x_reset ( ov , OV511_RESET_OMNICE ) ;
}
if ( ov51x_restart ( ov ) < 0 )
return - EIO ;
return 0 ;
}
/* Sets up the OV518/OV518+ with the given image parameters
*
* OV518 needs a completely different approach , until we can figure out what
* the individual registers do . Also , only 15 FPS is supported now .
*
* Do not put any sensor - specific code in here ( including I2C I / O functions )
*/
static int
ov518_mode_init_regs ( struct usb_ov511 * ov ,
int width , int height , int mode , int sub_flag )
{
int hsegs , vsegs , hi_res ;
if ( sub_flag ) {
width = ov - > subw ;
height = ov - > subh ;
}
PDEBUG ( 3 , " width:%d, height:%d, mode:%d, sub:%d " ,
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 )
return - EIO ;
/******** Set the mode ********/
reg_w ( ov , 0x2b , 0 ) ;
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 ) ;
if ( ov - > bridge = = BRG_OV518 & & ov518_color ) {
/* OV518 needs U and V swapped */
i2c_w_mask ( ov , 0x15 , 0x00 , 0x01 ) ;
if ( mode = = VIDEO_PALETTE_GREY ) {
/* Set 16-bit input format (UV data are ignored) */
reg_w_mask ( ov , 0x20 , 0x00 , 0x08 ) ;
/* Set 8-bit (4:0:0) output format */
reg_w_mask ( ov , 0x28 , 0x00 , 0xf0 ) ;
reg_w_mask ( ov , 0x38 , 0x00 , 0xf0 ) ;
} else {
/* Set 8-bit (YVYU) input format */
reg_w_mask ( ov , 0x20 , 0x08 , 0x08 ) ;
/* Set 12-bit (4:2:0) output format */
reg_w_mask ( ov , 0x28 , 0x80 , 0xf0 ) ;
reg_w_mask ( ov , 0x38 , 0x80 , 0xf0 ) ;
}
} else {
reg_w ( ov , 0x28 , ( mode = = VIDEO_PALETTE_GREY ) ? 0x00 : 0x80 ) ;
reg_w ( ov , 0x38 , ( mode = = VIDEO_PALETTE_GREY ) ? 0x00 : 0x80 ) ;
}
hsegs = width / 16 ;
vsegs = height / 4 ;
reg_w ( ov , 0x29 , hsegs ) ;
reg_w ( ov , 0x2a , vsegs ) ;
reg_w ( ov , 0x39 , hsegs ) ;
reg_w ( ov , 0x3a , vsegs ) ;
/* Windows driver does this here; who knows why */
reg_w ( ov , 0x2f , 0x80 ) ;
/******** Set the framerate (to 15 FPS) ********/
/* Mode independent, but framerate dependent, regs */
reg_w ( ov , 0x51 , 0x02 ) ; /* Clock divider; lower==faster */
reg_w ( ov , 0x22 , 0x18 ) ;
reg_w ( ov , 0x23 , 0xff ) ;
if ( ov - > bridge = = BRG_OV518PLUS )
reg_w ( ov , 0x21 , 0x19 ) ;
else
reg_w ( ov , 0x71 , 0x19 ) ; /* Compression-related? */
// FIXME: Sensor-specific
/* Bit 5 is what matters here. Of course, it is "reserved" */
i2c_w ( ov , 0x54 , 0x23 ) ;
reg_w ( ov , 0x2f , 0x80 ) ;
if ( ov - > bridge = = BRG_OV518PLUS ) {
reg_w ( ov , 0x24 , 0x94 ) ;
reg_w ( ov , 0x25 , 0x90 ) ;
ov518_reg_w32 ( ov , 0xc4 , 400 , 2 ) ; /* 190h */
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 , 0xc6 , 500 , 2 ) ; /* 1f4h */
ov518_reg_w32 ( ov , 0xc7 , 500 , 2 ) ; /* 1f4h */
ov518_reg_w32 ( ov , 0xc8 , 142 , 2 ) ; /* 8eh */
ov518_reg_w32 ( ov , 0xca , 131098 , 3 ) ; /* 2001ah */
ov518_reg_w32 ( ov , 0xcb , 532 , 2 ) ; /* 214h */
ov518_reg_w32 ( ov , 0xcc , 2000 , 2 ) ; /* 7d0h */
ov518_reg_w32 ( ov , 0xcd , 32 , 2 ) ; /* 20h */
ov518_reg_w32 ( ov , 0xce , 608 , 2 ) ; /* 260h */
}
reg_w ( ov , 0x2f , 0x80 ) ;
if ( ov51x_restart ( ov ) < 0 )
return - EIO ;
/* Reset it just for good measure */
if ( ov51x_reset ( ov , OV511_RESET_NOREGS ) < 0 )
return - EIO ;
return 0 ;
}
/* This is a wrapper around the OV511, OV518, and sensor specific functions */
static int
mode_init_regs ( struct usb_ov511 * ov ,
int width , int height , int mode , int sub_flag )
{
int rc = 0 ;
if ( ! ov | | ! ov - > dev )
return - EFAULT ;
if ( ov - > bclass = = BCL_OV518 ) {
rc = ov518_mode_init_regs ( ov , width , height , mode , sub_flag ) ;
} else {
rc = ov511_mode_init_regs ( ov , width , height , mode , sub_flag ) ;
}
if ( FATAL_ERROR ( rc ) )
return rc ;
switch ( ov - > sensor ) {
case SEN_OV7610 :
case SEN_OV7620 :
case SEN_OV76BE :
case SEN_OV8600 :
case SEN_OV6620 :
case SEN_OV6630 :
rc = set_ov_sensor_window ( ov , width , height , mode , sub_flag ) ;
break ;
case SEN_KS0127 :
case SEN_KS0127B :
err ( " KS0127-series decoders not supported yet " ) ;
rc = - EINVAL ;
break ;
case SEN_SAA7111A :
// rc = mode_init_saa_sensor_regs(ov, width, height, mode,
// sub_flag);
PDEBUG ( 1 , " SAA status = 0x%02X " , i2c_r ( ov , 0x1f ) ) ;
break ;
default :
err ( " Unknown sensor " ) ;
rc = - EINVAL ;
}
if ( FATAL_ERROR ( rc ) )
return rc ;
/* Sensor-independent settings */
rc = sensor_set_auto_brightness ( ov , ov - > auto_brt ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_set_auto_exposure ( ov , ov - > auto_exp ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_set_banding_filter ( ov , bandingfilter ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
if ( ov - > lightfreq ) {
rc = sensor_set_light_freq ( ov , lightfreq ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
}
rc = sensor_set_backlight ( ov , ov - > backlight ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
rc = sensor_set_mirror ( ov , ov - > mirror ) ;
if ( FATAL_ERROR ( rc ) )
return rc ;
return 0 ;
}
/* This sets the default image parameters. This is useful for apps that use
* read ( ) and do not set these .
*/
static int
ov51x_set_default_params ( struct usb_ov511 * ov )
{
int i ;
/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
* ( using read ( ) instead ) . */
for ( i = 0 ; i < OV511_NUMFRAMES ; i + + ) {
ov - > frame [ i ] . width = ov - > maxwidth ;
ov - > frame [ i ] . height = ov - > maxheight ;
ov - > frame [ i ] . bytes_read = 0 ;
if ( force_palette )
ov - > frame [ i ] . format = force_palette ;
else
ov - > frame [ i ] . format = VIDEO_PALETTE_YUV420 ;
ov - > frame [ i ] . depth = get_depth ( ov - > frame [ i ] . format ) ;
}
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 ,
ov - > frame [ 0 ] . format , 0 ) < 0 )
return - EINVAL ;
return 0 ;
}
/**********************************************************************
*
* Video decoder stuff
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Set analog input port of decoder */
static int
decoder_set_input ( struct usb_ov511 * ov , int input )
{
PDEBUG ( 4 , " port %d " , input ) ;
switch ( ov - > sensor ) {
case SEN_SAA7111A :
{
/* Select mode */
i2c_w_mask ( ov , 0x02 , input , 0x07 ) ;
/* Bypass chrominance trap for modes 4..7 */
i2c_w_mask ( ov , 0x09 , ( input > 3 ) ? 0x80 : 0x00 , 0x80 ) ;
break ;
}
default :
return - EINVAL ;
}
return 0 ;
}
/* Get ASCII name of video input */
static int
decoder_get_input_name ( struct usb_ov511 * ov , int input , char * name )
{
switch ( ov - > sensor ) {
case SEN_SAA7111A :
{
if ( input < 0 | | input > 7 )
return - EINVAL ;
else if ( input < 4 )
sprintf ( name , " CVBS-%d " , input ) ;
else // if (input < 8)
sprintf ( name , " S-Video-%d " , input - 4 ) ;
break ;
}
default :
sprintf ( name , " %s " , " Camera " ) ;
}
return 0 ;
}
/* Set norm (NTSC, PAL, SECAM, AUTO) */
static int
decoder_set_norm ( struct usb_ov511 * ov , int norm )
{
PDEBUG ( 4 , " %d " , norm ) ;
switch ( ov - > sensor ) {
case SEN_SAA7111A :
{
int reg_8 , reg_e ;
if ( norm = = VIDEO_MODE_NTSC ) {
reg_8 = 0x40 ; /* 60 Hz */
reg_e = 0x00 ; /* NTSC M / PAL BGHI */
} else if ( norm = = VIDEO_MODE_PAL ) {
reg_8 = 0x00 ; /* 50 Hz */
reg_e = 0x00 ; /* NTSC M / PAL BGHI */
} else if ( norm = = VIDEO_MODE_AUTO ) {
reg_8 = 0x80 ; /* Auto field detect */
reg_e = 0x00 ; /* NTSC M / PAL BGHI */
} else if ( norm = = VIDEO_MODE_SECAM ) {
reg_8 = 0x00 ; /* 50 Hz */
reg_e = 0x50 ; /* SECAM / PAL 4.43 */
} else {
return - EINVAL ;
}
i2c_w_mask ( ov , 0x08 , reg_8 , 0xc0 ) ;
i2c_w_mask ( ov , 0x0e , reg_e , 0x70 ) ;
break ;
}
default :
return - EINVAL ;
}
return 0 ;
}
/**********************************************************************
*
* Raw data parsing
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the
* image at pOut is specified by w .
*/
static inline void
make_8x8 ( unsigned char * pIn , unsigned char * pOut , int w )
{
unsigned char * pOut1 = pOut ;
int x , y ;
for ( y = 0 ; y < 8 ; y + + ) {
pOut1 = pOut ;
for ( x = 0 ; x < 8 ; x + + ) {
* pOut1 + + = * pIn + + ;
}
pOut + = w ;
}
}
/*
* For RAW BW ( YUV 4 : 0 : 0 ) images , data show up in 256 byte segments .
* The segments represent 4 squares of 8 x8 pixels as follows :
*
* 0 1 . . . 7 64 65 . . . 71 . . . 192 193 . . . 199
* 8 9 . . . 15 72 73 . . . 79 200 201 . . . 207
* . . . . . . . . .
* 56 57 . . . 63 120 121 . . . 127 248 249 . . . 255
*
*/
static void
yuv400raw_to_yuv400p ( struct ov511_frame * frame ,
unsigned char * pIn0 , unsigned char * pOut0 )
{
int x , y ;
unsigned char * pIn , * pOut , * pOutLine ;
/* Copy Y */
pIn = pIn0 ;
pOutLine = pOut0 ;
for ( y = 0 ; y < frame - > rawheight - 1 ; y + = 8 ) {
pOut = pOutLine ;
for ( x = 0 ; x < frame - > rawwidth - 1 ; x + = 8 ) {
make_8x8 ( pIn , pOut , frame - > rawwidth ) ;
pIn + = 64 ;
pOut + = 8 ;
}
pOutLine + = 8 * frame - > rawwidth ;
}
}
/*
* For YUV 4 : 2 : 0 images , the data show up in 384 byte segments .
* The first 64 bytes of each segment are U , the next 64 are V . The U and
* V are arranged as follows :
*
* 0 1 . . . 7
* 8 9 . . . 15
* . . .
* 56 57 . . . 63
*
* U and V are shipped at half resolution ( 1 U , V sample - > one 2 x2 block ) .
*
* The next 256 bytes are full resolution Y data and represent 4 squares
* of 8 x8 pixels as follows :
*
* 0 1 . . . 7 64 65 . . . 71 . . . 192 193 . . . 199
* 8 9 . . . 15 72 73 . . . 79 200 201 . . . 207
* . . . . . . . . .
* 56 57 . . . 63 120 121 . . . 127 . . . 248 249 . . . 255
*
* Note that the U and V data in one segment represent a 16 x 16 pixel
* area , but the Y data represent a 32 x 8 pixel area . If the width is not an
* even multiple of 32 , the extra 8 x8 blocks within a 32 x8 block belong to the
* next horizontal stripe .
*
* If dumppix module param is set , _parse_data just dumps the incoming segments ,
* verbatim , in order , into the frame . When used with vidcat - f ppm - s 640 x480
* this puts the data on the standard output and can be analyzed with the
* parseppm . c utility I wrote . That ' s a much faster way for figuring out how
* these data are scrambled .
*/
/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
*
* FIXME : Currently only handles width and height that are multiples of 16
*/
static void
yuv420raw_to_yuv420p ( struct ov511_frame * frame ,
unsigned char * pIn0 , unsigned char * pOut0 )
{
int k , x , y ;
unsigned char * pIn , * pOut , * pOutLine ;
const unsigned int a = frame - > rawwidth * frame - > rawheight ;
const unsigned int w = frame - > rawwidth / 2 ;
/* Copy U and V */
pIn = pIn0 ;
pOutLine = pOut0 + a ;
for ( y = 0 ; y < frame - > rawheight - 1 ; y + = 16 ) {
pOut = pOutLine ;
for ( x = 0 ; x < frame - > rawwidth - 1 ; x + = 16 ) {
make_8x8 ( pIn , pOut , w ) ;
make_8x8 ( pIn + 64 , pOut + a / 4 , w ) ;
pIn + = 384 ;
pOut + = 8 ;
}
pOutLine + = 8 * w ;
}
/* Copy Y */
pIn = pIn0 + 128 ;
pOutLine = pOut0 ;
k = 0 ;
for ( y = 0 ; y < frame - > rawheight - 1 ; y + = 8 ) {
pOut = pOutLine ;
for ( x = 0 ; x < frame - > rawwidth - 1 ; x + = 8 ) {
make_8x8 ( pIn , pOut , frame - > rawwidth ) ;
pIn + = 64 ;
pOut + = 8 ;
if ( ( + + k ) > 3 ) {
k = 0 ;
pIn + = 128 ;
}
}
pOutLine + = 8 * frame - > rawwidth ;
}
}
/**********************************************************************
*
* Decompression
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Chooses a decompression module, locks it, and sets ov->decomp_ops
* accordingly . Returns - ENXIO if decompressor is not available , otherwise
* returns 0 if no other error .
*/
static int
request_decompressor ( struct usb_ov511 * ov )
{
if ( ! ov )
return - ENODEV ;
if ( ov - > decomp_ops ) {
err ( " ERROR: Decompressor already requested! " ) ;
return - EINVAL ;
}
lock_kernel ( ) ;
/* Try to get MMX, and fall back on no-MMX if necessary */
if ( ov - > bclass = = BCL_OV511 ) {
if ( ov511_mmx_decomp_ops ) {
PDEBUG ( 3 , " Using OV511 MMX decompressor " ) ;
ov - > decomp_ops = ov511_mmx_decomp_ops ;
} else if ( ov511_decomp_ops ) {
PDEBUG ( 3 , " Using OV511 decompressor " ) ;
ov - > decomp_ops = ov511_decomp_ops ;
} else {
err ( " No decompressor available " ) ;
}
} else if ( ov - > bclass = = BCL_OV518 ) {
if ( ov518_mmx_decomp_ops ) {
PDEBUG ( 3 , " Using OV518 MMX decompressor " ) ;
ov - > decomp_ops = ov518_mmx_decomp_ops ;
} else if ( ov518_decomp_ops ) {
PDEBUG ( 3 , " Using OV518 decompressor " ) ;
ov - > decomp_ops = ov518_decomp_ops ;
} else {
err ( " No decompressor available " ) ;
}
} else {
err ( " Unknown bridge " ) ;
}
if ( ! ov - > decomp_ops )
goto nosys ;
if ( ! ov - > decomp_ops - > owner ) {
ov - > decomp_ops = NULL ;
goto nosys ;
}
if ( ! try_module_get ( ov - > decomp_ops - > owner ) )
goto nosys ;
unlock_kernel ( ) ;
return 0 ;
nosys :
unlock_kernel ( ) ;
return - ENOSYS ;
}
/* Unlocks decompression module and nulls ov->decomp_ops. Safe to call even
* if ov - > decomp_ops is NULL .
*/
static void
release_decompressor ( struct usb_ov511 * ov )
{
int released = 0 ; /* Did we actually do anything? */
if ( ! ov )
return ;
lock_kernel ( ) ;
if ( ov - > decomp_ops ) {
module_put ( ov - > decomp_ops - > owner ) ;
released = 1 ;
}
ov - > decomp_ops = NULL ;
unlock_kernel ( ) ;
if ( released )
PDEBUG ( 3 , " Decompressor released " ) ;
}
static void
decompress ( struct usb_ov511 * ov , struct ov511_frame * frame ,
unsigned char * pIn0 , unsigned char * pOut0 )
{
if ( ! ov - > decomp_ops )
if ( request_decompressor ( ov ) )
return ;
PDEBUG ( 4 , " Decompressing %d bytes " , frame - > bytes_recvd ) ;
if ( frame - > format = = VIDEO_PALETTE_GREY
& & ov - > decomp_ops - > decomp_400 ) {
int ret = ov - > decomp_ops - > decomp_400 (
pIn0 ,
pOut0 ,
frame - > compbuf ,
frame - > rawwidth ,
frame - > rawheight ,
frame - > bytes_recvd ) ;
PDEBUG ( 4 , " DEBUG: decomp_400 returned %d " , ret ) ;
} else if ( frame - > format ! = VIDEO_PALETTE_GREY
& & ov - > decomp_ops - > decomp_420 ) {
int ret = ov - > decomp_ops - > decomp_420 (
pIn0 ,
pOut0 ,
frame - > compbuf ,
frame - > rawwidth ,
frame - > rawheight ,
frame - > bytes_recvd ) ;
PDEBUG ( 4 , " DEBUG: decomp_420 returned %d " , ret ) ;
} else {
err ( " Decompressor does not support this format " ) ;
}
}
/**********************************************************************
*
* Format conversion
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Fuses even and odd fields together, and doubles width.
* 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
*/
static void
deinterlace ( struct ov511_frame * frame , int rawformat ,
unsigned char * pIn0 , unsigned char * pOut0 )
{
const int fieldheight = frame - > rawheight / 2 ;
const int fieldpix = fieldheight * frame - > rawwidth ;
const int w = frame - > width ;
int x , y ;
unsigned char * pInEven , * pInOdd , * pOut ;
PDEBUG ( 5 , " fieldheight=%d " , fieldheight ) ;
if ( frame - > rawheight ! = frame - > height ) {
err ( " invalid height " ) ;
return ;
}
if ( ( frame - > rawwidth * 2 ) ! = frame - > width ) {
err ( " invalid width " ) ;
return ;
}
/* Y */
pInOdd = pIn0 ;
pInEven = pInOdd + fieldpix ;
pOut = pOut0 ;
for ( y = 0 ; y < fieldheight ; y + + ) {
for ( x = 0 ; x < frame - > rawwidth ; x + + ) {
* pOut = * pInEven ;
* ( pOut + 1 ) = * pInEven + + ;
* ( pOut + w ) = * pInOdd ;
* ( pOut + w + 1 ) = * pInOdd + + ;
pOut + = 2 ;
}
pOut + = w ;
}
if ( rawformat = = RAWFMT_YUV420 ) {
/* U */
pInOdd = pIn0 + fieldpix * 2 ;
pInEven = pInOdd + fieldpix / 4 ;
for ( y = 0 ; y < fieldheight / 2 ; y + + ) {
for ( x = 0 ; x < frame - > rawwidth / 2 ; x + + ) {
* pOut = * pInEven ;
* ( pOut + 1 ) = * pInEven + + ;
* ( pOut + w / 2 ) = * pInOdd ;
* ( pOut + w / 2 + 1 ) = * pInOdd + + ;
pOut + = 2 ;
}
pOut + = w / 2 ;
}
/* V */
pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2 ;
pInEven = pInOdd + fieldpix / 4 ;
for ( y = 0 ; y < fieldheight / 2 ; y + + ) {
for ( x = 0 ; x < frame - > rawwidth / 2 ; x + + ) {
* pOut = * pInEven ;
* ( pOut + 1 ) = * pInEven + + ;
* ( pOut + w / 2 ) = * pInOdd ;
* ( pOut + w / 2 + 1 ) = * pInOdd + + ;
pOut + = 2 ;
}
pOut + = w / 2 ;
}
}
}
static void
ov51x_postprocess_grey ( struct usb_ov511 * ov , struct ov511_frame * frame )
{
/* Deinterlace frame, if necessary */
if ( ov - > sensor = = SEN_SAA7111A & & frame - > rawheight > = 480 ) {
if ( frame - > compressed )
decompress ( ov , frame , frame - > rawdata ,
frame - > tempdata ) ;
else
yuv400raw_to_yuv400p ( frame , frame - > rawdata ,
frame - > tempdata ) ;
deinterlace ( frame , RAWFMT_YUV400 , frame - > tempdata ,
frame - > data ) ;
} else {
if ( frame - > compressed )
decompress ( ov , frame , frame - > rawdata ,
frame - > data ) ;
else
yuv400raw_to_yuv400p ( frame , frame - > rawdata ,
frame - > data ) ;
}
}
/* Process raw YUV420 data into standard YUV420P */
static void
ov51x_postprocess_yuv420 ( struct usb_ov511 * ov , struct ov511_frame * frame )
{
/* Deinterlace frame, if necessary */
if ( ov - > sensor = = SEN_SAA7111A & & frame - > rawheight > = 480 ) {
if ( frame - > compressed )
decompress ( ov , frame , frame - > rawdata , frame - > tempdata ) ;
else
yuv420raw_to_yuv420p ( frame , frame - > rawdata ,
frame - > tempdata ) ;
deinterlace ( frame , RAWFMT_YUV420 , frame - > tempdata ,
frame - > data ) ;
} else {
if ( frame - > compressed )
decompress ( ov , frame , frame - > rawdata , frame - > data ) ;
else
yuv420raw_to_yuv420p ( frame , frame - > rawdata ,
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 ) {
case VIDEO_PALETTE_GREY :
ov51x_postprocess_grey ( ov , frame ) ;
break ;
case VIDEO_PALETTE_YUV420 :
case VIDEO_PALETTE_YUV420P :
ov51x_postprocess_yuv420 ( ov , frame ) ;
break ;
default :
err ( " Cannot convert data to %s " ,
symbolic ( v4l1_plist , frame - > format ) ) ;
}
}
}
/**********************************************************************
*
* OV51x data transfer , IRQ handler
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static inline void
ov511_move_data ( struct usb_ov511 * ov , unsigned char * in , int n )
{
int num , offset ;
int pnum = in [ ov - > packet_size - 1 ] ; /* Get packet number */
int max_raw = MAX_RAW_DATA_SIZE ( ov - > maxwidth , ov - > maxheight ) ;
struct ov511_frame * frame = & ov - > frame [ ov - > curframe ] ;
struct timeval * ts ;
/* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
* byte non - zero . The EOF packet has image width / height in the
* 10 th and 11 th bytes . The 9 th byte is given as follows :
*
* bit 7 : EOF
* 6 : compression enabled
* 5 : 422 / 420 / 400 modes
* 4 : 422 / 420 / 400 modes
* 3 : 1
* 2 : snapshot button on
* 1 : snapshot frame
* 0 : even / odd field
*/
if ( printph ) {
info ( " ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x " ,
pnum , in [ 0 ] , in [ 1 ] , in [ 2 ] , in [ 3 ] , in [ 4 ] , in [ 5 ] , in [ 6 ] ,
in [ 7 ] , in [ 8 ] , in [ 9 ] , in [ 10 ] , in [ 11 ] ) ;
}
/* Check for SOF/EOF packet */
if ( ( in [ 0 ] | in [ 1 ] | in [ 2 ] | in [ 3 ] | in [ 4 ] | in [ 5 ] | in [ 6 ] | in [ 7 ] ) | |
( ~ in [ 8 ] & 0x08 ) )
goto check_middle ;
/* Frame end */
if ( in [ 8 ] & 0x80 ) {
ts = ( struct timeval * ) ( frame - > data
+ MAX_FRAME_SIZE ( ov - > maxwidth , ov - > maxheight ) ) ;
do_gettimeofday ( ts ) ;
/* Get the actual frame size from the EOF header */
frame - > rawwidth = ( ( int ) ( in [ 9 ] ) + 1 ) * 8 ;
frame - > rawheight = ( ( int ) ( in [ 10 ] ) + 1 ) * 8 ;
PDEBUG ( 4 , " Frame end, frame=%d, pnum=%d, w=%d, h=%d, recvd=%d " ,
ov - > curframe , pnum , frame - > rawwidth , frame - > rawheight ,
frame - > bytes_recvd ) ;
/* Validate the header data */
RESTRICT_TO_RANGE ( frame - > rawwidth , ov - > minwidth , ov - > maxwidth ) ;
RESTRICT_TO_RANGE ( frame - > rawheight , ov - > minheight ,
ov - > maxheight ) ;
/* Don't allow byte count to exceed buffer size */
RESTRICT_TO_RANGE ( frame - > bytes_recvd , 8 , max_raw ) ;
if ( frame - > scanstate = = STATE_LINES ) {
int nextf ;
frame - > grabstate = FRAME_DONE ;
wake_up_interruptible ( & frame - > wq ) ;
/* If next frame is ready or grabbing,
* point to it */
nextf = ( ov - > curframe + 1 ) % OV511_NUMFRAMES ;
if ( ov - > frame [ nextf ] . grabstate = = FRAME_READY
| | ov - > frame [ nextf ] . grabstate = = FRAME_GRABBING ) {
ov - > curframe = nextf ;
ov - > frame [ nextf ] . scanstate = STATE_SCANNING ;
} else {
if ( frame - > grabstate = = FRAME_DONE ) {
PDEBUG ( 4 , " ** Frame done ** " ) ;
} else {
PDEBUG ( 4 , " Frame not ready? state = %d " ,
ov - > frame [ nextf ] . grabstate ) ;
}
ov - > curframe = - 1 ;
}
} else {
PDEBUG ( 5 , " Frame done, but not scanning " ) ;
}
/* Image corruption caused by misplaced frame->segment = 0
* fixed by carlosf @ conectiva . com . br
*/
} else {
/* Frame start */
PDEBUG ( 4 , " Frame start, framenum = %d " , ov - > curframe ) ;
/* Check to see if it's a snapshot frame */
/* FIXME?? Should the snapshot reset go here? Performance? */
if ( in [ 8 ] & 0x02 ) {
frame - > snapshot = 1 ;
PDEBUG ( 3 , " snapshot detected " ) ;
}
frame - > scanstate = STATE_LINES ;
frame - > bytes_recvd = 0 ;
frame - > compressed = in [ 8 ] & 0x40 ;
}
check_middle :
/* Are we in a frame? */
if ( frame - > scanstate ! = STATE_LINES ) {
PDEBUG ( 5 , " Not in a frame; packet skipped " ) ;
return ;
}
/* If frame start, skip header */
if ( frame - > bytes_recvd = = 0 )
offset = 9 ;
else
offset = 0 ;
num = n - offset - 1 ;
/* Dump all data exactly as received */
if ( dumppix = = 2 ) {
frame - > bytes_recvd + = n - 1 ;
if ( frame - > bytes_recvd < = max_raw )
memcpy ( frame - > rawdata + frame - > bytes_recvd - ( n - 1 ) ,
in , n - 1 ) ;
else
PDEBUG ( 3 , " Raw data buffer overrun!! (%d) " ,
frame - > bytes_recvd - max_raw ) ;
} else if ( ! frame - > compressed & & ! remove_zeros ) {
frame - > bytes_recvd + = num ;
if ( frame - > bytes_recvd < = max_raw )
memcpy ( frame - > rawdata + frame - > bytes_recvd - num ,
in + offset , num ) ;
else
PDEBUG ( 3 , " Raw data buffer overrun!! (%d) " ,
frame - > bytes_recvd - max_raw ) ;
} else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */
int b , read = 0 , allzero , copied = 0 ;
if ( offset ) {
frame - > bytes_recvd + = 32 - offset ; // Bytes out
memcpy ( frame - > rawdata , in + offset , 32 - offset ) ;
read + = 32 ;
}
while ( read < n - 1 ) {
allzero = 1 ;
for ( b = 0 ; b < 32 ; b + + ) {
if ( in [ read + b ] ) {
allzero = 0 ;
break ;
}
}
if ( allzero ) {
/* Don't copy it */
} else {
if ( frame - > bytes_recvd + copied + 32 < = max_raw )
{
memcpy ( frame - > rawdata
+ frame - > bytes_recvd + copied ,
in + read , 32 ) ;
copied + = 32 ;
} else {
PDEBUG ( 3 , " Raw data buffer overrun!! " ) ;
}
}
read + = 32 ;
}
frame - > bytes_recvd + = copied ;
}
}
static inline void
ov518_move_data ( struct usb_ov511 * ov , unsigned char * in , int n )
{
int max_raw = MAX_RAW_DATA_SIZE ( ov - > maxwidth , ov - > maxheight ) ;
struct ov511_frame * frame = & ov - > frame [ ov - > curframe ] ;
struct timeval * ts ;
/* Don't copy the packet number byte */
if ( ov - > packet_numbering )
- - n ;
/* A false positive here is likely, until OVT gives me
* the definitive SOF / EOF format */
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 ) {
PDEBUG ( 4 , " Detected frame end/start " ) ;
goto eof ;
} else { //scanstate == STATE_SCANNING
/* Frame start */
PDEBUG ( 4 , " Frame start, framenum = %d " , ov - > curframe ) ;
goto sof ;
}
} else {
goto check_middle ;
}
eof :
ts = ( struct timeval * ) ( frame - > data
+ MAX_FRAME_SIZE ( ov - > maxwidth , ov - > maxheight ) ) ;
do_gettimeofday ( ts ) ;
PDEBUG ( 4 , " Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d " ,
ov - > curframe ,
( int ) ( in [ 9 ] ) , ( int ) ( in [ 10 ] ) , frame - > bytes_recvd ) ;
// FIXME: Since we don't know the header formats yet,
// there is no way to know what the actual image size is
frame - > rawwidth = frame - > width ;
frame - > rawheight = frame - > height ;
/* Validate the header data */
RESTRICT_TO_RANGE ( frame - > rawwidth , ov - > minwidth , ov - > maxwidth ) ;
RESTRICT_TO_RANGE ( frame - > rawheight , ov - > minheight , ov - > maxheight ) ;
/* Don't allow byte count to exceed buffer size */
RESTRICT_TO_RANGE ( frame - > bytes_recvd , 8 , max_raw ) ;
if ( frame - > scanstate = = STATE_LINES ) {
int nextf ;
frame - > grabstate = FRAME_DONE ;
wake_up_interruptible ( & frame - > wq ) ;
/* If next frame is ready or grabbing,
* point to it */
nextf = ( ov - > curframe + 1 ) % OV511_NUMFRAMES ;
if ( ov - > frame [ nextf ] . grabstate = = FRAME_READY
| | ov - > frame [ nextf ] . grabstate = = FRAME_GRABBING ) {
ov - > curframe = nextf ;
ov - > frame [ nextf ] . scanstate = STATE_SCANNING ;
frame = & ov - > frame [ nextf ] ;
} else {
if ( frame - > grabstate = = FRAME_DONE ) {
PDEBUG ( 4 , " ** Frame done ** " ) ;
} else {
PDEBUG ( 4 , " Frame not ready? state = %d " ,
ov - > frame [ nextf ] . grabstate ) ;
}
ov - > curframe = - 1 ;
PDEBUG ( 4 , " SOF dropped (no active frame) " ) ;
return ; /* Nowhere to store this frame */
}
}
sof :
PDEBUG ( 4 , " Starting capture on frame %d " , frame - > framenum ) ;
// Snapshot not reverse-engineered yet.
#if 0
/* Check to see if it's a snapshot frame */
/* FIXME?? Should the snapshot reset go here? Performance? */
if ( in [ 8 ] & 0x02 ) {
frame - > snapshot = 1 ;
PDEBUG ( 3 , " snapshot detected " ) ;
}
# endif
frame - > scanstate = STATE_LINES ;
frame - > bytes_recvd = 0 ;
frame - > compressed = 1 ;
check_middle :
/* Are we in a frame? */
if ( frame - > scanstate ! = STATE_LINES ) {
PDEBUG ( 4 , " scanstate: no SOF yet " ) ;
return ;
}
/* Dump all data exactly as received */
if ( dumppix = = 2 ) {
frame - > bytes_recvd + = n ;
if ( frame - > bytes_recvd < = max_raw )
memcpy ( frame - > rawdata + frame - > bytes_recvd - n , in , n ) ;
else
PDEBUG ( 3 , " Raw data buffer overrun!! (%d) " ,
frame - > bytes_recvd - max_raw ) ;
} else {
/* All incoming data are divided into 8-byte segments. If the
* segment contains all zero bytes , it must be skipped . These
* zero - segments allow the OV518 to mainain a constant data rate
* regardless of the effectiveness of the compression . Segments
* are aligned relative to the beginning of each isochronous
* packet . The first segment in each image is a header ( the
* decompressor skips it later ) .
*/
int b , read = 0 , allzero , copied = 0 ;
while ( read < n ) {
allzero = 1 ;
for ( b = 0 ; b < 8 ; b + + ) {
if ( in [ read + b ] ) {
allzero = 0 ;
break ;
}
}
if ( allzero ) {
/* Don't copy it */
} else {
if ( frame - > bytes_recvd + copied + 8 < = max_raw )
{
memcpy ( frame - > rawdata
+ frame - > bytes_recvd + copied ,
in + read , 8 ) ;
copied + = 8 ;
} else {
PDEBUG ( 3 , " Raw data buffer overrun!! " ) ;
}
}
read + = 8 ;
}
frame - > bytes_recvd + = copied ;
}
}
static void
ov51x_isoc_irq ( struct urb * urb , struct pt_regs * regs )
{
int i ;
struct usb_ov511 * ov ;
struct ov511_sbuf * sbuf ;
if ( ! urb - > context ) {
PDEBUG ( 4 , " no context " ) ;
return ;
}
sbuf = urb - > context ;
ov = sbuf - > ov ;
if ( ! ov | | ! ov - > dev | | ! ov - > user ) {
PDEBUG ( 4 , " no device, or not open " ) ;
return ;
}
if ( ! ov - > streaming ) {
PDEBUG ( 4 , " hmmm... not streaming, but got interrupt " ) ;
return ;
}
if ( urb - > status = = - ENOENT | | urb - > status = = - ECONNRESET ) {
PDEBUG ( 4 , " URB unlinked " ) ;
return ;
}
if ( urb - > status ! = - EINPROGRESS & & urb - > status ! = 0 ) {
err ( " ERROR: urb->status=%d: %s " , urb - > status ,
symbolic ( urb_errlist , urb - > status ) ) ;
}
/* Copy the data received into our frame buffer */
PDEBUG ( 5 , " sbuf[%d]: Moving %d packets " , sbuf - > n ,
urb - > number_of_packets ) ;
for ( i = 0 ; i < urb - > number_of_packets ; i + + ) {
/* Warning: Don't call *_move_data() if no frame active! */
if ( ov - > curframe > = 0 ) {
int n = urb - > iso_frame_desc [ i ] . actual_length ;
int st = urb - > iso_frame_desc [ i ] . status ;
unsigned char * cdata ;
urb - > iso_frame_desc [ i ] . actual_length = 0 ;
urb - > iso_frame_desc [ i ] . status = 0 ;
cdata = urb - > transfer_buffer
+ urb - > iso_frame_desc [ i ] . offset ;
if ( ! n ) {
PDEBUG ( 4 , " Zero-length packet " ) ;
continue ;
}
if ( st )
PDEBUG ( 2 , " data error: [%d] len=%d, status=%d " ,
i , n , st ) ;
if ( ov - > bclass = = BCL_OV511 )
ov511_move_data ( ov , cdata , n ) ;
else if ( ov - > bclass = = BCL_OV518 )
ov518_move_data ( ov , cdata , n ) ;
else
err ( " Unknown bridge device (%d) " , ov - > bridge ) ;
} else if ( waitqueue_active ( & ov - > wq ) ) {
wake_up_interruptible ( & ov - > wq ) ;
}
}
/* Resubmit this URB */
urb - > dev = ov - > dev ;
if ( ( i = usb_submit_urb ( urb , GFP_ATOMIC ) ) ! = 0 )
err ( " usb_submit_urb() ret %d " , i ) ;
return ;
}
/****************************************************************************
*
* Stream initialization and termination
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int
ov51x_init_isoc ( struct usb_ov511 * ov )
{
struct urb * urb ;
int fx , err , n , size ;
PDEBUG ( 3 , " *** Initializing capture *** " ) ;
ov - > curframe = - 1 ;
if ( ov - > bridge = = BRG_OV511 ) {
if ( cams = = 1 )
size = 993 ;
else if ( cams = = 2 )
size = 513 ;
else if ( cams = = 3 | | cams = = 4 )
size = 257 ;
else {
err ( " \" cams \" parameter too high! " ) ;
return - 1 ;
}
} else if ( ov - > bridge = = BRG_OV511PLUS ) {
if ( cams = = 1 )
size = 961 ;
else if ( cams = = 2 )
size = 513 ;
else if ( cams = = 3 | | cams = = 4 )
size = 257 ;
else if ( cams > = 5 & & cams < = 8 )
size = 129 ;
else if ( cams > = 9 & & cams < = 31 )
size = 33 ;
else {
err ( " \" cams \" parameter too high! " ) ;
return - 1 ;
}
} else if ( ov - > bclass = = BCL_OV518 ) {
if ( cams = = 1 )
size = 896 ;
else if ( cams = = 2 )
size = 512 ;
else if ( cams = = 3 | | cams = = 4 )
size = 256 ;
else if ( cams > = 5 & & cams < = 8 )
size = 128 ;
else {
err ( " \" cams \" parameter too high! " ) ;
return - 1 ;
}
} else {
err ( " invalid bridge type " ) ;
return - 1 ;
}
// FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now
if ( ov - > bclass = = BCL_OV518 ) {
if ( packetsize = = - 1 ) {
ov518_set_packet_size ( ov , 640 ) ;
} else {
info ( " Forcing packet size to %d " , 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 + + ) {
urb = usb_alloc_urb ( FRAMES_PER_DESC , GFP_KERNEL ) ;
if ( ! urb ) {
err ( " init isoc: usb_alloc_urb ret. NULL " ) ;
return - ENOMEM ;
}
ov - > sbuf [ n ] . urb = urb ;
urb - > dev = ov - > dev ;
urb - > context = & ov - > sbuf [ n ] ;
urb - > pipe = usb_rcvisocpipe ( ov - > dev , OV511_ENDPOINT_ADDRESS ) ;
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > transfer_buffer = ov - > sbuf [ n ] . data ;
urb - > complete = ov51x_isoc_irq ;
urb - > number_of_packets = FRAMES_PER_DESC ;
urb - > transfer_buffer_length = ov - > packet_size * FRAMES_PER_DESC ;
urb - > interval = 1 ;
for ( fx = 0 ; fx < FRAMES_PER_DESC ; fx + + ) {
urb - > iso_frame_desc [ fx ] . offset = ov - > packet_size * fx ;
urb - > iso_frame_desc [ fx ] . length = ov - > packet_size ;
}
}
ov - > streaming = 1 ;
for ( n = 0 ; n < OV511_NUMSBUF ; n + + ) {
ov - > sbuf [ n ] . urb - > dev = ov - > dev ;
err = usb_submit_urb ( ov - > sbuf [ n ] . urb , GFP_KERNEL ) ;
if ( err ) {
err ( " init isoc: usb_submit_urb(%d) ret %d " , n , err ) ;
return err ;
}
}
return 0 ;
}
static void
ov51x_unlink_isoc ( struct usb_ov511 * ov )
{
int n ;
/* Unschedule all of the iso td's */
for ( n = OV511_NUMSBUF - 1 ; n > = 0 ; n - - ) {
if ( ov - > sbuf [ n ] . urb ) {
usb_kill_urb ( ov - > sbuf [ n ] . urb ) ;
usb_free_urb ( ov - > sbuf [ n ] . urb ) ;
ov - > sbuf [ n ] . urb = NULL ;
}
}
}
static void
ov51x_stop_isoc ( struct usb_ov511 * ov )
{
if ( ! ov - > streaming | | ! ov - > dev )
return ;
PDEBUG ( 3 , " *** Stopping capture *** " ) ;
if ( ov - > bclass = = BCL_OV518 )
ov518_set_packet_size ( ov , 0 ) ;
else
ov511_set_packet_size ( ov , 0 ) ;
ov - > streaming = 0 ;
ov51x_unlink_isoc ( ov ) ;
}
static int
ov51x_new_frame ( struct usb_ov511 * ov , int framenum )
{
struct ov511_frame * frame ;
int newnum ;
PDEBUG ( 4 , " ov->curframe = %d, framenum = %d " , ov - > curframe , framenum ) ;
if ( ! ov - > dev )
return - 1 ;
/* If we're not grabbing a frame right now and the other frame is */
/* ready to be grabbed into, then use it instead */
if ( ov - > curframe = = - 1 ) {
newnum = ( framenum - 1 + OV511_NUMFRAMES ) % OV511_NUMFRAMES ;
if ( ov - > frame [ newnum ] . grabstate = = FRAME_READY )
framenum = newnum ;
} else
return 0 ;
frame = & ov - > frame [ framenum ] ;
PDEBUG ( 4 , " framenum = %d, width = %d, height = %d " , framenum ,
frame - > width , frame - > height ) ;
frame - > grabstate = FRAME_GRABBING ;
frame - > scanstate = STATE_SCANNING ;
frame - > snapshot = 0 ;
ov - > curframe = framenum ;
/* Make sure it's not too big */
if ( frame - > width > ov - > maxwidth )
frame - > width = ov - > maxwidth ;
frame - > width & = ~ 7L ; /* Multiple of 8 */
if ( frame - > height > ov - > maxheight )
frame - > height = ov - > maxheight ;
frame - > height & = ~ 3L ; /* Multiple of 4 */
return 0 ;
}
/****************************************************************************
*
* Buffer management
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* - You must acquire buf_lock before entering this function .
* - Because this code will free any non - null pointer , you must be sure to null
* them if you explicitly free them somewhere else !
*/
static void
ov51x_do_dealloc ( struct usb_ov511 * ov )
{
int i ;
PDEBUG ( 4 , " entered " ) ;
if ( ov - > fbuf ) {
rvfree ( ov - > fbuf , OV511_NUMFRAMES
* MAX_DATA_SIZE ( ov - > maxwidth , ov - > maxheight ) ) ;
ov - > fbuf = NULL ;
}
vfree ( ov - > rawfbuf ) ;
ov - > rawfbuf = NULL ;
vfree ( ov - > tempfbuf ) ;
ov - > tempfbuf = NULL ;
for ( i = 0 ; i < OV511_NUMSBUF ; i + + ) {
2005-04-18 17:39:34 -07:00
kfree ( ov - > sbuf [ i ] . data ) ;
ov - > sbuf [ i ] . data = NULL ;
2005-04-16 15:20:36 -07:00
}
for ( i = 0 ; i < OV511_NUMFRAMES ; i + + ) {
ov - > frame [ i ] . data = NULL ;
ov - > frame [ i ] . rawdata = NULL ;
ov - > frame [ i ] . tempdata = NULL ;
if ( ov - > frame [ i ] . compbuf ) {
free_page ( ( unsigned long ) ov - > frame [ i ] . compbuf ) ;
ov - > frame [ i ] . compbuf = NULL ;
}
}
PDEBUG ( 4 , " buffer memory deallocated " ) ;
ov - > buf_state = BUF_NOT_ALLOCATED ;
PDEBUG ( 4 , " leaving " ) ;
}
static int
ov51x_alloc ( struct usb_ov511 * ov )
{
int i ;
const int w = ov - > maxwidth ;
const int h = ov - > maxheight ;
const int data_bufsize = OV511_NUMFRAMES * MAX_DATA_SIZE ( w , h ) ;
const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE ( w , h ) ;
PDEBUG ( 4 , " entered " ) ;
down ( & ov - > buf_lock ) ;
if ( ov - > buf_state = = BUF_ALLOCATED )
goto out ;
ov - > fbuf = rvmalloc ( data_bufsize ) ;
if ( ! ov - > fbuf )
goto error ;
ov - > rawfbuf = vmalloc ( raw_bufsize ) ;
if ( ! ov - > rawfbuf )
goto error ;
memset ( ov - > rawfbuf , 0 , raw_bufsize ) ;
ov - > tempfbuf = vmalloc ( raw_bufsize ) ;
if ( ! ov - > tempfbuf )
goto error ;
memset ( ov - > tempfbuf , 0 , raw_bufsize ) ;
for ( i = 0 ; i < OV511_NUMSBUF ; i + + ) {
ov - > sbuf [ i ] . data = kmalloc ( FRAMES_PER_DESC *
MAX_FRAME_SIZE_PER_DESC , GFP_KERNEL ) ;
if ( ! ov - > sbuf [ i ] . data )
goto error ;
PDEBUG ( 4 , " sbuf[%d] @ %p " , i , ov - > sbuf [ i ] . data ) ;
}
for ( i = 0 ; i < OV511_NUMFRAMES ; i + + ) {
ov - > frame [ i ] . data = ov - > fbuf + i * MAX_DATA_SIZE ( w , h ) ;
ov - > frame [ i ] . rawdata = ov - > rawfbuf
+ i * MAX_RAW_DATA_SIZE ( w , h ) ;
ov - > frame [ i ] . tempdata = ov - > tempfbuf
+ i * MAX_RAW_DATA_SIZE ( w , h ) ;
ov - > frame [ i ] . compbuf =
( unsigned char * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! ov - > frame [ i ] . compbuf )
goto error ;
PDEBUG ( 4 , " frame[%d] @ %p " , i , ov - > frame [ i ] . data ) ;
}
ov - > buf_state = BUF_ALLOCATED ;
out :
up ( & ov - > buf_lock ) ;
PDEBUG ( 4 , " leaving " ) ;
return 0 ;
error :
ov51x_do_dealloc ( ov ) ;
up ( & ov - > buf_lock ) ;
PDEBUG ( 4 , " errored " ) ;
return - ENOMEM ;
}
static void
ov51x_dealloc ( struct usb_ov511 * ov )
{
PDEBUG ( 4 , " entered " ) ;
down ( & ov - > buf_lock ) ;
ov51x_do_dealloc ( ov ) ;
up ( & ov - > buf_lock ) ;
PDEBUG ( 4 , " leaving " ) ;
}
/****************************************************************************
*
* V4L 1 API
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int
ov51x_v4l1_open ( struct inode * inode , struct file * file )
{
struct video_device * vdev = video_devdata ( file ) ;
struct usb_ov511 * ov = video_get_drvdata ( vdev ) ;
int err , i ;
PDEBUG ( 4 , " opening " ) ;
down ( & ov - > lock ) ;
err = - EBUSY ;
if ( ov - > user )
goto out ;
ov - > sub_flag = 0 ;
/* In case app doesn't set them... */
err = ov51x_set_default_params ( ov ) ;
if ( err < 0 )
goto out ;
/* Make sure frames are reset */
for ( i = 0 ; i < OV511_NUMFRAMES ; i + + ) {
ov - > frame [ i ] . grabstate = FRAME_UNUSED ;
ov - > frame [ i ] . bytes_read = 0 ;
}
/* If compression is on, make sure now that a
* decompressor can be loaded */
if ( ov - > compress & & ! ov - > decomp_ops ) {
err = request_decompressor ( ov ) ;
if ( err & & ! dumppix )
goto out ;
}
err = ov51x_alloc ( ov ) ;
if ( err < 0 )
goto out ;
err = ov51x_init_isoc ( ov ) ;
if ( err ) {
ov51x_dealloc ( ov ) ;
goto out ;
}
ov - > user + + ;
file - > private_data = vdev ;
if ( ov - > led_policy = = LED_AUTO )
ov51x_led_control ( ov , 1 ) ;
out :
up ( & ov - > lock ) ;
return err ;
}
static int
ov51x_v4l1_close ( struct inode * inode , struct file * file )
{
struct video_device * vdev = file - > private_data ;
struct usb_ov511 * ov = video_get_drvdata ( vdev ) ;
PDEBUG ( 4 , " ov511_close " ) ;
down ( & ov - > lock ) ;
ov - > user - - ;
ov51x_stop_isoc ( ov ) ;
release_decompressor ( ov ) ;
if ( ov - > led_policy = = LED_AUTO )
ov51x_led_control ( ov , 0 ) ;
if ( ov - > dev )
ov51x_dealloc ( ov ) ;
up ( & ov - > lock ) ;
/* Device unplugged while open. Only a minimum of unregistration is done
* here ; the disconnect callback already did the rest . */
if ( ! ov - > dev ) {
down ( & ov - > cbuf_lock ) ;
kfree ( ov - > cbuf ) ;
ov - > cbuf = NULL ;
up ( & ov - > cbuf_lock ) ;
ov51x_dealloc ( ov ) ;
kfree ( ov ) ;
ov = NULL ;
}
file - > private_data = NULL ;
return 0 ;
}
/* Do not call this function directly! */
static int
ov51x_v4l1_ioctl_internal ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg )
{
struct video_device * vdev = file - > private_data ;
struct usb_ov511 * ov = video_get_drvdata ( vdev ) ;
PDEBUG ( 5 , " IOCtl: 0x%X " , cmd ) ;
if ( ! ov - > dev )
return - EIO ;
switch ( cmd ) {
case VIDIOCGCAP :
{
struct video_capability * b = arg ;
PDEBUG ( 4 , " VIDIOCGCAP " ) ;
memset ( b , 0 , sizeof ( struct video_capability ) ) ;
sprintf ( b - > name , " %s USB Camera " ,
symbolic ( brglist , ov - > bridge ) ) ;
b - > type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE ;
b - > channels = ov - > num_inputs ;
b - > audios = 0 ;
b - > maxwidth = ov - > maxwidth ;
b - > maxheight = ov - > maxheight ;
b - > minwidth = ov - > minwidth ;
b - > minheight = ov - > minheight ;
return 0 ;
}
case VIDIOCGCHAN :
{
struct video_channel * v = arg ;
PDEBUG ( 4 , " VIDIOCGCHAN " ) ;
if ( ( unsigned ) ( v - > channel ) > = ov - > num_inputs ) {
err ( " Invalid channel (%d) " , v - > channel ) ;
return - EINVAL ;
}
v - > norm = ov - > norm ;
v - > type = VIDEO_TYPE_CAMERA ;
v - > flags = 0 ;
// v->flags |= (ov->has_decoder) ? VIDEO_VC_NORM : 0;
v - > tuners = 0 ;
decoder_get_input_name ( ov , v - > channel , v - > name ) ;
return 0 ;
}
case VIDIOCSCHAN :
{
struct video_channel * v = arg ;
int err ;
PDEBUG ( 4 , " VIDIOCSCHAN " ) ;
/* Make sure it's not a camera */
if ( ! ov - > has_decoder ) {
if ( v - > channel = = 0 )
return 0 ;
else
return - EINVAL ;
}
if ( v - > norm ! = VIDEO_MODE_PAL & &
v - > norm ! = VIDEO_MODE_NTSC & &
v - > norm ! = VIDEO_MODE_SECAM & &
v - > norm ! = VIDEO_MODE_AUTO ) {
err ( " Invalid norm (%d) " , v - > norm ) ;
return - EINVAL ;
}
if ( ( unsigned ) ( v - > channel ) > = ov - > num_inputs ) {
err ( " Invalid channel (%d) " , v - > channel ) ;
return - EINVAL ;
}
err = decoder_set_input ( ov , v - > channel ) ;
if ( err )
return err ;
err = decoder_set_norm ( ov , v - > norm ) ;
if ( err )
return err ;
return 0 ;
}
case VIDIOCGPICT :
{
struct video_picture * p = arg ;
PDEBUG ( 4 , " VIDIOCGPICT " ) ;
memset ( p , 0 , sizeof ( struct video_picture ) ) ;
if ( sensor_get_picture ( ov , p ) )
return - EIO ;
/* Can we get these from frame[0]? -claudio? */
p - > depth = ov - > frame [ 0 ] . depth ;
p - > palette = ov - > frame [ 0 ] . format ;
return 0 ;
}
case VIDIOCSPICT :
{
struct video_picture * p = arg ;
int i , rc ;
PDEBUG ( 4 , " VIDIOCSPICT " ) ;
if ( ! get_depth ( p - > palette ) )
return - EINVAL ;
if ( sensor_set_picture ( ov , p ) )
return - EIO ;
if ( force_palette & & p - > palette ! = force_palette ) {
info ( " Palette rejected (%s) " ,
symbolic ( v4l1_plist , p - > palette ) ) ;
return - EINVAL ;
}
// FIXME: Format should be independent of frames
if ( p - > palette ! = ov - > frame [ 0 ] . format ) {
PDEBUG ( 4 , " Detected format change " ) ;
rc = ov51x_wait_frames_inactive ( ov ) ;
if ( rc )
return rc ;
mode_init_regs ( ov , ov - > frame [ 0 ] . width ,
ov - > frame [ 0 ] . height , p - > palette , ov - > sub_flag ) ;
}
PDEBUG ( 4 , " Setting depth=%d, palette=%s " ,
p - > depth , symbolic ( v4l1_plist , p - > palette ) ) ;
for ( i = 0 ; i < OV511_NUMFRAMES ; i + + ) {
ov - > frame [ i ] . depth = p - > depth ;
ov - > frame [ i ] . format = p - > palette ;
}
return 0 ;
}
case VIDIOCGCAPTURE :
{
int * vf = arg ;
PDEBUG ( 4 , " VIDIOCGCAPTURE " ) ;
ov - > sub_flag = * vf ;
return 0 ;
}
case VIDIOCSCAPTURE :
{
struct video_capture * vc = arg ;
PDEBUG ( 4 , " VIDIOCSCAPTURE " ) ;
if ( vc - > flags )
return - EINVAL ;
if ( vc - > decimation )
return - EINVAL ;
vc - > x & = ~ 3L ;
vc - > y & = ~ 1L ;
vc - > y & = ~ 31L ;
if ( vc - > width = = 0 )
vc - > width = 32 ;
vc - > height / = 16 ;
vc - > height * = 16 ;
if ( vc - > height = = 0 )
vc - > height = 16 ;
ov - > subx = vc - > x ;
ov - > suby = vc - > y ;
ov - > subw = vc - > width ;
ov - > subh = vc - > height ;
return 0 ;
}
case VIDIOCSWIN :
{
struct video_window * vw = arg ;
int i , rc ;
PDEBUG ( 4 , " VIDIOCSWIN: %dx%d " , vw - > width , vw - > height ) ;
#if 0
if ( vw - > flags )
return - EINVAL ;
if ( vw - > clipcount )
return - EINVAL ;
if ( vw - > height ! = ov - > maxheight )
return - EINVAL ;
if ( vw - > width ! = ov - > maxwidth )
return - EINVAL ;
# endif
rc = ov51x_wait_frames_inactive ( ov ) ;
if ( rc )
return rc ;
rc = mode_init_regs ( ov , vw - > width , vw - > height ,
ov - > frame [ 0 ] . format , ov - > sub_flag ) ;
if ( rc < 0 )
return rc ;
for ( i = 0 ; i < OV511_NUMFRAMES ; i + + ) {
ov - > frame [ i ] . width = vw - > width ;
ov - > frame [ i ] . height = vw - > height ;
}
return 0 ;
}
case VIDIOCGWIN :
{
struct video_window * vw = arg ;
memset ( vw , 0 , sizeof ( struct video_window ) ) ;
vw - > x = 0 ; /* FIXME */
vw - > y = 0 ;
vw - > width = ov - > frame [ 0 ] . width ;
vw - > height = ov - > frame [ 0 ] . height ;
vw - > flags = 30 ;
PDEBUG ( 4 , " VIDIOCGWIN: %dx%d " , vw - > width , vw - > height ) ;
return 0 ;
}
case VIDIOCGMBUF :
{
struct video_mbuf * vm = arg ;
int i ;
PDEBUG ( 4 , " VIDIOCGMBUF " ) ;
memset ( vm , 0 , sizeof ( struct video_mbuf ) ) ;
vm - > size = OV511_NUMFRAMES
* MAX_DATA_SIZE ( ov - > maxwidth , ov - > maxheight ) ;
vm - > frames = OV511_NUMFRAMES ;
vm - > offsets [ 0 ] = 0 ;
for ( i = 1 ; i < OV511_NUMFRAMES ; i + + ) {
vm - > offsets [ i ] = vm - > offsets [ i - 1 ]
+ MAX_DATA_SIZE ( ov - > maxwidth , ov - > maxheight ) ;
}
return 0 ;
}
case VIDIOCMCAPTURE :
{
struct video_mmap * vm = arg ;
int rc , depth ;
unsigned int f = vm - > frame ;
PDEBUG ( 4 , " VIDIOCMCAPTURE: frame: %d, %dx%d, %s " , f , vm - > width ,
vm - > height , symbolic ( v4l1_plist , vm - > format ) ) ;
depth = get_depth ( vm - > format ) ;
if ( ! depth ) {
PDEBUG ( 2 , " VIDIOCMCAPTURE: invalid format (%s) " ,
symbolic ( v4l1_plist , vm - > format ) ) ;
return - EINVAL ;
}
if ( f > = OV511_NUMFRAMES ) {
err ( " VIDIOCMCAPTURE: invalid frame (%d) " , f ) ;
return - EINVAL ;
}
if ( vm - > width > ov - > maxwidth
| | vm - > height > ov - > maxheight ) {
err ( " VIDIOCMCAPTURE: requested dimensions too big " ) ;
return - EINVAL ;
}
if ( ov - > frame [ f ] . grabstate = = FRAME_GRABBING ) {
PDEBUG ( 4 , " VIDIOCMCAPTURE: already grabbing " ) ;
return - EBUSY ;
}
if ( force_palette & & ( vm - > format ! = force_palette ) ) {
PDEBUG ( 2 , " palette rejected (%s) " ,
symbolic ( v4l1_plist , vm - > format ) ) ;
return - EINVAL ;
}
if ( ( ov - > frame [ f ] . width ! = vm - > width ) | |
( ov - > frame [ f ] . height ! = vm - > height ) | |
( ov - > frame [ f ] . format ! = vm - > format ) | |
( ov - > frame [ f ] . sub_flag ! = ov - > sub_flag ) | |
( ov - > frame [ f ] . depth ! = depth ) ) {
PDEBUG ( 4 , " VIDIOCMCAPTURE: change in image parameters " ) ;
rc = ov51x_wait_frames_inactive ( ov ) ;
if ( rc )
return rc ;
rc = mode_init_regs ( ov , vm - > width , vm - > height ,
vm - > format , ov - > sub_flag ) ;
#if 0
if ( rc < 0 ) {
PDEBUG ( 1 , " Got error while initializing regs " ) ;
return ret ;
}
# endif
ov - > frame [ f ] . width = vm - > width ;
ov - > frame [ f ] . height = vm - > height ;
ov - > frame [ f ] . format = vm - > format ;
ov - > frame [ f ] . sub_flag = ov - > sub_flag ;
ov - > frame [ f ] . depth = depth ;
}
/* Mark it as ready */
ov - > frame [ f ] . grabstate = FRAME_READY ;
PDEBUG ( 4 , " VIDIOCMCAPTURE: renewing frame %d " , f ) ;
return ov51x_new_frame ( ov , f ) ;
}
case VIDIOCSYNC :
{
unsigned int fnum = * ( ( unsigned int * ) arg ) ;
struct ov511_frame * frame ;
int rc ;
if ( fnum > = OV511_NUMFRAMES ) {
err ( " VIDIOCSYNC: invalid frame (%d) " , fnum ) ;
return - EINVAL ;
}
frame = & ov - > frame [ fnum ] ;
PDEBUG ( 4 , " syncing to frame %d, grabstate = %d " , fnum ,
frame - > grabstate ) ;
switch ( frame - > grabstate ) {
case FRAME_UNUSED :
return - EINVAL ;
case FRAME_READY :
case FRAME_GRABBING :
case FRAME_ERROR :
redo :
if ( ! ov - > dev )
return - EIO ;
rc = wait_event_interruptible ( frame - > wq ,
( frame - > grabstate = = FRAME_DONE )
| | ( frame - > grabstate = = FRAME_ERROR ) ) ;
if ( rc )
return rc ;
if ( frame - > grabstate = = FRAME_ERROR ) {
if ( ( rc = ov51x_new_frame ( ov , fnum ) ) < 0 )
return rc ;
goto redo ;
}
/* Fall through */
case FRAME_DONE :
if ( ov - > snap_enabled & & ! frame - > snapshot ) {
if ( ( rc = ov51x_new_frame ( ov , fnum ) ) < 0 )
return rc ;
goto redo ;
}
frame - > grabstate = FRAME_UNUSED ;
/* Reset the hardware snapshot button */
/* FIXME - Is this the best place for this? */
if ( ( ov - > snap_enabled ) & & ( frame - > snapshot ) ) {
frame - > snapshot = 0 ;
ov51x_clear_snapshot ( ov ) ;
}
/* Decompression, format conversion, etc... */
ov51x_postprocess ( ov , frame ) ;
break ;
} /* end switch */
return 0 ;
}
case VIDIOCGFBUF :
{
struct video_buffer * vb = arg ;
PDEBUG ( 4 , " VIDIOCGFBUF " ) ;
memset ( vb , 0 , sizeof ( struct video_buffer ) ) ;
return 0 ;
}
case VIDIOCGUNIT :
{
struct video_unit * vu = arg ;
PDEBUG ( 4 , " VIDIOCGUNIT " ) ;
memset ( vu , 0 , sizeof ( struct video_unit ) ) ;
vu - > video = ov - > vdev - > minor ;
vu - > vbi = VIDEO_NO_UNIT ;
vu - > radio = VIDEO_NO_UNIT ;
vu - > audio = VIDEO_NO_UNIT ;
vu - > teletext = VIDEO_NO_UNIT ;
return 0 ;
}
case OV511IOC_WI2C :
{
struct ov511_i2c_struct * w = arg ;
return i2c_w_slave ( ov , w - > slave , w - > reg , w - > value , w - > mask ) ;
}
case OV511IOC_RI2C :
{
struct ov511_i2c_struct * r = arg ;
int rc ;
rc = i2c_r_slave ( ov , r - > slave , r - > reg ) ;
if ( rc < 0 )
return rc ;
r - > value = rc ;
return 0 ;
}
default :
PDEBUG ( 3 , " Unsupported IOCtl: 0x%X " , cmd ) ;
return - ENOIOCTLCMD ;
} /* end switch */
return 0 ;
}
static int
ov51x_v4l1_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
struct video_device * vdev = file - > private_data ;
struct usb_ov511 * ov = video_get_drvdata ( vdev ) ;
int rc ;
if ( down_interruptible ( & ov - > lock ) )
return - EINTR ;
rc = video_usercopy ( inode , file , cmd , arg , ov51x_v4l1_ioctl_internal ) ;
up ( & ov - > lock ) ;
return rc ;
}
static ssize_t
ov51x_v4l1_read ( struct file * file , char __user * buf , size_t cnt , loff_t * ppos )
{
struct video_device * vdev = file - > private_data ;
int noblock = file - > f_flags & O_NONBLOCK ;
unsigned long count = cnt ;
struct usb_ov511 * ov = video_get_drvdata ( vdev ) ;
int i , rc = 0 , frmx = - 1 ;
struct ov511_frame * frame ;
if ( down_interruptible ( & ov - > lock ) )
return - EINTR ;
PDEBUG ( 4 , " %ld bytes, noblock=%d " , count , noblock ) ;
if ( ! vdev | | ! buf ) {
rc = - EFAULT ;
goto error ;
}
if ( ! ov - > dev ) {
rc = - EIO ;
goto error ;
}
// FIXME: Only supports two frames
/* See if a frame is completed, then use it. */
if ( ov - > frame [ 0 ] . grabstate > = FRAME_DONE ) /* _DONE or _ERROR */
frmx = 0 ;
else if ( ov - > frame [ 1 ] . grabstate > = FRAME_DONE ) /* _DONE or _ERROR */
frmx = 1 ;
/* If nonblocking we return immediately */
if ( noblock & & ( frmx = = - 1 ) ) {
rc = - EAGAIN ;
goto error ;
}
/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
/* See if a frame is in process (grabbing), then use it. */
if ( frmx = = - 1 ) {
if ( ov - > frame [ 0 ] . grabstate = = FRAME_GRABBING )
frmx = 0 ;
else if ( ov - > frame [ 1 ] . grabstate = = FRAME_GRABBING )
frmx = 1 ;
}
/* If no frame is active, start one. */
if ( frmx = = - 1 ) {
if ( ( rc = ov51x_new_frame ( ov , frmx = 0 ) ) ) {
err ( " read: ov51x_new_frame error " ) ;
goto error ;
}
}
frame = & ov - > frame [ frmx ] ;
restart :
if ( ! ov - > dev ) {
rc = - EIO ;
goto error ;
}
/* Wait while we're grabbing the image */
PDEBUG ( 4 , " Waiting image grabbing " ) ;
rc = wait_event_interruptible ( frame - > wq ,
( frame - > grabstate = = FRAME_DONE )
| | ( frame - > grabstate = = FRAME_ERROR ) ) ;
if ( rc )
goto error ;
PDEBUG ( 4 , " Got image, frame->grabstate = %d " , frame - > grabstate ) ;
PDEBUG ( 4 , " bytes_recvd = %d " , frame - > bytes_recvd ) ;
if ( frame - > grabstate = = FRAME_ERROR ) {
frame - > bytes_read = 0 ;
err ( " ** ick! ** Errored frame %d " , ov - > curframe ) ;
if ( ov51x_new_frame ( ov , frmx ) ) {
err ( " read: ov51x_new_frame error " ) ;
goto error ;
}
goto restart ;
}
/* Repeat until we get a snapshot frame */
if ( ov - > snap_enabled )
PDEBUG ( 4 , " Waiting snapshot frame " ) ;
if ( ov - > snap_enabled & & ! frame - > snapshot ) {
frame - > bytes_read = 0 ;
if ( ( rc = ov51x_new_frame ( ov , frmx ) ) ) {
err ( " read: ov51x_new_frame error " ) ;
goto error ;
}
goto restart ;
}
/* Clear the snapshot */
if ( ov - > snap_enabled & & frame - > snapshot ) {
frame - > snapshot = 0 ;
ov51x_clear_snapshot ( ov ) ;
}
/* Decompression, format conversion, etc... */
ov51x_postprocess ( ov , frame ) ;
PDEBUG ( 4 , " frmx=%d, bytes_read=%ld, length=%ld " , frmx ,
frame - > bytes_read ,
get_frame_length ( frame ) ) ;
/* copy bytes to user space; we allow for partials reads */
// if ((count + frame->bytes_read)
// > get_frame_length((struct ov511_frame *)frame))
// count = frame->scanlength - frame->bytes_read;
/* FIXME - count hardwired to be one frame... */
count = get_frame_length ( frame ) ;
PDEBUG ( 4 , " Copy to user space: %ld bytes " , count ) ;
if ( ( i = copy_to_user ( buf , frame - > data + frame - > bytes_read , count ) ) ) {
PDEBUG ( 4 , " Copy failed! %d bytes not copied " , i ) ;
rc = - EFAULT ;
goto error ;
}
frame - > bytes_read + = count ;
PDEBUG ( 4 , " {copy} count used=%ld, new bytes_read=%ld " ,
count , frame - > bytes_read ) ;
/* If all data have been read... */
if ( frame - > bytes_read
> = get_frame_length ( frame ) ) {
frame - > bytes_read = 0 ;
// FIXME: Only supports two frames
/* Mark it as available to be used again. */
ov - > frame [ frmx ] . grabstate = FRAME_UNUSED ;
if ( ( rc = ov51x_new_frame ( ov , ! frmx ) ) ) {
err ( " ov51x_new_frame returned error " ) ;
goto error ;
}
}
PDEBUG ( 4 , " read finished, returning %ld (sweet) " , count ) ;
up ( & ov - > lock ) ;
return count ;
error :
up ( & ov - > lock ) ;
return rc ;
}
static int
ov51x_v4l1_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct video_device * vdev = file - > private_data ;
unsigned long start = vma - > vm_start ;
unsigned long size = vma - > vm_end - vma - > vm_start ;
struct usb_ov511 * ov = video_get_drvdata ( vdev ) ;
unsigned long page , pos ;
if ( ov - > dev = = NULL )
return - EIO ;
PDEBUG ( 4 , " mmap: %ld (%lX) bytes " , size , size ) ;
if ( size > ( ( ( OV511_NUMFRAMES
* MAX_DATA_SIZE ( ov - > maxwidth , ov - > maxheight )
+ PAGE_SIZE - 1 ) & ~ ( PAGE_SIZE - 1 ) ) ) )
return - EINVAL ;
if ( down_interruptible ( & ov - > lock ) )
return - EINTR ;
pos = ( unsigned long ) ov - > fbuf ;
while ( size > 0 ) {
page = vmalloc_to_pfn ( ( void * ) pos ) ;
if ( remap_pfn_range ( vma , start , page , PAGE_SIZE , PAGE_SHARED ) ) {
up ( & ov - > lock ) ;
return - EAGAIN ;
}
start + = PAGE_SIZE ;
pos + = PAGE_SIZE ;
if ( size > PAGE_SIZE )
size - = PAGE_SIZE ;
else
size = 0 ;
}
up ( & ov - > lock ) ;
return 0 ;
}
static struct file_operations ov511_fops = {
. owner = THIS_MODULE ,
. open = ov51x_v4l1_open ,
. release = ov51x_v4l1_close ,
. read = ov51x_v4l1_read ,
. mmap = ov51x_v4l1_mmap ,
. ioctl = ov51x_v4l1_ioctl ,
. llseek = no_llseek ,
} ;
static struct video_device vdev_template = {
. owner = THIS_MODULE ,
. name = " OV511 USB Camera " ,
. type = VID_TYPE_CAPTURE ,
. hardware = VID_HARDWARE_OV511 ,
. fops = & ov511_fops ,
. release = video_device_release ,
. minor = - 1 ,
} ;
/****************************************************************************
*
* OV511 and sensor configuration
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
* the same register settings as the OV7610 , since they are very similar .
*/
static int
ov7xx0_configure ( struct usb_ov511 * ov )
{
int i , success ;
int rc ;
/* 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 ov511_regvals aRegvalsNorm7610 [ ] = {
{ OV511_I2C_BUS , 0x10 , 0xff } ,
{ OV511_I2C_BUS , 0x16 , 0x06 } ,
{ OV511_I2C_BUS , 0x28 , 0x24 } ,
{ OV511_I2C_BUS , 0x2b , 0xac } ,
{ OV511_I2C_BUS , 0x12 , 0x00 } ,
{ OV511_I2C_BUS , 0x38 , 0x81 } ,
{ OV511_I2C_BUS , 0x28 , 0x24 } , /* 0c */
{ OV511_I2C_BUS , 0x0f , 0x85 } , /* lg's setting */
{ OV511_I2C_BUS , 0x15 , 0x01 } ,
{ OV511_I2C_BUS , 0x20 , 0x1c } ,
{ OV511_I2C_BUS , 0x23 , 0x2a } ,
{ OV511_I2C_BUS , 0x24 , 0x10 } ,
{ OV511_I2C_BUS , 0x25 , 0x8a } ,
{ OV511_I2C_BUS , 0x26 , 0xa2 } ,
{ OV511_I2C_BUS , 0x27 , 0xc2 } ,
{ OV511_I2C_BUS , 0x2a , 0x04 } ,
{ OV511_I2C_BUS , 0x2c , 0xfe } ,
{ OV511_I2C_BUS , 0x2d , 0x93 } ,
{ OV511_I2C_BUS , 0x30 , 0x71 } ,
{ OV511_I2C_BUS , 0x31 , 0x60 } ,
{ OV511_I2C_BUS , 0x32 , 0x26 } ,
{ OV511_I2C_BUS , 0x33 , 0x20 } ,
{ OV511_I2C_BUS , 0x34 , 0x48 } ,
{ OV511_I2C_BUS , 0x12 , 0x24 } ,
{ OV511_I2C_BUS , 0x11 , 0x01 } ,
{ OV511_I2C_BUS , 0x0c , 0x24 } ,
{ OV511_I2C_BUS , 0x0d , 0x24 } ,
{ OV511_DONE_BUS , 0x0 , 0x00 } ,
} ;
static struct ov511_regvals aRegvalsNorm7620 [ ] = {
{ OV511_I2C_BUS , 0x00 , 0x00 } ,
{ OV511_I2C_BUS , 0x01 , 0x80 } ,
{ OV511_I2C_BUS , 0x02 , 0x80 } ,
{ OV511_I2C_BUS , 0x03 , 0xc0 } ,
{ OV511_I2C_BUS , 0x06 , 0x60 } ,
{ OV511_I2C_BUS , 0x07 , 0x00 } ,
{ OV511_I2C_BUS , 0x0c , 0x24 } ,
{ OV511_I2C_BUS , 0x0c , 0x24 } ,
{ OV511_I2C_BUS , 0x0d , 0x24 } ,
{ OV511_I2C_BUS , 0x11 , 0x01 } ,
{ OV511_I2C_BUS , 0x12 , 0x24 } ,
{ OV511_I2C_BUS , 0x13 , 0x01 } ,
{ OV511_I2C_BUS , 0x14 , 0x84 } ,
{ OV511_I2C_BUS , 0x15 , 0x01 } ,
{ OV511_I2C_BUS , 0x16 , 0x03 } ,
{ OV511_I2C_BUS , 0x17 , 0x2f } ,
{ OV511_I2C_BUS , 0x18 , 0xcf } ,
{ OV511_I2C_BUS , 0x19 , 0x06 } ,
{ OV511_I2C_BUS , 0x1a , 0xf5 } ,
{ OV511_I2C_BUS , 0x1b , 0x00 } ,
{ OV511_I2C_BUS , 0x20 , 0x18 } ,
{ OV511_I2C_BUS , 0x21 , 0x80 } ,
{ OV511_I2C_BUS , 0x22 , 0x80 } ,
{ OV511_I2C_BUS , 0x23 , 0x00 } ,
{ OV511_I2C_BUS , 0x26 , 0xa2 } ,
{ OV511_I2C_BUS , 0x27 , 0xea } ,
{ OV511_I2C_BUS , 0x28 , 0x20 } ,
{ OV511_I2C_BUS , 0x29 , 0x00 } ,
{ OV511_I2C_BUS , 0x2a , 0x10 } ,
{ OV511_I2C_BUS , 0x2b , 0x00 } ,
{ OV511_I2C_BUS , 0x2c , 0x88 } ,
{ OV511_I2C_BUS , 0x2d , 0x91 } ,
{ OV511_I2C_BUS , 0x2e , 0x80 } ,
{ OV511_I2C_BUS , 0x2f , 0x44 } ,
{ OV511_I2C_BUS , 0x60 , 0x27 } ,
{ OV511_I2C_BUS , 0x61 , 0x02 } ,
{ OV511_I2C_BUS , 0x62 , 0x5f } ,
{ OV511_I2C_BUS , 0x63 , 0xd5 } ,
{ OV511_I2C_BUS , 0x64 , 0x57 } ,
{ OV511_I2C_BUS , 0x65 , 0x83 } ,
{ OV511_I2C_BUS , 0x66 , 0x55 } ,
{ OV511_I2C_BUS , 0x67 , 0x92 } ,
{ OV511_I2C_BUS , 0x68 , 0xcf } ,
{ OV511_I2C_BUS , 0x69 , 0x76 } ,
{ OV511_I2C_BUS , 0x6a , 0x22 } ,
{ OV511_I2C_BUS , 0x6b , 0x00 } ,
{ OV511_I2C_BUS , 0x6c , 0x02 } ,
{ OV511_I2C_BUS , 0x6d , 0x44 } ,
{ OV511_I2C_BUS , 0x6e , 0x80 } ,
{ OV511_I2C_BUS , 0x6f , 0x1d } ,
{ OV511_I2C_BUS , 0x70 , 0x8b } ,
{ OV511_I2C_BUS , 0x71 , 0x00 } ,
{ OV511_I2C_BUS , 0x72 , 0x14 } ,
{ OV511_I2C_BUS , 0x73 , 0x54 } ,
{ OV511_I2C_BUS , 0x74 , 0x00 } ,
{ OV511_I2C_BUS , 0x75 , 0x8e } ,
{ OV511_I2C_BUS , 0x76 , 0x00 } ,
{ OV511_I2C_BUS , 0x77 , 0xff } ,
{ OV511_I2C_BUS , 0x78 , 0x80 } ,
{ OV511_I2C_BUS , 0x79 , 0x80 } ,
{ OV511_I2C_BUS , 0x7a , 0x80 } ,
{ OV511_I2C_BUS , 0x7b , 0xe2 } ,
{ OV511_I2C_BUS , 0x7c , 0x00 } ,
{ OV511_DONE_BUS , 0x0 , 0x00 } ,
} ;
PDEBUG ( 4 , " starting configuration " ) ;
/* This looks redundant, but is necessary for WebCam 3 */
ov - > primary_i2c_slave = OV7xx0_SID ;
if ( ov51x_set_slave_ids ( ov , OV7xx0_SID ) < 0 )
return - 1 ;
if ( init_ov_sensor ( ov ) > = 0 ) {
PDEBUG ( 1 , " OV7xx0 sensor initalized (method 1) " ) ;
} else {
/* Reset the 76xx */
if ( i2c_w ( ov , 0x12 , 0x80 ) < 0 )
return - 1 ;
/* Wait for it to initialize */
msleep ( 150 ) ;
i = 0 ;
success = 0 ;
while ( i < = i2c_detect_tries ) {
if ( ( i2c_r ( ov , OV7610_REG_ID_HIGH ) = = 0x7F ) & &
( i2c_r ( ov , OV7610_REG_ID_LOW ) = = 0xA2 ) ) {
success = 1 ;
break ;
} else {
i + + ;
}
}
// Was (i == i2c_detect_tries) previously. This obviously used to always report
// success. Whether anyone actually depended on that bug is unknown
if ( ( i > = i2c_detect_tries ) & & ( success = = 0 ) ) {
err ( " Failed to read sensor ID. You might not have an " ) ;
err ( " OV7610/20, or it may be not responding. Report " ) ;
err ( " this to " EMAIL ) ;
err ( " This is only a warning. You can attempt to use " ) ;
err ( " your camera anyway " ) ;
// Only issue a warning for now
// return -1;
} else {
PDEBUG ( 1 , " OV7xx0 initialized (method 2, %dx) " , i + 1 ) ;
}
}
/* Detect sensor (sub)type */
rc = i2c_r ( ov , OV7610_REG_COM_I ) ;
if ( rc < 0 ) {
err ( " Error detecting sensor type " ) ;
return - 1 ;
} else if ( ( rc & 3 ) = = 3 ) {
info ( " Sensor is an OV7610 " ) ;
ov - > sensor = SEN_OV7610 ;
} else if ( ( rc & 3 ) = = 1 ) {
/* I don't know what's different about the 76BE yet. */
if ( i2c_r ( ov , 0x15 ) & 1 )
info ( " Sensor is an OV7620AE " ) ;
else
info ( " Sensor is an OV76BE " ) ;
/* OV511+ will return all zero isoc data unless we
* configure the sensor as a 7620. Someone needs to
* find the exact reg . setting that causes this . */
if ( ov - > bridge = = BRG_OV511PLUS ) {
info ( " Enabling 511+/7620AE workaround " ) ;
ov - > sensor = SEN_OV7620 ;
} else {
ov - > sensor = SEN_OV76BE ;
}
} else if ( ( rc & 3 ) = = 0 ) {
info ( " Sensor is an OV7620 " ) ;
ov - > sensor = SEN_OV7620 ;
} else {
err ( " Unknown image sensor version: %d " , rc & 3 ) ;
return - 1 ;
}
if ( ov - > sensor = = SEN_OV7620 ) {
PDEBUG ( 4 , " Writing 7620 registers " ) ;
if ( write_regvals ( ov , aRegvalsNorm7620 ) )
return - 1 ;
} else {
PDEBUG ( 4 , " Writing 7610 registers " ) ;
if ( write_regvals ( ov , aRegvalsNorm7610 ) )
return - 1 ;
}
/* Set sensor-specific vars */
ov - > maxwidth = 640 ;
ov - > maxheight = 480 ;
ov - > minwidth = 64 ;
ov - > minheight = 48 ;
// FIXME: These do not match the actual settings yet
ov - > brightness = 0x80 < < 8 ;
ov - > contrast = 0x80 < < 8 ;
ov - > colour = 0x80 < < 8 ;
ov - > hue = 0x80 < < 8 ;
return 0 ;
}
/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
static int
ov6xx0_configure ( struct usb_ov511 * ov )
{
int rc ;
static struct ov511_regvals aRegvalsNorm6x20 [ ] = {
{ OV511_I2C_BUS , 0x12 , 0x80 } , /* reset */
{ OV511_I2C_BUS , 0x11 , 0x01 } ,
{ OV511_I2C_BUS , 0x03 , 0x60 } ,
{ OV511_I2C_BUS , 0x05 , 0x7f } , /* For when autoadjust is off */
{ OV511_I2C_BUS , 0x07 , 0xa8 } ,
/* The ratio of 0x0c and 0x0d controls the white point */
{ OV511_I2C_BUS , 0x0c , 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 , 0x14 , 0x04 } ,
/* 0x16: 0x06 helps frame stability with moving objects */
{ OV511_I2C_BUS , 0x16 , 0x06 } ,
// { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
{ OV511_I2C_BUS , 0x26 , 0xb2 } , /* BLC enable */
/* 0x28: 0x05 Selects RGB format if RGB on */
{ OV511_I2C_BUS , 0x28 , 0x05 } ,
{ OV511_I2C_BUS , 0x2a , 0x04 } , /* Disable framerate adjust */
// { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
{ OV511_I2C_BUS , 0x2d , 0x99 } ,
2005-05-03 19:07:24 -06:00
{ OV511_I2C_BUS , 0x33 , 0xa0 } , /* Color Processing Parameter */
2005-04-16 15:20:36 -07:00
{ OV511_I2C_BUS , 0x34 , 0xd2 } , /* Max A/D range */
{ OV511_I2C_BUS , 0x38 , 0x8b } ,
{ OV511_I2C_BUS , 0x39 , 0x40 } ,
{ OV511_I2C_BUS , 0x3c , 0x39 } , /* Enable AEC mode changing */
{ OV511_I2C_BUS , 0x3c , 0x3c } , /* Change AEC mode */
{ OV511_I2C_BUS , 0x3c , 0x24 } , /* Disable AEC mode changing */
{ OV511_I2C_BUS , 0x3d , 0x80 } ,
/* These next two registers (0x4a, 0x4b) are undocumented. They
* control the color balance */
{ OV511_I2C_BUS , 0x4a , 0x80 } ,
{ OV511_I2C_BUS , 0x4b , 0x80 } ,
{ OV511_I2C_BUS , 0x4d , 0xd2 } , /* This reduces noise a bit */
{ OV511_I2C_BUS , 0x4e , 0xc1 } ,
{ OV511_I2C_BUS , 0x4f , 0x04 } ,
// Do 50-53 have any effect?
// Toggle 0x12[2] off and on here?
{ OV511_DONE_BUS , 0x0 , 0x00 } , /* END MARKER */
} ;
static struct ov511_regvals aRegvalsNorm6x30 [ ] = {
/*OK*/ { OV511_I2C_BUS , 0x12 , 0x80 } , /* reset */
{ OV511_I2C_BUS , 0x11 , 0x00 } ,
/*OK*/ { OV511_I2C_BUS , 0x03 , 0x60 } ,
/*0A?*/ { OV511_I2C_BUS , 0x05 , 0x7f } , /* For when autoadjust is off */
{ OV511_I2C_BUS , 0x07 , 0xa8 } ,
/* The ratio of 0x0c and 0x0d controls the white point */
/*OK*/ { OV511_I2C_BUS , 0x0c , 0x24 } ,
/*OK*/ { OV511_I2C_BUS , 0x0d , 0x24 } ,
/*A*/ { OV511_I2C_BUS , 0x0e , 0x20 } ,
// /*04?*/ { OV511_I2C_BUS, 0x14, 0x80 },
{ OV511_I2C_BUS , 0x16 , 0x03 } ,
// /*OK*/ { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
// 21 & 22? The suggested values look wrong. Go with default
/*A*/ { OV511_I2C_BUS , 0x23 , 0xc0 } ,
/*A*/ { OV511_I2C_BUS , 0x25 , 0x9a } , // Check this against default
// /*OK*/ { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
/* 0x28: 0x05 Selects RGB format if RGB on */
// /*04?*/ { OV511_I2C_BUS, 0x28, 0x05 },
// /*04?*/ { OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus
/*OK*/ { OV511_I2C_BUS , 0x2a , 0x04 } , /* Disable framerate adjust */
// /*OK*/ { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
{ OV511_I2C_BUS , 0x2d , 0x99 } ,
// /*A*/ { OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
// /*d2?*/ { OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
// /*8b?*/ { OV511_I2C_BUS, 0x38, 0x83 },
// /*40?*/ { OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
// { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
// { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
// { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
{ OV511_I2C_BUS , 0x3d , 0x80 } ,
// /*A*/ { OV511_I2C_BUS, 0x3f, 0x0e },
/* These next two registers (0x4a, 0x4b) are undocumented. They
* control the color balance */
// /*OK?*/ { OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
// /*OK?*/ { OV511_I2C_BUS, 0x4b, 0x80 },
{ OV511_I2C_BUS , 0x4d , 0x10 } , /* U = 0.563u, V = 0.714v */
/*c1?*/ { OV511_I2C_BUS , 0x4e , 0x40 } ,
/* UV average mode, color killer: strongest */
{ OV511_I2C_BUS , 0x4f , 0x07 } ,
{ OV511_I2C_BUS , 0x54 , 0x23 } , /* Max AGC gain: 18dB */
{ OV511_I2C_BUS , 0x57 , 0x81 } , /* (default) */
{ OV511_I2C_BUS , 0x59 , 0x01 } , /* AGC dark current comp: +1 */
{ OV511_I2C_BUS , 0x5a , 0x2c } , /* (undocumented) */
{ OV511_I2C_BUS , 0x5b , 0x0f } , /* AWB chrominance levels */
// { OV511_I2C_BUS, 0x5c, 0x10 },
{ OV511_DONE_BUS , 0x0 , 0x00 } , /* END MARKER */
} ;
PDEBUG ( 4 , " starting sensor configuration " ) ;
if ( init_ov_sensor ( ov ) < 0 ) {
err ( " Failed to read sensor ID. You might not have an OV6xx0, " ) ;
err ( " or it may be not responding. Report this to " EMAIL ) ;
return - 1 ;
} else {
PDEBUG ( 1 , " OV6xx0 sensor detected " ) ;
}
/* Detect sensor (sub)type */
rc = i2c_r ( ov , OV7610_REG_COM_I ) ;
if ( rc < 0 ) {
err ( " Error detecting sensor type " ) ;
return - 1 ;
}
if ( ( rc & 3 ) = = 0 ) {
ov - > sensor = SEN_OV6630 ;
info ( " Sensor is an OV6630 " ) ;
} else if ( ( rc & 3 ) = = 1 ) {
ov - > sensor = SEN_OV6620 ;
info ( " Sensor is an OV6620 " ) ;
} else if ( ( rc & 3 ) = = 2 ) {
ov - > sensor = SEN_OV6630 ;
info ( " Sensor is an OV6630AE " ) ;
} else if ( ( rc & 3 ) = = 3 ) {
ov - > sensor = SEN_OV6630 ;
info ( " Sensor is an OV6630AF " ) ;
}
/* Set sensor-specific vars */
ov - > maxwidth = 352 ;
ov - > maxheight = 288 ;
ov - > minwidth = 64 ;
ov - > minheight = 48 ;
// FIXME: These do not match the actual settings yet
ov - > brightness = 0x80 < < 8 ;
ov - > contrast = 0x80 < < 8 ;
ov - > colour = 0x80 < < 8 ;
ov - > hue = 0x80 < < 8 ;
if ( ov - > sensor = = SEN_OV6620 ) {
PDEBUG ( 4 , " Writing 6x20 registers " ) ;
if ( write_regvals ( ov , aRegvalsNorm6x20 ) )
return - 1 ;
} else {
PDEBUG ( 4 , " Writing 6x30 registers " ) ;
if ( write_regvals ( ov , aRegvalsNorm6x30 ) )
return - 1 ;
}
return 0 ;
}
/* This initializes the KS0127 and KS0127B video decoders. */
static int
ks0127_configure ( struct usb_ov511 * ov )
{
int rc ;
// FIXME: I don't know how to sync or reset it yet
#if 0
if ( ov51x_init_ks_sensor ( ov ) < 0 ) {
err ( " Failed to initialize the KS0127 " ) ;
return - 1 ;
} else {
PDEBUG ( 1 , " KS012x(B) sensor detected " ) ;
}
# endif
/* Detect decoder subtype */
rc = i2c_r ( ov , 0x00 ) ;
if ( rc < 0 ) {
err ( " Error detecting sensor type " ) ;
return - 1 ;
} else if ( rc & 0x08 ) {
rc = i2c_r ( ov , 0x3d ) ;
if ( rc < 0 ) {
err ( " Error detecting sensor type " ) ;
return - 1 ;
} else if ( ( rc & 0x0f ) = = 0 ) {
info ( " Sensor is a KS0127 " ) ;
ov - > sensor = SEN_KS0127 ;
} else if ( ( rc & 0x0f ) = = 9 ) {
info ( " Sensor is a KS0127B Rev. A " ) ;
ov - > sensor = SEN_KS0127B ;
}
} else {
err ( " Error: Sensor is an unsupported KS0122 " ) ;
return - 1 ;
}
/* Set sensor-specific vars */
ov - > maxwidth = 640 ;
ov - > maxheight = 480 ;
ov - > minwidth = 64 ;
ov - > minheight = 48 ;
// FIXME: These do not match the actual settings yet
ov - > brightness = 0x80 < < 8 ;
ov - > contrast = 0x80 < < 8 ;
ov - > colour = 0x80 < < 8 ;
ov - > hue = 0x80 < < 8 ;
/* This device is not supported yet. Bail out now... */
err ( " This sensor is not supported yet. " ) ;
return - 1 ;
return 0 ;
}
/* This initializes the SAA7111A video decoder. */
static int
saa7111a_configure ( struct usb_ov511 * ov )
{
int rc ;
/* Since there is no register reset command, all registers must be
* written , otherwise gives erratic results */
static struct ov511_regvals aRegvalsNormSAA7111A [ ] = {
{ OV511_I2C_BUS , 0x06 , 0xce } ,
{ OV511_I2C_BUS , 0x07 , 0x00 } ,
{ OV511_I2C_BUS , 0x10 , 0x44 } , /* YUV422, 240/286 lines */
{ OV511_I2C_BUS , 0x0e , 0x01 } , /* NTSC M or PAL BGHI */
{ OV511_I2C_BUS , 0x00 , 0x00 } ,
{ OV511_I2C_BUS , 0x01 , 0x00 } ,
{ OV511_I2C_BUS , 0x03 , 0x23 } ,
{ OV511_I2C_BUS , 0x04 , 0x00 } ,
{ OV511_I2C_BUS , 0x05 , 0x00 } ,
{ OV511_I2C_BUS , 0x08 , 0xc8 } , /* Auto field freq */
{ OV511_I2C_BUS , 0x09 , 0x01 } , /* Chrom. trap off, APER=0.25 */
{ OV511_I2C_BUS , 0x0a , 0x80 } , /* BRIG=128 */
{ OV511_I2C_BUS , 0x0b , 0x40 } , /* CONT=1.0 */
{ OV511_I2C_BUS , 0x0c , 0x40 } , /* SATN=1.0 */
{ OV511_I2C_BUS , 0x0d , 0x00 } , /* HUE=0 */
{ OV511_I2C_BUS , 0x0f , 0x00 } ,
{ OV511_I2C_BUS , 0x11 , 0x0c } ,
{ OV511_I2C_BUS , 0x12 , 0x00 } ,
{ OV511_I2C_BUS , 0x13 , 0x00 } ,
{ OV511_I2C_BUS , 0x14 , 0x00 } ,
{ OV511_I2C_BUS , 0x15 , 0x00 } ,
{ OV511_I2C_BUS , 0x16 , 0x00 } ,
{ OV511_I2C_BUS , 0x17 , 0x00 } ,
{ OV511_I2C_BUS , 0x02 , 0xc0 } , /* Composite input 0 */
{ OV511_DONE_BUS , 0x0 , 0x00 } ,
} ;
// FIXME: I don't know how to sync or reset it yet
#if 0
if ( ov51x_init_saa_sensor ( ov ) < 0 ) {
err ( " Failed to initialize the SAA7111A " ) ;
return - 1 ;
} else {
PDEBUG ( 1 , " SAA7111A sensor detected " ) ;
}
# endif
/* 640x480 not supported with PAL */
if ( ov - > pal ) {
ov - > maxwidth = 320 ;
ov - > maxheight = 240 ; /* Even field only */
} else {
ov - > maxwidth = 640 ;
ov - > maxheight = 480 ; /* Even/Odd fields */
}
ov - > minwidth = 320 ;
ov - > minheight = 240 ; /* Even field only */
ov - > has_decoder = 1 ;
ov - > num_inputs = 8 ;
ov - > norm = VIDEO_MODE_AUTO ;
ov - > stop_during_set = 0 ; /* Decoder guarantees stable image */
/* Decoder doesn't change these values, so we use these instead of
* acutally reading the registers ( which doesn ' t work ) */
ov - > brightness = 0x80 < < 8 ;
ov - > contrast = 0x40 < < 9 ;
ov - > colour = 0x40 < < 9 ;
ov - > hue = 32768 ;
PDEBUG ( 4 , " Writing SAA7111A registers " ) ;
if ( write_regvals ( ov , aRegvalsNormSAA7111A ) )
return - 1 ;
/* Detect version of decoder. This must be done after writing the
* initial regs or the decoder will lock up . */
rc = i2c_r ( ov , 0x00 ) ;
if ( rc < 0 ) {
err ( " Error detecting sensor version " ) ;
return - 1 ;
} else {
info ( " Sensor is an SAA7111A (version 0x%x) " , rc ) ;
ov - > sensor = SEN_SAA7111A ;
}
// FIXME: Fix this for OV518(+)
/* Latch to negative edge of clock. Otherwise, we get incorrect
* colors and jitter in the digital signal . */
if ( ov - > bclass = = BCL_OV511 )
reg_w ( ov , 0x11 , 0x00 ) ;
else
warn ( " SAA7111A not yet supported with OV518/OV518+ " ) ;
return 0 ;
}
/* This initializes the OV511/OV511+ and the sensor */
static int
ov511_configure ( struct usb_ov511 * ov )
{
static struct ov511_regvals aRegvalsInit511 [ ] = {
{ OV511_REG_BUS , R51x_SYS_RESET , 0x7f } ,
{ OV511_REG_BUS , R51x_SYS_INIT , 0x01 } ,
{ OV511_REG_BUS , R51x_SYS_RESET , 0x7f } ,
{ OV511_REG_BUS , R51x_SYS_INIT , 0x01 } ,
{ OV511_REG_BUS , R51x_SYS_RESET , 0x3f } ,
{ OV511_REG_BUS , R51x_SYS_INIT , 0x01 } ,
{ OV511_REG_BUS , R51x_SYS_RESET , 0x3d } ,
{ OV511_DONE_BUS , 0x0 , 0x00 } ,
} ;
static struct ov511_regvals aRegvalsNorm511 [ ] = {
{ OV511_REG_BUS , R511_DRAM_FLOW_CTL , 0x01 } ,
{ OV511_REG_BUS , R51x_SYS_SNAP , 0x00 } ,
{ OV511_REG_BUS , R51x_SYS_SNAP , 0x02 } ,
{ OV511_REG_BUS , R51x_SYS_SNAP , 0x00 } ,
{ OV511_REG_BUS , R511_FIFO_OPTS , 0x1f } ,
{ OV511_REG_BUS , R511_COMP_EN , 0x00 } ,
{ OV511_REG_BUS , R511_COMP_LUT_EN , 0x03 } ,
{ OV511_DONE_BUS , 0x0 , 0x00 } ,
} ;
static struct ov511_regvals aRegvalsNorm511Plus [ ] = {
{ OV511_REG_BUS , R511_DRAM_FLOW_CTL , 0xff } ,
{ OV511_REG_BUS , R51x_SYS_SNAP , 0x00 } ,
{ OV511_REG_BUS , R51x_SYS_SNAP , 0x02 } ,
{ OV511_REG_BUS , R51x_SYS_SNAP , 0x00 } ,
{ OV511_REG_BUS , R511_FIFO_OPTS , 0xff } ,
{ OV511_REG_BUS , R511_COMP_EN , 0x00 } ,
{ OV511_REG_BUS , R511_COMP_LUT_EN , 0x03 } ,
{ OV511_DONE_BUS , 0x0 , 0x00 } ,
} ;
PDEBUG ( 4 , " " ) ;
ov - > customid = reg_r ( ov , R511_SYS_CUST_ID ) ;
if ( ov - > customid < 0 ) {
err ( " Unable to read camera bridge registers " ) ;
goto error ;
}
PDEBUG ( 1 , " CustomID = %d " , ov - > customid ) ;
ov - > desc = symbolic ( camlist , ov - > customid ) ;
info ( " model: %s " , ov - > desc ) ;
if ( 0 = = strcmp ( ov - > desc , NOT_DEFINED_STR ) ) {
err ( " Camera type (%d) not recognized " , ov - > customid ) ;
err ( " Please notify " EMAIL " of the name, " ) ;
err ( " manufacturer, model, and this number of your camera. " ) ;
err ( " Also include the output of the detection process. " ) ;
}
if ( ov - > customid = = 70 ) /* USB Life TV (PAL/SECAM) */
ov - > pal = 1 ;
if ( write_regvals ( ov , aRegvalsInit511 ) )
goto error ;
if ( ov - > led_policy = = LED_OFF | | ov - > led_policy = = LED_AUTO )
ov51x_led_control ( ov , 0 ) ;
/* The OV511+ has undocumented bits in the flow control register.
* Setting it to 0xff fixes the corruption with moving objects . */
if ( ov - > bridge = = BRG_OV511 ) {
if ( write_regvals ( ov , aRegvalsNorm511 ) )
goto error ;
} else if ( ov - > bridge = = BRG_OV511PLUS ) {
if ( write_regvals ( ov , aRegvalsNorm511Plus ) )
goto error ;
} else {
err ( " Invalid bridge " ) ;
}
if ( ov511_init_compression ( ov ) )
goto error ;
ov - > packet_numbering = 1 ;
ov511_set_packet_size ( ov , 0 ) ;
ov - > snap_enabled = snapshot ;
/* Test for 7xx0 */
PDEBUG ( 3 , " Testing for 0V7xx0 " ) ;
ov - > primary_i2c_slave = OV7xx0_SID ;
if ( ov51x_set_slave_ids ( ov , OV7xx0_SID ) < 0 )
goto error ;
if ( i2c_w ( ov , 0x12 , 0x80 ) < 0 ) {
/* Test for 6xx0 */
PDEBUG ( 3 , " Testing for 0V6xx0 " ) ;
ov - > primary_i2c_slave = OV6xx0_SID ;
if ( ov51x_set_slave_ids ( ov , OV6xx0_SID ) < 0 )
goto error ;
if ( i2c_w ( ov , 0x12 , 0x80 ) < 0 ) {
/* Test for 8xx0 */
PDEBUG ( 3 , " Testing for 0V8xx0 " ) ;
ov - > primary_i2c_slave = OV8xx0_SID ;
if ( ov51x_set_slave_ids ( ov , OV8xx0_SID ) < 0 )
goto error ;
if ( i2c_w ( ov , 0x12 , 0x80 ) < 0 ) {
/* Test for SAA7111A */
PDEBUG ( 3 , " Testing for SAA7111A " ) ;
ov - > primary_i2c_slave = SAA7111A_SID ;
if ( ov51x_set_slave_ids ( ov , SAA7111A_SID ) < 0 )
goto error ;
if ( i2c_w ( ov , 0x0d , 0x00 ) < 0 ) {
/* Test for KS0127 */
PDEBUG ( 3 , " Testing for KS0127 " ) ;
ov - > primary_i2c_slave = KS0127_SID ;
if ( ov51x_set_slave_ids ( ov , KS0127_SID ) < 0 )
goto error ;
if ( i2c_w ( ov , 0x10 , 0x00 ) < 0 ) {
err ( " Can't determine sensor slave IDs " ) ;
goto error ;
} else {
if ( ks0127_configure ( ov ) < 0 ) {
err ( " Failed to configure KS0127 " ) ;
goto error ;
}
}
} else {
if ( saa7111a_configure ( ov ) < 0 ) {
err ( " Failed to configure SAA7111A " ) ;
goto error ;
}
}
} else {
err ( " Detected unsupported OV8xx0 sensor " ) ;
goto error ;
}
} else {
if ( ov6xx0_configure ( ov ) < 0 ) {
err ( " Failed to configure OV6xx0 " ) ;
goto error ;
}
}
} else {
if ( ov7xx0_configure ( ov ) < 0 ) {
err ( " Failed to configure OV7xx0 " ) ;
goto error ;
}
}
return 0 ;
error :
err ( " OV511 Config failed " ) ;
return - EBUSY ;
}
/* This initializes the OV518/OV518+ and the sensor */
static int
ov518_configure ( struct usb_ov511 * ov )
{
/* For 518 and 518+ */
static struct ov511_regvals aRegvalsInit518 [ ] = {
{ OV511_REG_BUS , R51x_SYS_RESET , 0x40 } ,
{ OV511_REG_BUS , R51x_SYS_INIT , 0xe1 } ,
{ OV511_REG_BUS , R51x_SYS_RESET , 0x3e } ,
{ OV511_REG_BUS , R51x_SYS_INIT , 0xe1 } ,
{ OV511_REG_BUS , R51x_SYS_RESET , 0x00 } ,
{ OV511_REG_BUS , R51x_SYS_INIT , 0xe1 } ,
{ OV511_REG_BUS , 0x46 , 0x00 } ,
{ OV511_REG_BUS , 0x5d , 0x03 } ,
{ OV511_DONE_BUS , 0x0 , 0x00 } ,
} ;
static struct ov511_regvals aRegvalsNorm518 [ ] = {
{ 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 , 0x00 } ,
{ OV511_REG_BUS , 0x51 , 0x04 } ,
{ OV511_REG_BUS , 0x71 , 0x19 } ,
{ 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 } ,
{ 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 , " " ) ;
/* 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 ) ) ;
/* Give it the default description */
ov - > desc = symbolic ( camlist , 0 ) ;
if ( write_regvals ( ov , aRegvalsInit518 ) )
goto error ;
/* Set LED GPIO pin to output mode */
if ( reg_w_mask ( ov , 0x57 , 0x00 , 0x02 ) < 0 )
goto error ;
/* LED is off by default with OV518; have to explicitly turn it on */
if ( ov - > led_policy = = LED_OFF | | ov - > led_policy = = LED_AUTO )
ov51x_led_control ( ov , 0 ) ;
else
ov51x_led_control ( ov , 1 ) ;
/* Don't require compression if dumppix is enabled; otherwise it's
* required . OV518 has no uncompressed mode , to save RAM . */
if ( ! dumppix & & ! ov - > compress ) {
ov - > compress = 1 ;
warn ( " Compression required with OV518...enabling " ) ;
}
if ( ov - > bridge = = BRG_OV518 ) {
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 ( ov518_init_compression ( ov ) )
goto error ;
if ( ov - > bridge = = BRG_OV518 )
{
struct usb_interface * ifp ;
struct usb_host_interface * alt ;
__u16 mxps = 0 ;
ifp = usb_ifnum_to_if ( ov - > dev , 0 ) ;
if ( ifp ) {
alt = usb_altnum_to_altsetting ( ifp , 7 ) ;
if ( alt )
mxps = le16_to_cpu ( alt - > endpoint [ 0 ] . desc . wMaxPacketSize ) ;
}
/* Some OV518s have packet numbering by default, some don't */
if ( mxps = = 897 )
ov - > packet_numbering = 1 ;
else
ov - > packet_numbering = 0 ;
} else {
/* OV518+ has packet numbering turned on by default */
ov - > packet_numbering = 1 ;
}
ov518_set_packet_size ( ov , 0 ) ;
ov - > snap_enabled = snapshot ;
/* Test for 76xx */
ov - > primary_i2c_slave = OV7xx0_SID ;
if ( ov51x_set_slave_ids ( ov , OV7xx0_SID ) < 0 )
goto error ;
/* The OV518 must be more aggressive about sensor detection since
* I2C write will never fail if the sensor is not present . We have
* to try to initialize the sensor to detect its presence */
if ( init_ov_sensor ( ov ) < 0 ) {
/* Test for 6xx0 */
ov - > primary_i2c_slave = OV6xx0_SID ;
if ( ov51x_set_slave_ids ( ov , OV6xx0_SID ) < 0 )
goto error ;
if ( init_ov_sensor ( ov ) < 0 ) {
/* Test for 8xx0 */
ov - > primary_i2c_slave = OV8xx0_SID ;
if ( ov51x_set_slave_ids ( ov , OV8xx0_SID ) < 0 )
goto error ;
if ( init_ov_sensor ( ov ) < 0 ) {
err ( " Can't determine sensor slave IDs " ) ;
goto error ;
} else {
err ( " Detected unsupported OV8xx0 sensor " ) ;
goto error ;
}
} else {
if ( ov6xx0_configure ( ov ) < 0 ) {
err ( " Failed to configure OV6xx0 " ) ;
goto error ;
}
}
} else {
if ( ov7xx0_configure ( ov ) < 0 ) {
err ( " Failed to configure OV7xx0 " ) ;
goto error ;
}
}
ov - > maxwidth = 352 ;
ov - > maxheight = 288 ;
// The OV518 cannot go as low as the sensor can
ov - > minwidth = 160 ;
ov - > minheight = 120 ;
return 0 ;
error :
err ( " OV518 Config failed " ) ;
return - EBUSY ;
}
/****************************************************************************
* sysfs
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static inline struct usb_ov511 * cd_to_ov ( struct class_device * cd )
{
struct video_device * vdev = to_video_device ( cd ) ;
return video_get_drvdata ( vdev ) ;
}
static ssize_t show_custom_id ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
return sprintf ( buf , " %d \n " , ov - > customid ) ;
}
static CLASS_DEVICE_ATTR ( custom_id , S_IRUGO , show_custom_id , NULL ) ;
static ssize_t show_model ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
return sprintf ( buf , " %s \n " , ov - > desc ) ;
}
static CLASS_DEVICE_ATTR ( model , S_IRUGO , show_model , NULL ) ;
static ssize_t show_bridge ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
return sprintf ( buf , " %s \n " , symbolic ( brglist , ov - > bridge ) ) ;
}
static CLASS_DEVICE_ATTR ( bridge , S_IRUGO , show_bridge , NULL ) ;
static ssize_t show_sensor ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
return sprintf ( buf , " %s \n " , symbolic ( senlist , ov - > sensor ) ) ;
}
static CLASS_DEVICE_ATTR ( sensor , S_IRUGO , show_sensor , NULL ) ;
static ssize_t show_brightness ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
unsigned short x ;
if ( ! ov - > dev )
return - ENODEV ;
sensor_get_brightness ( ov , & x ) ;
return sprintf ( buf , " %d \n " , x > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( brightness , S_IRUGO , show_brightness , NULL ) ;
static ssize_t show_saturation ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
unsigned short x ;
if ( ! ov - > dev )
return - ENODEV ;
sensor_get_saturation ( ov , & x ) ;
return sprintf ( buf , " %d \n " , x > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( saturation , S_IRUGO , show_saturation , NULL ) ;
static ssize_t show_contrast ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
unsigned short x ;
if ( ! ov - > dev )
return - ENODEV ;
sensor_get_contrast ( ov , & x ) ;
return sprintf ( buf , " %d \n " , x > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( contrast , S_IRUGO , show_contrast , NULL ) ;
static ssize_t show_hue ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
unsigned short x ;
if ( ! ov - > dev )
return - ENODEV ;
sensor_get_hue ( ov , & x ) ;
return sprintf ( buf , " %d \n " , x > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( hue , S_IRUGO , show_hue , NULL ) ;
static ssize_t show_exposure ( struct class_device * cd , char * buf )
{
struct usb_ov511 * ov = cd_to_ov ( cd ) ;
unsigned char exp ;
if ( ! ov - > dev )
return - ENODEV ;
sensor_get_exposure ( ov , & exp ) ;
return sprintf ( buf , " %d \n " , exp > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( exposure , S_IRUGO , show_exposure , NULL ) ;
static void ov_create_sysfs ( struct video_device * vdev )
{
video_device_create_file ( vdev , & class_device_attr_custom_id ) ;
video_device_create_file ( vdev , & class_device_attr_model ) ;
video_device_create_file ( vdev , & class_device_attr_bridge ) ;
video_device_create_file ( vdev , & class_device_attr_sensor ) ;
video_device_create_file ( vdev , & class_device_attr_brightness ) ;
video_device_create_file ( vdev , & class_device_attr_saturation ) ;
video_device_create_file ( vdev , & class_device_attr_contrast ) ;
video_device_create_file ( vdev , & class_device_attr_hue ) ;
video_device_create_file ( vdev , & class_device_attr_exposure ) ;
}
/****************************************************************************
* USB routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int
ov51x_probe ( struct usb_interface * intf , const struct usb_device_id * id )
{
struct usb_device * dev = interface_to_usbdev ( intf ) ;
struct usb_interface_descriptor * idesc ;
struct usb_ov511 * ov ;
int i ;
PDEBUG ( 1 , " probing for device... " ) ;
/* We don't handle multi-config cameras */
if ( dev - > descriptor . bNumConfigurations ! = 1 )
return - ENODEV ;
idesc = & intf - > cur_altsetting - > desc ;
if ( idesc - > bInterfaceClass ! = 0xFF )
return - ENODEV ;
if ( idesc - > bInterfaceSubClass ! = 0x00 )
return - ENODEV ;
if ( ( ov = kmalloc ( sizeof ( * ov ) , GFP_KERNEL ) ) = = NULL ) {
err ( " couldn't kmalloc ov struct " ) ;
goto error_out ;
}
memset ( ov , 0 , sizeof ( * ov ) ) ;
ov - > dev = dev ;
ov - > iface = idesc - > bInterfaceNumber ;
ov - > led_policy = led ;
ov - > compress = compress ;
ov - > lightfreq = lightfreq ;
ov - > num_inputs = 1 ; /* Video decoder init functs. change this */
ov - > stop_during_set = ! fastset ;
ov - > backlight = backlight ;
ov - > mirror = mirror ;
ov - > auto_brt = autobright ;
ov - > auto_gain = autogain ;
ov - > auto_exp = autoexp ;
switch ( le16_to_cpu ( dev - > descriptor . idProduct ) ) {
case PROD_OV511 :
ov - > bridge = BRG_OV511 ;
ov - > bclass = BCL_OV511 ;
break ;
case PROD_OV511PLUS :
ov - > bridge = BRG_OV511PLUS ;
ov - > bclass = BCL_OV511 ;
break ;
case PROD_OV518 :
ov - > bridge = BRG_OV518 ;
ov - > bclass = BCL_OV518 ;
break ;
case PROD_OV518PLUS :
ov - > bridge = BRG_OV518PLUS ;
ov - > bclass = BCL_OV518 ;
break ;
case PROD_ME2CAM :
if ( le16_to_cpu ( dev - > descriptor . idVendor ) ! = VEND_MATTEL )
goto error ;
ov - > bridge = BRG_OV511PLUS ;
ov - > bclass = BCL_OV511 ;
break ;
default :
err ( " Unknown product ID 0x%04x " , le16_to_cpu ( dev - > descriptor . idProduct ) ) ;
goto error ;
}
info ( " USB %s video device found " , symbolic ( brglist , ov - > bridge ) ) ;
init_waitqueue_head ( & ov - > wq ) ;
init_MUTEX ( & ov - > lock ) ; /* to 1 == available */
init_MUTEX ( & ov - > buf_lock ) ;
init_MUTEX ( & ov - > param_lock ) ;
init_MUTEX ( & ov - > i2c_lock ) ;
init_MUTEX ( & ov - > cbuf_lock ) ;
ov - > buf_state = BUF_NOT_ALLOCATED ;
if ( usb_make_path ( dev , ov - > usb_path , OV511_USB_PATH_LEN ) < 0 ) {
err ( " usb_make_path error " ) ;
goto error ;
}
/* Allocate control transfer buffer. */
/* Must be kmalloc()'ed, for DMA compatibility */
ov - > cbuf = kmalloc ( OV511_CBUF_SIZE , GFP_KERNEL ) ;
if ( ! ov - > cbuf )
goto error ;
if ( ov - > bclass = = BCL_OV518 ) {
if ( ov518_configure ( ov ) < 0 )
goto error ;
} else {
if ( ov511_configure ( ov ) < 0 )
goto error ;
}
for ( i = 0 ; i < OV511_NUMFRAMES ; i + + ) {
ov - > frame [ i ] . framenum = i ;
init_waitqueue_head ( & ov - > frame [ i ] . wq ) ;
}
for ( i = 0 ; i < OV511_NUMSBUF ; i + + ) {
ov - > sbuf [ i ] . ov = ov ;
spin_lock_init ( & ov - > sbuf [ i ] . lock ) ;
ov - > sbuf [ i ] . n = i ;
}
/* Unnecessary? (This is done on open(). Need to make sure variables
* are properly initialized without this before removing it , though ) . */
if ( ov51x_set_default_params ( ov ) < 0 )
goto error ;
# ifdef OV511_DEBUG
if ( dump_bridge ) {
if ( ov - > bclass = = BCL_OV511 )
ov511_dump_regs ( ov ) ;
else
ov518_dump_regs ( ov ) ;
}
# endif
ov - > vdev = video_device_alloc ( ) ;
if ( ! ov - > vdev )
goto error ;
memcpy ( ov - > vdev , & vdev_template , sizeof ( * ov - > vdev ) ) ;
ov - > vdev - > dev = & dev - > dev ;
video_set_drvdata ( ov - > vdev , ov ) ;
for ( i = 0 ; i < OV511_MAX_UNIT_VIDEO ; i + + ) {
/* Minor 0 cannot be specified; assume user wants autodetect */
if ( unit_video [ i ] = = 0 )
break ;
if ( video_register_device ( ov - > vdev , VFL_TYPE_GRABBER ,
unit_video [ i ] ) > = 0 ) {
break ;
}
}
/* Use the next available one */
if ( ( ov - > vdev - > minor = = - 1 ) & &
video_register_device ( ov - > vdev , VFL_TYPE_GRABBER , - 1 ) < 0 ) {
err ( " video_register_device failed " ) ;
goto error ;
}
info ( " Device at %s registered to minor %d " , ov - > usb_path ,
ov - > vdev - > minor ) ;
usb_set_intfdata ( intf , ov ) ;
ov_create_sysfs ( ov - > vdev ) ;
return 0 ;
error :
if ( ov - > vdev ) {
if ( - 1 = = ov - > vdev - > minor )
video_device_release ( ov - > vdev ) ;
else
video_unregister_device ( ov - > vdev ) ;
ov - > vdev = NULL ;
}
if ( ov - > cbuf ) {
down ( & ov - > cbuf_lock ) ;
kfree ( ov - > cbuf ) ;
ov - > cbuf = NULL ;
up ( & ov - > cbuf_lock ) ;
}
2005-04-18 17:39:34 -07:00
kfree ( ov ) ;
ov = NULL ;
2005-04-16 15:20:36 -07:00
error_out :
err ( " Camera initialization failed " ) ;
return - EIO ;
}
static void
ov51x_disconnect ( struct usb_interface * intf )
{
struct usb_ov511 * ov = usb_get_intfdata ( intf ) ;
int n ;
PDEBUG ( 3 , " " ) ;
usb_set_intfdata ( intf , NULL ) ;
if ( ! ov )
return ;
if ( ov - > vdev )
video_unregister_device ( ov - > vdev ) ;
for ( n = 0 ; n < OV511_NUMFRAMES ; n + + )
ov - > frame [ n ] . grabstate = FRAME_ERROR ;
ov - > curframe = - 1 ;
/* This will cause the process to request another frame */
for ( n = 0 ; n < OV511_NUMFRAMES ; n + + )
wake_up_interruptible ( & ov - > frame [ n ] . wq ) ;
wake_up_interruptible ( & ov - > wq ) ;
ov - > streaming = 0 ;
ov51x_unlink_isoc ( ov ) ;
ov - > dev = NULL ;
/* Free the memory */
if ( ov & & ! ov - > user ) {
down ( & ov - > cbuf_lock ) ;
kfree ( ov - > cbuf ) ;
ov - > cbuf = NULL ;
up ( & ov - > cbuf_lock ) ;
ov51x_dealloc ( ov ) ;
kfree ( ov ) ;
ov = NULL ;
}
PDEBUG ( 3 , " Disconnect complete " ) ;
}
static struct usb_driver ov511_driver = {
. owner = THIS_MODULE ,
. name = " ov511 " ,
. id_table = device_table ,
. probe = ov51x_probe ,
. disconnect = ov51x_disconnect
} ;
/****************************************************************************
*
* Module routines
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Returns 0 for success */
int
ov511_register_decomp_module ( int ver , struct ov51x_decomp_ops * ops , int ov518 ,
int mmx )
{
if ( ver ! = DECOMP_INTERFACE_VER ) {
err ( " Decompression module has incompatible " ) ;
err ( " interface version %d " , ver ) ;
err ( " Interface version %d is required " , DECOMP_INTERFACE_VER ) ;
return - EINVAL ;
}
if ( ! ops )
return - EFAULT ;
if ( mmx & & ! ov51x_mmx_available ) {
err ( " MMX not available on this system or kernel " ) ;
return - EINVAL ;
}
lock_kernel ( ) ;
if ( ov518 ) {
if ( mmx ) {
if ( ov518_mmx_decomp_ops )
goto err_in_use ;
else
ov518_mmx_decomp_ops = ops ;
} else {
if ( ov518_decomp_ops )
goto err_in_use ;
else
ov518_decomp_ops = ops ;
}
} else {
if ( mmx ) {
if ( ov511_mmx_decomp_ops )
goto err_in_use ;
else
ov511_mmx_decomp_ops = ops ;
} else {
if ( ov511_decomp_ops )
goto err_in_use ;
else
ov511_decomp_ops = ops ;
}
}
unlock_kernel ( ) ;
return 0 ;
err_in_use :
unlock_kernel ( ) ;
return - EBUSY ;
}
void
ov511_deregister_decomp_module ( int ov518 , int mmx )
{
lock_kernel ( ) ;
if ( ov518 ) {
if ( mmx )
ov518_mmx_decomp_ops = NULL ;
else
ov518_decomp_ops = NULL ;
} else {
if ( mmx )
ov511_mmx_decomp_ops = NULL ;
else
ov511_decomp_ops = NULL ;
}
unlock_kernel ( ) ;
}
static int __init
usb_ov511_init ( void )
{
int retval ;
retval = usb_register ( & ov511_driver ) ;
if ( retval )
goto out ;
info ( DRIVER_VERSION " : " DRIVER_DESC ) ;
out :
return retval ;
}
static void __exit
usb_ov511_exit ( void )
{
usb_deregister ( & ov511_driver ) ;
info ( " driver deregistered " ) ;
}
module_init ( usb_ov511_init ) ;
module_exit ( usb_ov511_exit ) ;
EXPORT_SYMBOL ( ov511_register_decomp_module ) ;
EXPORT_SYMBOL ( ov511_deregister_decomp_module ) ;