2005-04-17 02:20:36 +04:00
/***************************************************************************
2007-01-08 16:43:56 +03:00
* Plug - in for PAS202BCB image sensor connected to the SN9C1xx PC Camera *
2005-04-17 02:20:36 +04:00
* Controllers *
* *
* Copyright ( C ) 2004 by Carlos Eduardo Medaglia Dyonisio *
* < medaglia @ undl . org . br > *
* *
2007-01-08 16:43:56 +03:00
* Support for SN9C103 , DAC Magnitude , exposure and green gain controls *
* added by Luca Risolia < luca . risolia @ studio . unibo . it > *
2005-04-17 02:20:36 +04:00
* *
* 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/delay.h>
# include "sn9c102_sensor.h"
2008-09-04 10:33:43 +04:00
# include "sn9c102_devtable.h"
2005-04-17 02:20:36 +04:00
static int pas202bcb_init ( struct sn9c102_device * cam )
{
int err = 0 ;
2007-01-08 16:43:56 +03:00
switch ( sn9c102_get_bridge ( cam ) ) {
case BRIDGE_SN9C101 :
case BRIDGE_SN9C102 :
2007-05-02 17:04:03 +04:00
err = sn9c102_write_const_regs ( cam , { 0x00 , 0x10 } , { 0x00 , 0x11 } ,
{ 0x00 , 0x14 } , { 0x20 , 0x17 } ,
{ 0x30 , 0x19 } , { 0x09 , 0x18 } ) ;
2007-01-08 16:43:56 +03:00
break ;
case BRIDGE_SN9C103 :
2007-05-02 17:04:03 +04:00
err = sn9c102_write_const_regs ( cam , { 0x00 , 0x02 } , { 0x00 , 0x03 } ,
{ 0x1a , 0x04 } , { 0x20 , 0x05 } ,
{ 0x20 , 0x06 } , { 0x20 , 0x07 } ,
{ 0x00 , 0x10 } , { 0x00 , 0x11 } ,
{ 0x00 , 0x14 } , { 0x20 , 0x17 } ,
{ 0x30 , 0x19 } , { 0x09 , 0x18 } ,
{ 0x02 , 0x1c } , { 0x03 , 0x1d } ,
{ 0x0f , 0x1e } , { 0x0c , 0x1f } ,
{ 0x00 , 0x20 } , { 0x10 , 0x21 } ,
{ 0x20 , 0x22 } , { 0x30 , 0x23 } ,
{ 0x40 , 0x24 } , { 0x50 , 0x25 } ,
{ 0x60 , 0x26 } , { 0x70 , 0x27 } ,
{ 0x80 , 0x28 } , { 0x90 , 0x29 } ,
{ 0xa0 , 0x2a } , { 0xb0 , 0x2b } ,
{ 0xc0 , 0x2c } , { 0xd0 , 0x2d } ,
{ 0xe0 , 0x2e } , { 0xf0 , 0x2f } ,
{ 0xff , 0x30 } ) ;
2007-01-08 16:43:56 +03:00
break ;
default :
break ;
}
2005-04-17 02:20:36 +04:00
err + = sn9c102_i2c_write ( cam , 0x02 , 0x14 ) ;
err + = sn9c102_i2c_write ( cam , 0x03 , 0x40 ) ;
err + = sn9c102_i2c_write ( cam , 0x0d , 0x2c ) ;
err + = sn9c102_i2c_write ( cam , 0x0e , 0x01 ) ;
err + = sn9c102_i2c_write ( cam , 0x0f , 0xa9 ) ;
err + = sn9c102_i2c_write ( cam , 0x10 , 0x08 ) ;
err + = sn9c102_i2c_write ( cam , 0x13 , 0x63 ) ;
err + = sn9c102_i2c_write ( cam , 0x15 , 0x70 ) ;
err + = sn9c102_i2c_write ( cam , 0x11 , 0x01 ) ;
msleep ( 400 ) ;
return err ;
}
2006-03-25 15:19:53 +03:00
static int pas202bcb_get_ctrl ( struct sn9c102_device * cam ,
struct v4l2_control * ctrl )
2005-04-17 02:20:36 +04:00
{
switch ( ctrl - > id ) {
case V4L2_CID_EXPOSURE :
{
int r1 = sn9c102_i2c_read ( cam , 0x04 ) ,
r2 = sn9c102_i2c_read ( cam , 0x05 ) ;
if ( r1 < 0 | | r2 < 0 )
return - EIO ;
ctrl - > value = ( r1 < < 6 ) | ( r2 & 0x3f ) ;
}
return 0 ;
case V4L2_CID_RED_BALANCE :
if ( ( ctrl - > value = sn9c102_i2c_read ( cam , 0x09 ) ) < 0 )
return - EIO ;
ctrl - > value & = 0x0f ;
return 0 ;
case V4L2_CID_BLUE_BALANCE :
if ( ( ctrl - > value = sn9c102_i2c_read ( cam , 0x07 ) ) < 0 )
return - EIO ;
ctrl - > value & = 0x0f ;
return 0 ;
case V4L2_CID_GAIN :
if ( ( ctrl - > value = sn9c102_i2c_read ( cam , 0x10 ) ) < 0 )
return - EIO ;
ctrl - > value & = 0x1f ;
return 0 ;
case SN9C102_V4L2_CID_GREEN_BALANCE :
if ( ( ctrl - > value = sn9c102_i2c_read ( cam , 0x08 ) ) < 0 )
return - EIO ;
ctrl - > value & = 0x0f ;
return 0 ;
case SN9C102_V4L2_CID_DAC_MAGNITUDE :
if ( ( ctrl - > value = sn9c102_i2c_read ( cam , 0x0c ) ) < 0 )
return - EIO ;
return 0 ;
default :
return - EINVAL ;
}
}
2006-03-25 15:19:53 +03:00
static int pas202bcb_set_pix_format ( struct sn9c102_device * cam ,
const struct v4l2_pix_format * pix )
2005-04-17 02:20:36 +04:00
{
int err = 0 ;
if ( pix - > pixelformat = = V4L2_PIX_FMT_SN9C10X )
2007-01-08 16:43:56 +03:00
err + = sn9c102_write_reg ( cam , 0x28 , 0x17 ) ;
2005-04-17 02:20:36 +04:00
else
err + = sn9c102_write_reg ( cam , 0x20 , 0x17 ) ;
return err ;
}
2006-03-25 15:19:53 +03:00
static int pas202bcb_set_ctrl ( struct sn9c102_device * cam ,
const struct v4l2_control * ctrl )
2005-04-17 02:20:36 +04:00
{
int err = 0 ;
switch ( ctrl - > id ) {
case V4L2_CID_EXPOSURE :
err + = sn9c102_i2c_write ( cam , 0x04 , ctrl - > value > > 6 ) ;
err + = sn9c102_i2c_write ( cam , 0x05 , ctrl - > value & 0x3f ) ;
break ;
case V4L2_CID_RED_BALANCE :
err + = sn9c102_i2c_write ( cam , 0x09 , ctrl - > value ) ;
break ;
case V4L2_CID_BLUE_BALANCE :
err + = sn9c102_i2c_write ( cam , 0x07 , ctrl - > value ) ;
break ;
case V4L2_CID_GAIN :
err + = sn9c102_i2c_write ( cam , 0x10 , ctrl - > value ) ;
break ;
case SN9C102_V4L2_CID_GREEN_BALANCE :
err + = sn9c102_i2c_write ( cam , 0x08 , ctrl - > value ) ;
break ;
case SN9C102_V4L2_CID_DAC_MAGNITUDE :
err + = sn9c102_i2c_write ( cam , 0x0c , ctrl - > value ) ;
break ;
default :
return - EINVAL ;
}
err + = sn9c102_i2c_write ( cam , 0x11 , 0x01 ) ;
return err ? - EIO : 0 ;
}
2006-03-25 15:19:53 +03:00
static int pas202bcb_set_crop ( struct sn9c102_device * cam ,
const struct v4l2_rect * rect )
2005-04-17 02:20:36 +04:00
{
2007-01-08 16:43:56 +03:00
struct sn9c102_sensor * s = sn9c102_get_sensor ( cam ) ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
2007-01-08 16:43:56 +03:00
u8 h_start = 0 ,
2005-04-17 02:20:36 +04:00
v_start = ( u8 ) ( rect - > top - s - > cropcap . bounds . top ) + 3 ;
2007-01-08 16:43:56 +03:00
switch ( sn9c102_get_bridge ( cam ) ) {
case BRIDGE_SN9C101 :
case BRIDGE_SN9C102 :
h_start = ( u8 ) ( rect - > left - s - > cropcap . bounds . left ) + 4 ;
break ;
case BRIDGE_SN9C103 :
h_start = ( u8 ) ( rect - > left - s - > cropcap . bounds . left ) + 3 ;
break ;
default :
break ;
}
2005-04-17 02:20:36 +04:00
err + = sn9c102_write_reg ( cam , h_start , 0x12 ) ;
err + = sn9c102_write_reg ( cam , v_start , 0x13 ) ;
return err ;
}
2007-05-02 17:04:03 +04:00
static const struct sn9c102_sensor pas202bcb = {
2005-04-17 02:20:36 +04:00
. name = " PAS202BCB " ,
2007-01-08 16:43:56 +03:00
. maintainer = " Luca Risolia <luca.risolia@studio.unibo.it> " ,
. supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 ,
2005-04-17 02:20:36 +04:00
. sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE ,
. frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ ,
. interface = SN9C102_I2C_2WIRES ,
. i2c_slave_id = 0x40 ,
. init = & pas202bcb_init ,
. qctrl = {
{
. id = V4L2_CID_EXPOSURE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " exposure " ,
. minimum = 0x01e5 ,
. maximum = 0x3fff ,
. step = 0x0001 ,
. default_value = 0x01e5 ,
. flags = 0 ,
} ,
{
. id = V4L2_CID_GAIN ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " global gain " ,
. minimum = 0x00 ,
. maximum = 0x1f ,
. step = 0x01 ,
2007-01-08 16:43:56 +03:00
. default_value = 0x0b ,
2005-04-17 02:20:36 +04:00
. flags = 0 ,
} ,
{
. id = V4L2_CID_RED_BALANCE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " red balance " ,
. minimum = 0x00 ,
. maximum = 0x0f ,
. step = 0x01 ,
2007-01-08 16:43:56 +03:00
. default_value = 0x00 ,
2005-04-17 02:20:36 +04:00
. flags = 0 ,
} ,
{
. id = V4L2_CID_BLUE_BALANCE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " blue balance " ,
. minimum = 0x00 ,
. maximum = 0x0f ,
. step = 0x01 ,
. default_value = 0x05 ,
. flags = 0 ,
} ,
{
. id = SN9C102_V4L2_CID_GREEN_BALANCE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " green balance " ,
. minimum = 0x00 ,
. maximum = 0x0f ,
. step = 0x01 ,
. default_value = 0x00 ,
. flags = 0 ,
} ,
{
. id = SN9C102_V4L2_CID_DAC_MAGNITUDE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " DAC magnitude " ,
. minimum = 0x00 ,
. maximum = 0xff ,
. step = 0x01 ,
. default_value = 0x04 ,
. flags = 0 ,
} ,
} ,
. get_ctrl = & pas202bcb_get_ctrl ,
. set_ctrl = & pas202bcb_set_ctrl ,
. cropcap = {
. bounds = {
. left = 0 ,
. top = 0 ,
. width = 640 ,
. height = 480 ,
} ,
. defrect = {
. left = 0 ,
. top = 0 ,
. width = 640 ,
. height = 480 ,
} ,
} ,
. set_crop = & pas202bcb_set_crop ,
. pix_format = {
. width = 640 ,
. height = 480 ,
. pixelformat = V4L2_PIX_FMT_SBGGR8 ,
. priv = 8 ,
} ,
. set_pix_format = & pas202bcb_set_pix_format
} ;
int sn9c102_probe_pas202bcb ( struct sn9c102_device * cam )
2006-02-25 09:50:47 +03:00
{
2005-04-17 02:20:36 +04:00
int r0 = 0 , r1 = 0 , err = 0 ;
unsigned int pid = 0 ;
/*
* Minimal initialization to enable the I2C communication
* NOTE : do NOT change the values !
*/
2007-01-08 16:43:56 +03:00
switch ( sn9c102_get_bridge ( cam ) ) {
case BRIDGE_SN9C101 :
case BRIDGE_SN9C102 :
2007-04-05 00:11:04 +04:00
err = sn9c102_write_const_regs ( cam ,
{ 0x01 , 0x01 } , /* power down */
{ 0x40 , 0x01 } , /* power on */
{ 0x28 , 0x17 } ) ; /* clock 24 MHz */
2007-01-08 16:43:56 +03:00
break ;
case BRIDGE_SN9C103 : /* do _not_ change anything! */
2007-05-02 17:04:03 +04:00
err = sn9c102_write_const_regs ( cam , { 0x09 , 0x01 } , { 0x44 , 0x01 } ,
{ 0x44 , 0x02 } , { 0x29 , 0x17 } ) ;
2007-01-08 16:43:56 +03:00
break ;
default :
break ;
}
2005-04-17 02:20:36 +04:00
r0 = sn9c102_i2c_try_read ( cam , & pas202bcb , 0x00 ) ;
r1 = sn9c102_i2c_try_read ( cam , & pas202bcb , 0x01 ) ;
2007-01-08 16:43:56 +03:00
if ( err | | r0 < 0 | | r1 < 0 )
2005-04-17 02:20:36 +04:00
return - EIO ;
pid = ( r0 < < 4 ) | ( ( r1 & 0xf0 ) > > 4 ) ;
if ( pid ! = 0x017 )
return - ENODEV ;
sn9c102_attach_sensor ( cam , & pas202bcb ) ;
return 0 ;
}