2005-09-07 02:19:37 +04:00
/*
* indycam . c - Silicon Graphics IndyCam digital camera driver
*
* Copyright ( C ) 2003 Ladislav Michl < ladis @ linux - mips . org >
* Copyright ( C ) 2004 , 2005 Mikael Nousiainen < tmnousia @ cc . hut . fi >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/fs.h>
2005-09-23 14:52:27 +04:00
# include <linux/init.h>
2005-09-07 02:19:37 +04:00
# include <linux/kernel.h>
# include <linux/major.h>
2005-09-23 14:52:27 +04:00
# include <linux/module.h>
2005-09-07 02:19:37 +04:00
# include <linux/mm.h>
2005-09-23 14:52:27 +04:00
# include <linux/slab.h>
2005-09-07 02:19:37 +04:00
/* IndyCam decodes stream of photons into digital image representation ;-) */
2009-02-27 15:05:10 +03:00
# include <linux/videodev2.h>
2005-09-07 02:19:37 +04:00
# include <linux/i2c.h>
2009-02-14 01:38:10 +03:00
# include <media/v4l2-device.h>
# include <media/v4l2-chip-ident.h>
2005-09-07 02:19:37 +04:00
# include "indycam.h"
2005-09-01 19:07:34 +04:00
# define INDYCAM_MODULE_VERSION "0.0.5"
2005-09-07 02:19:37 +04:00
MODULE_DESCRIPTION ( " SGI IndyCam driver " ) ;
MODULE_VERSION ( INDYCAM_MODULE_VERSION ) ;
MODULE_AUTHOR ( " Mikael Nousiainen <tmnousia@cc.hut.fi> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-02-27 15:05:10 +03:00
2005-09-01 19:07:34 +04:00
// #define INDYCAM_DEBUG
2005-09-07 02:19:37 +04:00
# ifdef INDYCAM_DEBUG
# define dprintk(x...) printk("IndyCam: " x);
# define indycam_regdump(client) indycam_regdump_debug(client)
# else
# define dprintk(x...)
# define indycam_regdump(client)
# endif
struct indycam {
2009-02-14 01:38:10 +03:00
struct v4l2_subdev sd ;
2005-09-01 19:07:34 +04:00
u8 version ;
2005-09-07 02:19:37 +04:00
} ;
2009-02-14 01:38:10 +03:00
static inline struct indycam * to_indycam ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct indycam , sd ) ;
}
2005-09-01 19:07:34 +04:00
static const u8 initseq [ ] = {
2005-09-07 02:19:37 +04:00
INDYCAM_CONTROL_AGCENA , /* INDYCAM_CONTROL */
2005-09-01 19:07:34 +04:00
INDYCAM_SHUTTER_60 , /* INDYCAM_SHUTTER */
2005-09-07 02:19:37 +04:00
INDYCAM_GAIN_DEFAULT , /* INDYCAM_GAIN */
0x00 , /* INDYCAM_BRIGHTNESS (read-only) */
INDYCAM_RED_BALANCE_DEFAULT , /* INDYCAM_RED_BALANCE */
INDYCAM_BLUE_BALANCE_DEFAULT , /* INDYCAM_BLUE_BALANCE */
INDYCAM_RED_SATURATION_DEFAULT , /* INDYCAM_RED_SATURATION */
INDYCAM_BLUE_SATURATION_DEFAULT , /* INDYCAM_BLUE_SATURATION */
} ;
/* IndyCam register handling */
2009-02-14 01:38:10 +03:00
static int indycam_read_reg ( struct v4l2_subdev * sd , u8 reg , u8 * value )
2005-09-07 02:19:37 +04:00
{
2009-02-14 01:38:10 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-09-07 02:19:37 +04:00
int ret ;
2005-09-01 19:07:34 +04:00
if ( reg = = INDYCAM_REG_RESET ) {
2005-09-07 02:19:37 +04:00
dprintk ( " indycam_read_reg(): "
" skipping write-only register %d \n " , reg ) ;
* value = 0 ;
return 0 ;
}
ret = i2c_smbus_read_byte_data ( client , reg ) ;
2005-09-01 19:07:34 +04:00
2005-09-07 02:19:37 +04:00
if ( ret < 0 ) {
printk ( KERN_ERR " IndyCam: indycam_read_reg(): read failed, "
" register = 0x%02x \n " , reg ) ;
return ret ;
}
2005-09-01 19:07:34 +04:00
* value = ( u8 ) ret ;
2005-09-07 02:19:37 +04:00
return 0 ;
}
2009-02-14 01:38:10 +03:00
static int indycam_write_reg ( struct v4l2_subdev * sd , u8 reg , u8 value )
2005-09-07 02:19:37 +04:00
{
2009-02-14 01:38:10 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-09-07 02:19:37 +04:00
int err ;
2009-02-14 01:38:10 +03:00
if ( reg = = INDYCAM_REG_BRIGHTNESS | | reg = = INDYCAM_REG_VERSION ) {
2005-09-07 02:19:37 +04:00
dprintk ( " indycam_write_reg(): "
" skipping read-only register %d \n " , reg ) ;
return 0 ;
}
dprintk ( " Writing Reg %d = 0x%02x \n " , reg , value ) ;
err = i2c_smbus_write_byte_data ( client , reg , value ) ;
2005-09-01 19:07:34 +04:00
2005-09-07 02:19:37 +04:00
if ( err ) {
printk ( KERN_ERR " IndyCam: indycam_write_reg(): write failed, "
" register = 0x%02x, value = 0x%02x \n " , reg , value ) ;
}
return err ;
}
2009-02-14 01:38:10 +03:00
static int indycam_write_block ( struct v4l2_subdev * sd , u8 reg ,
2005-09-01 19:07:34 +04:00
u8 length , u8 * data )
2005-09-07 02:19:37 +04:00
{
2005-09-01 19:07:34 +04:00
int i , err ;
2005-09-07 02:19:37 +04:00
2005-09-01 19:07:34 +04:00
for ( i = 0 ; i < length ; i + + ) {
2009-02-14 01:38:10 +03:00
err = indycam_write_reg ( sd , reg + i , data [ i ] ) ;
2005-09-07 02:19:37 +04:00
if ( err )
return err ;
}
return 0 ;
}
/* Helper functions */
# ifdef INDYCAM_DEBUG
2009-02-14 01:38:10 +03:00
static void indycam_regdump_debug ( struct v4l2_subdev * sd )
2005-09-07 02:19:37 +04:00
{
int i ;
2005-09-01 19:07:34 +04:00
u8 val ;
2005-09-07 02:19:37 +04:00
for ( i = 0 ; i < 9 ; i + + ) {
2009-02-14 01:38:10 +03:00
indycam_read_reg ( sd , i , & val ) ;
2005-09-07 02:19:37 +04:00
dprintk ( " Reg %d = 0x%02x \n " , i , val ) ;
}
}
# endif
2009-02-14 01:38:10 +03:00
static int indycam_g_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2005-09-07 02:19:37 +04:00
{
2009-02-14 01:38:10 +03:00
struct indycam * camera = to_indycam ( sd ) ;
2005-09-01 19:07:34 +04:00
u8 reg ;
int ret = 0 ;
2009-02-27 15:05:10 +03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUTOGAIN :
case V4L2_CID_AUTO_WHITE_BALANCE :
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd , INDYCAM_REG_CONTROL , & reg ) ;
2005-09-01 19:07:34 +04:00
if ( ret )
return - EIO ;
2009-02-27 15:05:10 +03:00
if ( ctrl - > id = = V4L2_CID_AUTOGAIN )
2005-09-01 19:07:34 +04:00
ctrl - > value = ( reg & INDYCAM_CONTROL_AGCENA )
? 1 : 0 ;
else
ctrl - > value = ( reg & INDYCAM_CONTROL_AWBCTL )
? 1 : 0 ;
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_EXPOSURE :
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd , INDYCAM_REG_SHUTTER , & reg ) ;
2005-09-01 19:07:34 +04:00
if ( ret )
return - EIO ;
ctrl - > value = ( ( s32 ) reg = = 0x00 ) ? 0xff : ( ( s32 ) reg - 1 ) ;
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_GAIN :
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd , INDYCAM_REG_GAIN , & reg ) ;
2005-09-01 19:07:34 +04:00
if ( ret )
return - EIO ;
ctrl - > value = ( s32 ) reg ;
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_RED_BALANCE :
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd , INDYCAM_REG_RED_BALANCE , & reg ) ;
2005-09-01 19:07:34 +04:00
if ( ret )
return - EIO ;
ctrl - > value = ( s32 ) reg ;
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_BLUE_BALANCE :
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd , INDYCAM_REG_BLUE_BALANCE , & reg ) ;
2005-09-01 19:07:34 +04:00
if ( ret )
return - EIO ;
ctrl - > value = ( s32 ) reg ;
break ;
case INDYCAM_CONTROL_RED_SATURATION :
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd ,
2005-09-01 19:07:34 +04:00
INDYCAM_REG_RED_SATURATION , & reg ) ;
if ( ret )
return - EIO ;
ctrl - > value = ( s32 ) reg ;
break ;
case INDYCAM_CONTROL_BLUE_SATURATION :
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd ,
2005-09-01 19:07:34 +04:00
INDYCAM_REG_BLUE_SATURATION , & reg ) ;
if ( ret )
return - EIO ;
ctrl - > value = ( s32 ) reg ;
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_GAMMA :
2005-09-01 19:07:34 +04:00
if ( camera - > version = = CAMERA_VERSION_MOOSE ) {
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd ,
2005-09-01 19:07:34 +04:00
INDYCAM_REG_GAMMA , & reg ) ;
if ( ret )
return - EIO ;
ctrl - > value = ( s32 ) reg ;
} else {
ctrl - > value = INDYCAM_GAMMA_DEFAULT ;
}
break ;
default :
ret = - EINVAL ;
}
2005-09-07 02:19:37 +04:00
2005-09-01 19:07:34 +04:00
return ret ;
2005-09-07 02:19:37 +04:00
}
2009-02-14 01:38:10 +03:00
static int indycam_s_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2005-09-07 02:19:37 +04:00
{
2009-02-14 01:38:10 +03:00
struct indycam * camera = to_indycam ( sd ) ;
2005-09-01 19:07:34 +04:00
u8 reg ;
int ret = 0 ;
2009-02-27 15:05:10 +03:00
switch ( ctrl - > id ) {
case V4L2_CID_AUTOGAIN :
case V4L2_CID_AUTO_WHITE_BALANCE :
2009-02-14 01:38:10 +03:00
ret = indycam_read_reg ( sd , INDYCAM_REG_CONTROL , & reg ) ;
2005-09-01 19:07:34 +04:00
if ( ret )
break ;
2005-09-07 02:19:37 +04:00
2009-02-27 15:05:10 +03:00
if ( ctrl - > id = = V4L2_CID_AUTOGAIN ) {
2005-09-01 19:07:34 +04:00
if ( ctrl - > value )
reg | = INDYCAM_CONTROL_AGCENA ;
else
reg & = ~ INDYCAM_CONTROL_AGCENA ;
} else {
if ( ctrl - > value )
reg | = INDYCAM_CONTROL_AWBCTL ;
else
reg & = ~ INDYCAM_CONTROL_AWBCTL ;
}
2009-02-14 01:38:10 +03:00
ret = indycam_write_reg ( sd , INDYCAM_REG_CONTROL , reg ) ;
2005-09-01 19:07:34 +04:00
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_EXPOSURE :
2005-09-01 19:07:34 +04:00
reg = ( ctrl - > value = = 0xff ) ? 0x00 : ( ctrl - > value + 1 ) ;
2009-02-14 01:38:10 +03:00
ret = indycam_write_reg ( sd , INDYCAM_REG_SHUTTER , reg ) ;
2005-09-01 19:07:34 +04:00
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_GAIN :
2009-02-14 01:38:10 +03:00
ret = indycam_write_reg ( sd , INDYCAM_REG_GAIN , ctrl - > value ) ;
2005-09-01 19:07:34 +04:00
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_RED_BALANCE :
2009-02-14 01:38:10 +03:00
ret = indycam_write_reg ( sd , INDYCAM_REG_RED_BALANCE ,
2005-09-01 19:07:34 +04:00
ctrl - > value ) ;
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_BLUE_BALANCE :
2009-02-14 01:38:10 +03:00
ret = indycam_write_reg ( sd , INDYCAM_REG_BLUE_BALANCE ,
2005-09-01 19:07:34 +04:00
ctrl - > value ) ;
break ;
case INDYCAM_CONTROL_RED_SATURATION :
2009-02-14 01:38:10 +03:00
ret = indycam_write_reg ( sd , INDYCAM_REG_RED_SATURATION ,
2005-09-01 19:07:34 +04:00
ctrl - > value ) ;
break ;
case INDYCAM_CONTROL_BLUE_SATURATION :
2009-02-14 01:38:10 +03:00
ret = indycam_write_reg ( sd , INDYCAM_REG_BLUE_SATURATION ,
2005-09-01 19:07:34 +04:00
ctrl - > value ) ;
break ;
2009-02-27 15:05:10 +03:00
case V4L2_CID_GAMMA :
2005-09-01 19:07:34 +04:00
if ( camera - > version = = CAMERA_VERSION_MOOSE ) {
2009-02-14 01:38:10 +03:00
ret = indycam_write_reg ( sd , INDYCAM_REG_GAMMA ,
2005-09-01 19:07:34 +04:00
ctrl - > value ) ;
}
break ;
default :
ret = - EINVAL ;
2005-09-07 02:19:37 +04:00
}
2005-09-01 19:07:34 +04:00
return ret ;
2005-09-07 02:19:37 +04:00
}
/* I2C-interface */
2009-02-14 01:38:10 +03:00
static int indycam_g_chip_ident ( struct v4l2_subdev * sd ,
struct v4l2_dbg_chip_ident * chip )
2009-02-27 15:05:10 +03:00
{
2009-02-14 01:38:10 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct indycam * camera = to_indycam ( sd ) ;
2009-02-27 15:05:10 +03:00
2009-02-14 01:38:10 +03:00
return v4l2_chip_ident_i2c_client ( client , chip , V4L2_IDENT_INDYCAM ,
camera - > version ) ;
}
2009-02-27 15:05:10 +03:00
2009-02-14 01:38:10 +03:00
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops indycam_core_ops = {
. g_chip_ident = indycam_g_chip_ident ,
. g_ctrl = indycam_g_ctrl ,
. s_ctrl = indycam_s_ctrl ,
} ;
static const struct v4l2_subdev_ops indycam_ops = {
. core = & indycam_core_ops ,
} ;
2009-02-27 15:05:10 +03:00
static int indycam_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-09-07 02:19:37 +04:00
{
int err = 0 ;
struct indycam * camera ;
2009-02-14 01:38:10 +03:00
struct v4l2_subdev * sd ;
2005-09-07 02:19:37 +04:00
2009-02-27 15:05:10 +03:00
v4l_info ( client , " chip found @ 0x%x (%s) \n " ,
client - > addr < < 1 , client - > adapter - > name ) ;
2005-09-07 02:19:37 +04:00
2006-01-12 00:40:56 +03:00
camera = kzalloc ( sizeof ( struct indycam ) , GFP_KERNEL ) ;
2009-02-27 15:05:10 +03:00
if ( ! camera )
return - ENOMEM ;
2005-09-07 02:19:37 +04:00
2009-02-14 01:38:10 +03:00
sd = & camera - > sd ;
v4l2_i2c_subdev_init ( sd , client , & indycam_ops ) ;
2005-09-07 02:19:37 +04:00
2005-09-01 19:07:34 +04:00
camera - > version = i2c_smbus_read_byte_data ( client ,
INDYCAM_REG_VERSION ) ;
2005-09-07 02:19:37 +04:00
if ( camera - > version ! = CAMERA_VERSION_INDY & &
camera - > version ! = CAMERA_VERSION_MOOSE ) {
2009-02-27 15:05:10 +03:00
kfree ( camera ) ;
return - ENODEV ;
2005-09-07 02:19:37 +04:00
}
2009-02-27 15:05:10 +03:00
2005-09-07 02:19:37 +04:00
printk ( KERN_INFO " IndyCam v%d.%d detected \n " ,
INDYCAM_VERSION_MAJOR ( camera - > version ) ,
INDYCAM_VERSION_MINOR ( camera - > version ) ) ;
2009-02-14 01:38:10 +03:00
indycam_regdump ( sd ) ;
2005-09-07 02:19:37 +04:00
// initialize
2009-02-14 01:38:10 +03:00
err = indycam_write_block ( sd , 0 , sizeof ( initseq ) , ( u8 * ) & initseq ) ;
2005-09-07 02:19:37 +04:00
if ( err ) {
2008-02-03 18:18:59 +03:00
printk ( KERN_ERR " IndyCam initialization failed \n " ) ;
2009-02-27 15:05:10 +03:00
kfree ( camera ) ;
return - EIO ;
2005-09-07 02:19:37 +04:00
}
2009-02-14 01:38:10 +03:00
indycam_regdump ( sd ) ;
2005-09-07 02:19:37 +04:00
// white balance
2009-02-14 01:38:10 +03:00
err = indycam_write_reg ( sd , INDYCAM_REG_CONTROL ,
2005-09-07 02:19:37 +04:00
INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL ) ;
if ( err ) {
2005-09-01 19:07:34 +04:00
printk ( KERN_ERR " IndyCam: White balancing camera failed \n " ) ;
2009-02-27 15:05:10 +03:00
kfree ( camera ) ;
return - EIO ;
2005-09-07 02:19:37 +04:00
}
2009-02-14 01:38:10 +03:00
indycam_regdump ( sd ) ;
2005-09-07 02:19:37 +04:00
printk ( KERN_INFO " IndyCam initialized \n " ) ;
return 0 ;
}
2009-02-27 15:05:10 +03:00
static int indycam_remove ( struct i2c_client * client )
2005-09-07 02:19:37 +04:00
{
2009-02-14 01:38:10 +03:00
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
2005-09-07 02:19:37 +04:00
2009-02-14 01:38:10 +03:00
v4l2_device_unregister_subdev ( sd ) ;
kfree ( to_indycam ( sd ) ) ;
2005-09-07 02:19:37 +04:00
return 0 ;
}
2009-02-27 15:05:10 +03:00
static const struct i2c_device_id indycam_id [ ] = {
{ " indycam " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , indycam_id ) ;
2010-09-15 22:17:19 +04:00
static struct i2c_driver indycam_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " indycam " ,
} ,
. probe = indycam_probe ,
. remove = indycam_remove ,
. id_table = indycam_id ,
2005-09-07 02:19:37 +04:00
} ;
2010-09-15 22:17:19 +04:00
2012-02-12 13:56:32 +04:00
module_i2c_driver ( indycam_driver ) ;