2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-12-29 07:35:23 -03:00
/*
* Copyright ( c ) 2001 Jean - Fredric Clere , Nikolas Zimmermann , Georg Acher
* Mark Cave - Ayland , Carlo E Prelz , Dick Streefland
* Copyright ( c ) 2002 , 2003 Tuukka Toivonen
* Copyright ( c ) 2008 Erik Andrén
*
* P / N 861037 : Sensor HDCS1000 ASIC STV0600
* P / N 861050 - 0010 : Sensor HDCS1000 ASIC STV0600
* P / N 861050 - 0020 : Sensor Photobit PB100 ASIC STV0600 - 1 - QuickCam Express
* P / N 861055 : Sensor ST VV6410 ASIC STV0610 - LEGO cam
* P / N 861075 - 0040 : Sensor HDCS1000 ASIC
* P / N 961179 - 0700 : Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB
* P / N 861040 - 0000 : Sensor ST VV6410 ASIC STV0610 - QuickCam Web
*/
2011-08-21 19:56:57 -03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-12-29 07:35:23 -03:00
# include "stv06xx_vv6410.h"
2008-12-31 14:33:53 -03:00
static struct v4l2_pix_format vv6410_mode [ ] = {
{
356 ,
292 ,
V4L2_PIX_FMT_SGRBG8 ,
V4L2_FIELD_NONE ,
. sizeimage = 356 * 292 ,
. bytesperline = 356 ,
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = 0
}
} ;
2012-05-06 09:28:30 -03:00
static int vv6410_s_ctrl ( struct v4l2_ctrl * ctrl )
{
2012-05-09 11:19:00 -03:00
struct gspca_dev * gspca_dev =
container_of ( ctrl - > handler , struct gspca_dev , ctrl_handler ) ;
2012-05-06 09:28:30 -03:00
int err = - EINVAL ;
switch ( ctrl - > id ) {
case V4L2_CID_HFLIP :
2012-11-22 08:59:06 -03:00
if ( ! gspca_dev - > streaming )
return 0 ;
2012-05-09 11:19:00 -03:00
err = vv6410_set_hflip ( gspca_dev , ctrl - > val ) ;
2012-05-06 09:28:30 -03:00
break ;
case V4L2_CID_VFLIP :
2012-11-22 08:59:06 -03:00
if ( ! gspca_dev - > streaming )
return 0 ;
2012-05-09 11:19:00 -03:00
err = vv6410_set_vflip ( gspca_dev , ctrl - > val ) ;
2012-05-06 09:28:30 -03:00
break ;
case V4L2_CID_GAIN :
2012-05-09 11:19:00 -03:00
err = vv6410_set_analog_gain ( gspca_dev , ctrl - > val ) ;
2012-05-06 09:28:30 -03:00
break ;
case V4L2_CID_EXPOSURE :
2012-05-09 11:19:00 -03:00
err = vv6410_set_exposure ( gspca_dev , ctrl - > val ) ;
2012-05-06 09:28:30 -03:00
break ;
2008-12-31 14:33:53 -03:00
}
2012-05-06 09:28:30 -03:00
return err ;
}
static const struct v4l2_ctrl_ops vv6410_ctrl_ops = {
. s_ctrl = vv6410_s_ctrl ,
} ;
2008-12-31 14:33:53 -03:00
2008-12-29 07:35:23 -03:00
static int vv6410_probe ( struct sd * sd )
{
u16 data ;
2012-05-06 09:28:30 -03:00
int err ;
2008-12-29 07:35:23 -03:00
err = stv06xx_read_sensor ( sd , VV6410_DEVICEH , & data ) ;
if ( err < 0 )
return - ENODEV ;
2012-05-06 09:28:30 -03:00
if ( data ! = 0x19 )
return - ENODEV ;
2008-12-29 07:35:23 -03:00
2012-05-06 09:28:30 -03:00
pr_info ( " vv6410 sensor detected \n " ) ;
2009-01-07 06:11:50 -03:00
2012-05-06 09:28:30 -03:00
sd - > gspca_dev . cam . cam_mode = vv6410_mode ;
sd - > gspca_dev . cam . nmodes = ARRAY_SIZE ( vv6410_mode ) ;
return 0 ;
}
2009-01-07 06:11:50 -03:00
2012-05-06 09:28:30 -03:00
static int vv6410_init_controls ( struct sd * sd )
{
2012-05-09 11:19:00 -03:00
struct v4l2_ctrl_handler * hdl = & sd - > gspca_dev . ctrl_handler ;
2012-05-06 09:28:30 -03:00
2012-12-10 16:35:19 -03:00
v4l2_ctrl_handler_init ( hdl , 2 ) ;
/* Disable the hardware VFLIP and HFLIP as we currently lack a
mechanism to adjust the image offset in such a way that
we don ' t need to renegotiate the announced format */
/* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */
/* V4L2_CID_HFLIP, 0, 1, 1, 0); */
/* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */
/* V4L2_CID_VFLIP, 0, 1, 1, 0); */
2012-05-06 09:28:30 -03:00
v4l2_ctrl_new_std ( hdl , & vv6410_ctrl_ops ,
V4L2_CID_EXPOSURE , 0 , 32768 , 1 , 20000 ) ;
v4l2_ctrl_new_std ( hdl , & vv6410_ctrl_ops ,
V4L2_CID_GAIN , 0 , 15 , 1 , 10 ) ;
return hdl - > error ;
2008-12-29 07:35:23 -03:00
}
static int vv6410_init ( struct sd * sd )
{
int err = 0 , i ;
2012-05-06 09:28:30 -03:00
for ( i = 0 ; i < ARRAY_SIZE ( stv_bridge_init ) ; i + + )
2011-07-27 17:19:58 -03:00
stv06xx_write_bridge ( sd , stv_bridge_init [ i ] . addr , stv_bridge_init [ i ] . data ) ;
2008-12-29 07:35:23 -03:00
err = stv06xx_write_sensor_bytes ( sd , ( u8 * ) vv6410_sensor_init ,
ARRAY_SIZE ( vv6410_sensor_init ) ) ;
return ( err < 0 ) ? err : 0 ;
}
static int vv6410_start ( struct sd * sd )
{
int err ;
2013-02-04 13:17:55 -03:00
struct gspca_dev * gspca_dev = ( struct gspca_dev * ) sd ;
2008-12-29 07:35:23 -03:00
struct cam * cam = & sd - > gspca_dev . cam ;
u32 priv = cam - > cam_mode [ sd - > gspca_dev . curr_mode ] . priv ;
if ( priv & VV6410_SUBSAMPLE ) {
2017-09-22 15:20:33 -04:00
gspca_dbg ( gspca_dev , D_CONF , " Enabling subsampling \n " ) ;
2008-12-29 07:35:23 -03:00
stv06xx_write_bridge ( sd , STV_Y_CTRL , 0x02 ) ;
stv06xx_write_bridge ( sd , STV_X_CTRL , 0x06 ) ;
stv06xx_write_bridge ( sd , STV_SCAN_RATE , 0x10 ) ;
} else {
stv06xx_write_bridge ( sd , STV_Y_CTRL , 0x01 ) ;
stv06xx_write_bridge ( sd , STV_X_CTRL , 0x0a ) ;
2011-07-27 17:21:49 -03:00
stv06xx_write_bridge ( sd , STV_SCAN_RATE , 0x00 ) ;
2008-12-29 07:35:23 -03:00
}
/* Turn on LED */
err = stv06xx_write_bridge ( sd , STV_LED_CTRL , LED_ON ) ;
if ( err < 0 )
return err ;
err = stv06xx_write_sensor ( sd , VV6410_SETUP0 , 0 ) ;
if ( err < 0 )
return err ;
2017-09-22 15:20:33 -04:00
gspca_dbg ( gspca_dev , D_STREAM , " Starting stream \n " ) ;
2008-12-29 07:35:23 -03:00
return 0 ;
}
static int vv6410_stop ( struct sd * sd )
{
2013-02-04 13:17:55 -03:00
struct gspca_dev * gspca_dev = ( struct gspca_dev * ) sd ;
2008-12-29 07:35:23 -03:00
int err ;
/* Turn off LED */
err = stv06xx_write_bridge ( sd , STV_LED_CTRL , LED_OFF ) ;
if ( err < 0 )
return err ;
err = stv06xx_write_sensor ( sd , VV6410_SETUP0 , VV6410_LOW_POWER_MODE ) ;
if ( err < 0 )
return err ;
2017-09-22 15:20:33 -04:00
gspca_dbg ( gspca_dev , D_STREAM , " Halting stream \n " ) ;
2008-12-29 07:35:23 -03:00
2014-02-18 12:00:45 -03:00
return 0 ;
2008-12-29 07:35:23 -03:00
}
static int vv6410_dump ( struct sd * sd )
{
u8 i ;
int err = 0 ;
2011-08-21 19:56:57 -03:00
pr_info ( " Dumping all vv6410 sensor registers \n " ) ;
2008-12-29 07:35:23 -03:00
for ( i = 0 ; i < 0xff & & ! err ; i + + ) {
u16 data ;
err = stv06xx_read_sensor ( sd , i , & data ) ;
2011-08-21 19:56:57 -03:00
pr_info ( " Register 0x%x contained 0x%x \n " , i , data ) ;
2008-12-29 07:35:23 -03:00
}
return ( err < 0 ) ? err : 0 ;
}
static int vv6410_set_hflip ( struct gspca_dev * gspca_dev , __s32 val )
{
int err ;
u16 i2c_data ;
struct sd * sd = ( struct sd * ) gspca_dev ;
2009-01-07 06:11:50 -03:00
2008-12-29 07:35:23 -03:00
err = stv06xx_read_sensor ( sd , VV6410_DATAFORMAT , & i2c_data ) ;
if ( err < 0 )
return err ;
if ( val )
i2c_data | = VV6410_HFLIP ;
else
i2c_data & = ~ VV6410_HFLIP ;
2017-09-22 15:20:33 -04:00
gspca_dbg ( gspca_dev , D_CONF , " Set horizontal flip to %d \n " , val ) ;
2008-12-29 07:35:23 -03:00
err = stv06xx_write_sensor ( sd , VV6410_DATAFORMAT , i2c_data ) ;
return ( err < 0 ) ? err : 0 ;
}
static int vv6410_set_vflip ( struct gspca_dev * gspca_dev , __s32 val )
{
int err ;
u16 i2c_data ;
struct sd * sd = ( struct sd * ) gspca_dev ;
2009-01-07 06:11:50 -03:00
2008-12-29 07:35:23 -03:00
err = stv06xx_read_sensor ( sd , VV6410_DATAFORMAT , & i2c_data ) ;
if ( err < 0 )
return err ;
if ( val )
i2c_data | = VV6410_VFLIP ;
else
i2c_data & = ~ VV6410_VFLIP ;
2017-09-22 15:20:33 -04:00
gspca_dbg ( gspca_dev , D_CONF , " Set vertical flip to %d \n " , val ) ;
2008-12-29 07:35:23 -03:00
err = stv06xx_write_sensor ( sd , VV6410_DATAFORMAT , i2c_data ) ;
return ( err < 0 ) ? err : 0 ;
}
static int vv6410_set_analog_gain ( struct gspca_dev * gspca_dev , __s32 val )
{
int err ;
struct sd * sd = ( struct sd * ) gspca_dev ;
2017-09-22 15:20:33 -04:00
gspca_dbg ( gspca_dev , D_CONF , " Set analog gain to %d \n " , val ) ;
2008-12-29 07:35:23 -03:00
err = stv06xx_write_sensor ( sd , VV6410_ANALOGGAIN , 0xf0 | ( val & 0xf ) ) ;
return ( err < 0 ) ? err : 0 ;
}
2009-05-03 15:45:48 -03:00
static int vv6410_set_exposure ( struct gspca_dev * gspca_dev , __s32 val )
{
int err ;
struct sd * sd = ( struct sd * ) gspca_dev ;
unsigned int fine , coarse ;
val = ( val * val > > 14 ) + val / 4 ;
fine = val % VV6410_CIF_LINELENGTH ;
coarse = min ( 512 , val / VV6410_CIF_LINELENGTH ) ;
2017-09-22 15:20:33 -04:00
gspca_dbg ( gspca_dev , D_CONF , " Set coarse exposure to %d, fine exposure to %d \n " ,
coarse , fine ) ;
2009-05-03 15:45:48 -03:00
err = stv06xx_write_sensor ( sd , VV6410_FINEH , fine > > 8 ) ;
if ( err < 0 )
goto out ;
err = stv06xx_write_sensor ( sd , VV6410_FINEL , fine & 0xff ) ;
if ( err < 0 )
goto out ;
err = stv06xx_write_sensor ( sd , VV6410_COARSEH , coarse > > 8 ) ;
if ( err < 0 )
goto out ;
err = stv06xx_write_sensor ( sd , VV6410_COARSEL , coarse & 0xff ) ;
out :
return err ;
}