2005-04-16 15:20:36 -07:00
/* Shared Code for OmniVision Camera Chip Drivers
*
* Copyright ( c ) 2004 Mark McClelland < mark @ alpha . dyndns . org >
* http : //alpha.dyndns.org/ov511/
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version . NO WARRANTY OF ANY KIND is expressed or implied .
*/
# define DEBUG
# include <linux/init.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/delay.h>
2009-03-08 10:19:44 -03:00
# include <linux/i2c.h>
# include <media/v4l2-device.h>
# include <media/v4l2-i2c-drv.h>
2005-04-16 15:20:36 -07:00
# include "ovcamchip_priv.h"
# define DRIVER_VERSION "v2.27 for Linux 2.6"
# define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org>"
# define DRIVER_DESC "OV camera chip I2C driver"
# define PINFO(fmt, args...) printk(KERN_INFO "ovcamchip: " fmt "\n" , ## args);
# define PERROR(fmt, args...) printk(KERN_ERR "ovcamchip: " fmt "\n" , ## args);
# ifdef DEBUG
int ovcamchip_debug = 0 ;
static int debug ;
module_param ( debug , int , 0 ) ;
MODULE_PARM_DESC ( debug ,
" Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=all " ) ;
# endif
/* By default, let bridge driver tell us if chip is monochrome. mono=0
* will ignore that and always treat chips as color . mono = 1 will force
* monochrome mode for all chips . */
static int mono = - 1 ;
module_param ( mono , int , 0 ) ;
MODULE_PARM_DESC ( mono ,
" 1=chips are monochrome (OVx1xx), 0=force color, -1=autodetect (default) " ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-03-08 10:19:44 -03:00
2005-04-16 15:20:36 -07:00
/* Registers common to all chips, that are needed for detection */
# define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */
# define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */
# define GENERIC_REG_COM_I 0x29 /* misc ID bits */
static char * chip_names [ NUM_CC_TYPES ] = {
[ CC_UNKNOWN ] = " Unknown chip " ,
[ CC_OV76BE ] = " OV76BE " ,
[ CC_OV7610 ] = " OV7610 " ,
[ CC_OV7620 ] = " OV7620 " ,
[ CC_OV7620AE ] = " OV7620AE " ,
[ CC_OV6620 ] = " OV6620 " ,
[ CC_OV6630 ] = " OV6630 " ,
[ CC_OV6630AE ] = " OV6630AE " ,
[ CC_OV6630AF ] = " OV6630AF " ,
} ;
/* ----------------------------------------------------------------------- */
int ov_write_regvals ( struct i2c_client * c , struct ovcamchip_regvals * rvals )
{
int rc ;
while ( rvals - > reg ! = 0xff ) {
rc = ov_write ( c , rvals - > reg , rvals - > val ) ;
if ( rc < 0 )
return rc ;
rvals + + ;
}
return 0 ;
}
/* 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 " .
*/
int ov_write_mask ( struct i2c_client * c ,
unsigned char reg ,
unsigned char value ,
unsigned char mask )
{
int rc ;
unsigned char oldval , newval ;
if ( mask = = 0xff ) {
newval = value ;
} else {
rc = ov_read ( c , reg , & oldval ) ;
if ( rc < 0 )
return rc ;
oldval & = ( ~ mask ) ; /* Clear the masked bits */
value & = mask ; /* Enforce mask on value */
newval = oldval | value ; /* Set the desired bits */
}
return ov_write ( c , reg , newval ) ;
}
/* ----------------------------------------------------------------------- */
/* Reset the chip and ensure that I2C is synchronized. Returns <0 if failure.
*/
static int init_camchip ( struct i2c_client * c )
{
int i , success ;
unsigned char high , low ;
/* Reset the chip */
ov_write ( c , 0x12 , 0x80 ) ;
/* Wait for it to initialize */
msleep ( 150 ) ;
for ( i = 0 , success = 0 ; i < I2C_DETECT_RETRIES & & ! success ; i + + ) {
if ( ov_read ( c , GENERIC_REG_ID_HIGH , & high ) > = 0 ) {
if ( ov_read ( c , GENERIC_REG_ID_LOW , & low ) > = 0 ) {
if ( high = = 0x7F & & low = = 0xA2 ) {
success = 1 ;
continue ;
}
}
}
/* Reset the chip */
ov_write ( c , 0x12 , 0x80 ) ;
/* Wait for it to initialize */
msleep ( 150 ) ;
/* Dummy read to sync I2C */
ov_read ( c , 0x00 , & low ) ;
}
if ( ! success )
return - EIO ;
PDEBUG ( 1 , " I2C synced in %d attempt(s) " , i ) ;
return 0 ;
}
/* This detects the OV7610, OV7620, or OV76BE chip. */
static int ov7xx0_detect ( struct i2c_client * c )
{
struct ovcamchip * ov = i2c_get_clientdata ( c ) ;
int rc ;
unsigned char val ;
PDEBUG ( 4 , " " ) ;
/* Detect chip (sub)type */
rc = ov_read ( c , GENERIC_REG_COM_I , & val ) ;
if ( rc < 0 ) {
PERROR ( " Error detecting ov7xx0 type " ) ;
return rc ;
}
if ( ( val & 3 ) = = 3 ) {
PINFO ( " Camera chip is an OV7610 " ) ;
ov - > subtype = CC_OV7610 ;
} else if ( ( val & 3 ) = = 1 ) {
rc = ov_read ( c , 0x15 , & val ) ;
if ( rc < 0 ) {
PERROR ( " Error detecting ov7xx0 type " ) ;
return rc ;
}
if ( val & 1 ) {
PINFO ( " Camera chip is an OV7620AE " ) ;
/* OV7620 is a close enough match for now. There are
* some definite differences though , so this should be
* fixed */
ov - > subtype = CC_OV7620 ;
} else {
PINFO ( " Camera chip is an OV76BE " ) ;
ov - > subtype = CC_OV76BE ;
}
} else if ( ( val & 3 ) = = 0 ) {
PINFO ( " Camera chip is an OV7620 " ) ;
ov - > subtype = CC_OV7620 ;
} else {
PERROR ( " Unknown camera chip version: %d " , val & 3 ) ;
return - ENOSYS ;
}
if ( ov - > subtype = = CC_OV76BE )
ov - > sops = & ov76be_ops ;
else if ( ov - > subtype = = CC_OV7620 )
ov - > sops = & ov7x20_ops ;
else
ov - > sops = & ov7x10_ops ;
return 0 ;
}
/* This detects the OV6620, OV6630, OV6630AE, or OV6630AF chip. */
static int ov6xx0_detect ( struct i2c_client * c )
{
struct ovcamchip * ov = i2c_get_clientdata ( c ) ;
int rc ;
unsigned char val ;
PDEBUG ( 4 , " " ) ;
/* Detect chip (sub)type */
rc = ov_read ( c , GENERIC_REG_COM_I , & val ) ;
if ( rc < 0 ) {
PERROR ( " Error detecting ov6xx0 type " ) ;
return - 1 ;
}
if ( ( val & 3 ) = = 0 ) {
ov - > subtype = CC_OV6630 ;
PINFO ( " Camera chip is an OV6630 " ) ;
} else if ( ( val & 3 ) = = 1 ) {
ov - > subtype = CC_OV6620 ;
PINFO ( " Camera chip is an OV6620 " ) ;
} else if ( ( val & 3 ) = = 2 ) {
ov - > subtype = CC_OV6630 ;
PINFO ( " Camera chip is an OV6630AE " ) ;
} else if ( ( val & 3 ) = = 3 ) {
ov - > subtype = CC_OV6630 ;
PINFO ( " Camera chip is an OV6630AF " ) ;
}
if ( ov - > subtype = = CC_OV6620 )
ov - > sops = & ov6x20_ops ;
else
ov - > sops = & ov6x30_ops ;
return 0 ;
}
static int ovcamchip_detect ( struct i2c_client * c )
{
/* Ideally we would just try a single register write and see if it NAKs.
* That isn ' t possible since the OV518 can ' t report I2C transaction
* failures . So , we have to try to initialize the chip ( i . e . reset it
* and check the ID registers ) to detect its presence . */
/* Test for 7xx0 */
PDEBUG ( 3 , " Testing for 0V7xx0 " ) ;
2009-03-08 10:19:44 -03:00
if ( init_camchip ( c ) < 0 )
return - ENODEV ;
/* 7-bit addresses with bit 0 set are for the OV7xx0 */
if ( c - > addr & 1 ) {
2005-04-16 15:20:36 -07:00
if ( ov7xx0_detect ( c ) < 0 ) {
PERROR ( " Failed to init OV7xx0 " ) ;
2006-03-25 09:19:53 -03:00
return - EIO ;
2005-04-16 15:20:36 -07:00
}
2009-03-08 10:19:44 -03:00
return 0 ;
}
/* Test for 6xx0 */
PDEBUG ( 3 , " Testing for 0V6xx0 " ) ;
if ( ov6xx0_detect ( c ) < 0 ) {
PERROR ( " Failed to init OV6xx0 " ) ;
return - EIO ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
/* ----------------------------------------------------------------------- */
2009-03-08 10:19:44 -03:00
static long ovcamchip_ioctl ( struct v4l2_subdev * sd , unsigned int cmd , void * arg )
2005-04-16 15:20:36 -07:00
{
2009-03-08 10:19:44 -03:00
struct ovcamchip * ov = to_ovcamchip ( sd ) ;
struct i2c_client * c = v4l2_get_subdevdata ( sd ) ;
2005-04-16 15:20:36 -07:00
if ( ! ov - > initialized & &
cmd ! = OVCAMCHIP_CMD_Q_SUBTYPE & &
cmd ! = OVCAMCHIP_CMD_INITIALIZE ) {
2009-03-08 10:19:44 -03:00
v4l2_err ( sd , " Camera chip not initialized yet! \n " ) ;
2005-04-16 15:20:36 -07:00
return - EPERM ;
}
switch ( cmd ) {
case OVCAMCHIP_CMD_Q_SUBTYPE :
{
* ( int * ) arg = ov - > subtype ;
return 0 ;
}
case OVCAMCHIP_CMD_INITIALIZE :
{
int rc ;
if ( mono = = - 1 )
ov - > mono = * ( int * ) arg ;
else
ov - > mono = mono ;
if ( ov - > mono ) {
if ( ov - > subtype ! = CC_OV7620 )
2009-03-08 10:19:44 -03:00
v4l2_warn ( sd , " Monochrome not "
2005-04-16 15:20:36 -07:00
" implemented for this chip \n " ) ;
else
2009-03-08 10:19:44 -03:00
v4l2_info ( sd , " Initializing chip as "
2005-04-16 15:20:36 -07:00
" monochrome \n " ) ;
}
rc = ov - > sops - > init ( c ) ;
if ( rc < 0 )
return rc ;
ov - > initialized = 1 ;
return 0 ;
}
default :
return ov - > sops - > command ( c , cmd , arg ) ;
}
}
/* ----------------------------------------------------------------------- */
2009-03-08 10:19:44 -03:00
static const struct v4l2_subdev_core_ops ovcamchip_core_ops = {
. ioctl = ovcamchip_ioctl ,
2005-04-16 15:20:36 -07:00
} ;
2009-03-08 10:19:44 -03:00
static const struct v4l2_subdev_ops ovcamchip_ops = {
. core = & ovcamchip_core_ops ,
2005-04-16 15:20:36 -07:00
} ;
2009-03-08 10:19:44 -03:00
static int ovcamchip_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-04-16 15:20:36 -07:00
{
2009-03-08 10:19:44 -03:00
struct ovcamchip * ov ;
struct v4l2_subdev * sd ;
int rc = 0 ;
2005-04-16 15:20:36 -07:00
2009-03-08 10:19:44 -03:00
ov = kzalloc ( sizeof * ov , GFP_KERNEL ) ;
if ( ! ov ) {
rc = - ENOMEM ;
goto no_ov ;
}
sd = & ov - > sd ;
v4l2_i2c_subdev_init ( sd , client , & ovcamchip_ops ) ;
rc = ovcamchip_detect ( client ) ;
if ( rc < 0 )
goto error ;
v4l_info ( client , " %s found @ 0x%02x (%s) \n " ,
chip_names [ ov - > subtype ] , client - > addr < < 1 , client - > adapter - > name ) ;
PDEBUG ( 1 , " Camera chip detection complete " ) ;
return rc ;
error :
kfree ( ov ) ;
no_ov :
PDEBUG ( 1 , " returning %d " , rc ) ;
return rc ;
2005-04-16 15:20:36 -07:00
}
2009-03-08 10:19:44 -03:00
static int ovcamchip_remove ( struct i2c_client * client )
2005-04-16 15:20:36 -07:00
{
2009-03-08 10:19:44 -03:00
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct ovcamchip * ov = to_ovcamchip ( sd ) ;
int rc ;
v4l2_device_unregister_subdev ( sd ) ;
rc = ov - > sops - > free ( client ) ;
if ( rc < 0 )
return rc ;
kfree ( ov ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2009-03-08 10:19:44 -03:00
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id ovcamchip_id [ ] = {
{ " ovcamchip " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ovcamchip_id ) ;
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
. name = " ovcamchip " ,
. probe = ovcamchip_probe ,
. remove = ovcamchip_remove ,
. id_table = ovcamchip_id ,
} ;