2019-05-19 15:51:54 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-06-30 15:50:11 -03:00
/*
* Pixart PAC7311 library
* Copyright ( C ) 2005 Thomas Kaiser thomas @ kaiser - linux . li
*
* V4L2 by Jean - Francois Moine < http : //moinejf.free.fr>
*/
2008-09-03 17:12:14 -03:00
/* Some documentation about various registers as determined by trial and error.
2012-04-28 10:20:50 -03:00
*
* Register page 1 :
*
* Address Description
* 0x08 Unknown compressor related , must always be 8 except when not
* in 640 x480 resolution and page 4 reg 2 < = 3 then set it to 9 !
* 0x1b Auto white balance related , bit 0 is AWB enable ( inverted )
* bits 345 seem to toggle per color gains on / off ( inverted )
* 0x78 Global control , bit 6 controls the LED ( inverted )
2012-04-27 12:56:59 -03:00
* 0x80 Compression balance , interesting settings :
* 0x01 Use this to allow the camera to switch to higher compr .
* on the fly . Needed to stay within bandwidth @ 640 x480 @ 30
* 0x1c From usb captures under Windows for 640 x480
* 0x2a Values > = this switch the camera to a lower compression ,
* using the same table for both luminance and chrominance .
* This gives a sharper picture . Usable only at 640 x480 @ <
* 15 fps or 320 x240 / 160 x120 . Note currently the driver
* does not use this as the quality gain is small and the
* generated JPG - s are only understood by v4l - utils > = 0.8 .9
* 0x3f From usb captures under Windows for 320 x240
* 0x69 From usb captures under Windows for 160 x120
2012-04-28 10:20:50 -03:00
*
* Register page 4 :
*
* Address Description
* 0x02 Clock divider 2 - 63 , fps = ~ 60 / val . Must be a multiple of 3 on
* the 7302 , so one of 3 , 6 , 9 , . . . , except when between 6 and 12 ?
* 0x0f Master gain 1 - 245 , low value = high gain
* 0x10 Another gain 0 - 15 , limited influence ( 1 - 2 x gain I guess )
* 0x21 Bitfield : 0 - 1 unused , 2 - 3 vflip / hflip , 4 - 5 unknown , 6 - 7 unused
2012-05-10 12:13:16 -03:00
* Note setting vflip disabled leads to a much lower image quality ,
* so we always vflip , and tell userspace to flip it back
2012-04-28 10:20:50 -03:00
* 0x27 Seems to toggle various gains on / off , Setting bit 7 seems to
* completely disable the analog amplification block . Set to 0x68
* for max gain , 0x14 for minimal gain .
*/
2008-09-03 17:12:14 -03:00
2011-08-21 19:56:57 -03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-06-30 15:50:11 -03:00
# define MODULE_NAME "pac7311"
2010-01-29 11:04:19 -03:00
# include <linux/input.h>
2008-06-30 15:50:11 -03:00
# include "gspca.h"
2012-04-27 11:40:28 -03:00
/* Include pac common sof detection functions */
# include "pac_common.h"
2008-06-30 15:50:11 -03:00
2012-05-10 10:52:54 -03:00
# define PAC7311_GAIN_DEFAULT 122
# define PAC7311_EXPOSURE_DEFAULT 3 /* 20 fps, avoid using high compr. */
2008-06-30 15:50:11 -03:00
MODULE_AUTHOR ( " Thomas Kaiser thomas@kaiser-linux.li " ) ;
MODULE_DESCRIPTION ( " Pixart PAC7311 " ) ;
MODULE_LICENSE ( " GPL " ) ;
struct sd {
struct gspca_dev gspca_dev ; /* !! must be the first item */
2012-05-10 10:52:54 -03:00
struct v4l2_ctrl * contrast ;
2012-05-10 12:13:16 -03:00
struct v4l2_ctrl * hflip ;
2008-09-03 16:47:25 -03:00
2008-09-03 17:12:14 -03:00
u8 sof_read ;
u8 autogain_ignore_frames ;
atomic_t avg_lum ;
2008-06-30 15:50:11 -03:00
} ;
2008-12-29 07:49:41 -03:00
static const struct v4l2_pix_format vga_mode [ ] = {
2008-09-03 16:47:35 -03:00
{ 160 , 120 , V4L2_PIX_FMT_PJPG , V4L2_FIELD_NONE ,
2008-07-05 11:49:20 -03:00
. bytesperline = 160 ,
. sizeimage = 160 * 120 * 3 / 8 + 590 ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. priv = 2 } ,
2008-09-03 16:47:35 -03:00
{ 320 , 240 , V4L2_PIX_FMT_PJPG , V4L2_FIELD_NONE ,
2008-07-05 11:49:20 -03:00
. bytesperline = 320 ,
. sizeimage = 320 * 240 * 3 / 8 + 590 ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. priv = 1 } ,
2008-09-03 16:47:35 -03:00
{ 640 , 480 , V4L2_PIX_FMT_PJPG , V4L2_FIELD_NONE ,
2008-07-05 11:49:20 -03:00
. bytesperline = 640 ,
. sizeimage = 640 * 480 * 3 / 8 + 590 ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. priv = 0 } ,
2008-06-30 15:50:11 -03:00
} ;
2009-10-04 13:53:22 -03:00
# define LOAD_PAGE4 254
# define END_OF_SEQUENCE 0
2008-09-03 17:12:19 -03:00
static const __u8 init_7311 [ ] = {
2012-05-10 12:10:56 -03:00
0xff , 0x01 ,
2008-09-03 17:12:14 -03:00
0x78 , 0x40 , /* Bit_0=start stream, Bit_6=LED */
0x78 , 0x40 , /* Bit_0=start stream, Bit_6=LED */
0x78 , 0x44 , /* Bit_0=start stream, Bit_6=LED */
2008-09-03 16:47:25 -03:00
0xff , 0x04 ,
0x27 , 0x80 ,
0x28 , 0xca ,
0x29 , 0x53 ,
0x2a , 0x0e ,
0xff , 0x01 ,
0x3e , 0x20 ,
} ;
static const __u8 start_7311 [ ] = {
/* index, len, [value]* */
2008-09-03 16:47:33 -03:00
0xff , 1 , 0x01 , /* page 1 */
0x02 , 43 , 0x48 , 0x0a , 0x40 , 0x08 , 0x00 , 0x00 , 0x08 , 0x00 ,
2008-09-03 16:47:25 -03:00
0x06 , 0xff , 0x11 , 0xff , 0x5a , 0x30 , 0x90 , 0x4c ,
0x00 , 0x07 , 0x00 , 0x0a , 0x10 , 0x00 , 0xa0 , 0x10 ,
0x02 , 0x00 , 0x00 , 0x00 , 0x00 , 0x0b , 0x01 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 ,
2008-09-03 16:47:33 -03:00
0x3e , 42 , 0x00 , 0x00 , 0x78 , 0x52 , 0x4a , 0x52 , 0x78 , 0x6e ,
2008-09-03 16:47:25 -03:00
0x48 , 0x46 , 0x48 , 0x6e , 0x5f , 0x49 , 0x42 , 0x49 ,
0x5f , 0x5f , 0x49 , 0x42 , 0x49 , 0x5f , 0x6e , 0x48 ,
0x46 , 0x48 , 0x6e , 0x78 , 0x52 , 0x4a , 0x52 , 0x78 ,
0x00 , 0x00 , 0x09 , 0x1b , 0x34 , 0x49 , 0x5c , 0x9b ,
0xd0 , 0xff ,
0x78 , 6 , 0x44 , 0x00 , 0xf2 , 0x01 , 0x01 , 0x80 ,
0x7f , 18 , 0x2a , 0x1c , 0x00 , 0xc8 , 0x02 , 0x58 , 0x03 , 0x84 ,
0x12 , 0x00 , 0x1a , 0x04 , 0x08 , 0x0c , 0x10 , 0x14 ,
0x18 , 0x20 ,
0x96 , 3 , 0x01 , 0x08 , 0x04 ,
0xa0 , 4 , 0x44 , 0x44 , 0x44 , 0x04 ,
0xf0 , 13 , 0x01 , 0x00 , 0x00 , 0x00 , 0x22 , 0x00 , 0x20 , 0x00 ,
0x3f , 0x00 , 0x0a , 0x01 , 0x00 ,
2008-09-03 16:47:33 -03:00
0xff , 1 , 0x04 , /* page 4 */
2009-10-04 13:53:22 -03:00
0 , LOAD_PAGE4 , /* load the page 4 */
2008-09-03 16:47:25 -03:00
0x11 , 1 , 0x01 ,
2009-10-04 13:53:22 -03:00
0 , END_OF_SEQUENCE /* end of sequence */
2008-09-03 16:47:25 -03:00
} ;
2009-11-02 08:13:21 -03:00
# define SKIP 0xaa
2009-10-04 13:51:26 -03:00
/* page 4 - the value SKIP says skip the index - see reg_w_page() */
2008-09-03 16:47:25 -03:00
static const __u8 page4_7311 [ ] = {
2009-10-04 13:51:26 -03:00
SKIP , SKIP , 0x04 , 0x54 , 0x07 , 0x2b , 0x09 , 0x0f ,
0x09 , 0x00 , SKIP , SKIP , 0x07 , 0x00 , 0x00 , 0x62 ,
0x08 , SKIP , 0x07 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x03 , 0xa0 , 0x01 , 0xf4 , SKIP ,
SKIP , 0x00 , 0x08 , SKIP , 0x03 , SKIP , 0x00 , 0x68 ,
2008-09-03 16:47:25 -03:00
0xca , 0x10 , 0x06 , 0x78 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x23 , 0x28 , 0x04 , 0x11 , 0x00 , 0x00
} ;
2010-01-13 15:28:22 -03:00
static void reg_w_buf ( struct gspca_dev * gspca_dev ,
2008-09-03 16:47:25 -03:00
__u8 index ,
2010-12-28 06:59:04 -03:00
const u8 * buffer , int len )
2008-06-30 15:50:11 -03:00
{
2009-11-05 05:35:08 -03:00
int ret ;
2010-01-13 15:28:22 -03:00
if ( gspca_dev - > usb_err < 0 )
return ;
2008-07-14 09:38:29 -03:00
memcpy ( gspca_dev - > usb_buf , buffer , len ) ;
2009-11-05 05:35:08 -03:00
ret = usb_control_msg ( gspca_dev - > dev ,
2008-07-14 09:38:29 -03:00
usb_sndctrlpipe ( gspca_dev - > dev , 0 ) ,
2010-06-24 04:50:26 -03:00
0 , /* request */
2008-06-30 15:50:11 -03:00
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
2008-07-03 11:09:12 -03:00
0 , /* value */
2008-07-14 09:38:29 -03:00
index , gspca_dev - > usb_buf , len ,
2008-06-30 15:50:11 -03:00
500 ) ;
2010-01-13 15:28:22 -03:00
if ( ret < 0 ) {
2011-08-21 19:56:57 -03:00
pr_err ( " reg_w_buf() failed index 0x%02x, error %d \n " ,
index , ret ) ;
2010-01-13 15:28:22 -03:00
gspca_dev - > usb_err = ret ;
}
2008-06-30 15:50:11 -03:00
}
2010-01-13 15:28:22 -03:00
static void reg_w ( struct gspca_dev * gspca_dev ,
2008-09-03 16:47:25 -03:00
__u8 index ,
2008-07-14 09:38:29 -03:00
__u8 value )
2008-06-30 15:50:11 -03:00
{
2009-11-05 05:35:08 -03:00
int ret ;
2010-01-13 15:28:22 -03:00
if ( gspca_dev - > usb_err < 0 )
return ;
2008-07-14 09:38:29 -03:00
gspca_dev - > usb_buf [ 0 ] = value ;
2009-11-05 05:35:08 -03:00
ret = usb_control_msg ( gspca_dev - > dev ,
2008-07-14 09:38:29 -03:00
usb_sndctrlpipe ( gspca_dev - > dev , 0 ) ,
2008-07-03 11:09:12 -03:00
0 , /* request */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
2008-09-03 17:12:17 -03:00
0 , index , gspca_dev - > usb_buf , 1 ,
2008-07-03 11:09:12 -03:00
500 ) ;
2010-01-13 15:28:22 -03:00
if ( ret < 0 ) {
2011-08-21 19:56:57 -03:00
pr_err ( " reg_w() failed index 0x%02x, value 0x%02x, error %d \n " ,
index , value , ret ) ;
2010-01-13 15:28:22 -03:00
gspca_dev - > usb_err = ret ;
}
2008-06-30 15:50:11 -03:00
}
2010-01-13 15:28:22 -03:00
static void reg_w_seq ( struct gspca_dev * gspca_dev ,
2008-09-03 16:47:25 -03:00
const __u8 * seq , int len )
{
while ( - - len > = 0 ) {
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , seq [ 0 ] , seq [ 1 ] ) ;
2008-09-03 16:47:25 -03:00
seq + = 2 ;
}
}
/* load the beginning of a page */
2010-01-13 15:28:22 -03:00
static void reg_w_page ( struct gspca_dev * gspca_dev ,
2008-09-03 16:47:25 -03:00
const __u8 * page , int len )
{
int index ;
2009-11-07 05:52:02 -03:00
int ret = 0 ;
2008-09-03 16:47:25 -03:00
2010-01-13 15:28:22 -03:00
if ( gspca_dev - > usb_err < 0 )
return ;
2008-09-03 16:47:25 -03:00
for ( index = 0 ; index < len ; index + + ) {
2009-10-04 13:51:26 -03:00
if ( page [ index ] = = SKIP ) /* skip this index */
2008-09-03 16:47:25 -03:00
continue ;
gspca_dev - > usb_buf [ 0 ] = page [ index ] ;
2009-11-05 05:35:08 -03:00
ret = usb_control_msg ( gspca_dev - > dev ,
2008-09-03 16:47:25 -03:00
usb_sndctrlpipe ( gspca_dev - > dev , 0 ) ,
0 , /* request */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , index , gspca_dev - > usb_buf , 1 ,
500 ) ;
2009-11-07 05:52:02 -03:00
if ( ret < 0 ) {
2011-08-21 19:56:57 -03:00
pr_err ( " reg_w_page() failed index 0x%02x, value 0x%02x, error %d \n " ,
index , page [ index ] , ret ) ;
2010-01-13 15:28:22 -03:00
gspca_dev - > usb_err = ret ;
2009-11-07 05:52:02 -03:00
break ;
}
2008-09-03 16:47:25 -03:00
}
}
/* output a variable sequence */
2010-01-13 15:28:22 -03:00
static void reg_w_var ( struct gspca_dev * gspca_dev ,
2009-11-02 08:13:21 -03:00
const __u8 * seq ,
const __u8 * page4 , unsigned int page4_len )
2008-09-03 16:47:25 -03:00
{
int index , len ;
for ( ; ; ) {
index = * seq + + ;
len = * seq + + ;
switch ( len ) {
2009-10-04 13:53:22 -03:00
case END_OF_SEQUENCE :
2010-01-13 15:28:22 -03:00
return ;
2009-10-04 13:53:22 -03:00
case LOAD_PAGE4 :
2010-01-13 15:28:22 -03:00
reg_w_page ( gspca_dev , page4 , page4_len ) ;
2008-09-03 16:47:25 -03:00
break ;
default :
2009-10-04 13:54:48 -03:00
if ( len > USB_BUF_SZ ) {
2017-09-22 14:33:35 -04:00
gspca_err ( gspca_dev , " Incorrect variable sequence \n " ) ;
2010-01-13 15:28:22 -03:00
return ;
2008-09-03 16:47:25 -03:00
}
while ( len > 0 ) {
if ( len < 8 ) {
2010-01-13 15:28:22 -03:00
reg_w_buf ( gspca_dev ,
2009-11-07 05:52:02 -03:00
index , seq , len ) ;
2008-09-03 16:47:25 -03:00
seq + = len ;
break ;
}
2010-01-13 15:28:22 -03:00
reg_w_buf ( gspca_dev , index , seq , 8 ) ;
2008-09-03 16:47:25 -03:00
seq + = 8 ;
index + = 8 ;
len - = 8 ;
}
}
}
/* not reached */
}
2009-11-02 08:13:21 -03:00
/* this function is called at probe time for pac7311 */
2008-06-30 15:50:11 -03:00
static int sd_config ( struct gspca_dev * gspca_dev ,
const struct usb_device_id * id )
{
2012-04-27 11:40:28 -03:00
struct cam * cam = & gspca_dev - > cam ;
2008-09-03 16:47:25 -03:00
2009-11-02 08:13:21 -03:00
cam - > cam_mode = vga_mode ;
cam - > nmodes = ARRAY_SIZE ( vga_mode ) ;
2012-05-10 12:13:16 -03:00
cam - > input_flags = V4L2_IN_ST_VFLIP ;
2008-06-30 15:50:11 -03:00
return 0 ;
}
2012-05-10 10:52:54 -03:00
static void setcontrast ( struct gspca_dev * gspca_dev , s32 val )
2008-06-30 15:50:11 -03:00
{
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x04 ) ;
2012-05-10 10:52:54 -03:00
reg_w ( gspca_dev , 0x10 , val ) ;
2008-06-30 15:50:11 -03:00
/* load registers to sensor (Bit 0, auto clear) */
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
2008-06-30 15:50:11 -03:00
}
2012-05-10 10:52:54 -03:00
static void setgain ( struct gspca_dev * gspca_dev , s32 val )
2008-06-30 15:50:11 -03:00
{
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x04 ) ; /* page 4 */
reg_w ( gspca_dev , 0x0e , 0x00 ) ;
2012-05-10 10:52:54 -03:00
reg_w ( gspca_dev , 0x0f , gspca_dev - > gain - > maximum - val + 1 ) ;
2008-06-30 15:50:11 -03:00
/* load registers to sensor (Bit 0, auto clear) */
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
2008-06-30 15:50:11 -03:00
}
2012-05-10 10:52:54 -03:00
static void setexposure ( struct gspca_dev * gspca_dev , s32 val )
2008-06-30 15:50:11 -03:00
{
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x04 ) ; /* page 4 */
2012-05-10 10:52:54 -03:00
reg_w ( gspca_dev , 0x02 , val ) ;
2010-01-13 15:28:22 -03:00
2012-04-18 06:12:57 -03:00
/* load registers to sensor (Bit 0, auto clear) */
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
2012-04-28 10:20:50 -03:00
/*
* Page 1 register 8 must always be 0x08 except when not in
* 640 x480 mode and page 4 reg 2 < = 3 then it must be 9
*/
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x01 ) ;
2013-08-30 17:54:23 -03:00
if ( gspca_dev - > pixfmt . width ! = 640 & & val < = 3 )
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0x08 , 0x09 ) ;
2012-04-27 12:56:59 -03:00
else
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0x08 , 0x08 ) ;
2012-04-27 12:56:59 -03:00
/*
* Page1 register 80 sets the compression balance , normally we
* want / use 0x1c , but for 640 x480 @ 30f ps we must allow the
* camera to use higher compression or we may run out of
* bandwidth .
*/
2013-08-30 17:54:23 -03:00
if ( gspca_dev - > pixfmt . width = = 640 & & val = = 2 )
2012-04-27 12:56:59 -03:00
reg_w ( gspca_dev , 0x80 , 0x01 ) ;
else
reg_w ( gspca_dev , 0x80 , 0x1c ) ;
2009-11-02 08:13:21 -03:00
2008-09-03 17:12:17 -03:00
/* load registers to sensor (Bit 0, auto clear) */
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
2008-06-30 15:50:11 -03:00
}
2012-05-10 10:52:54 -03:00
static void sethvflip ( struct gspca_dev * gspca_dev , s32 hflip , s32 vflip )
2008-09-03 16:47:58 -03:00
{
__u8 data ;
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x04 ) ; /* page 4 */
2012-05-10 10:52:54 -03:00
data = ( hflip ? 0x04 : 0x00 ) |
( vflip ? 0x08 : 0x00 ) ;
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0x21 , data ) ;
2008-09-03 17:12:17 -03:00
/* load registers to sensor (Bit 0, auto clear) */
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
2008-09-03 16:47:58 -03:00
}
2009-11-02 08:13:21 -03:00
/* this function is called at probe and resume time for pac7311 */
2008-09-03 17:12:16 -03:00
static int sd_init ( struct gspca_dev * gspca_dev )
2008-06-30 15:50:11 -03:00
{
2010-01-13 15:28:22 -03:00
reg_w_seq ( gspca_dev , init_7311 , sizeof ( init_7311 ) / 2 ) ;
return gspca_dev - > usb_err ;
2008-06-30 15:50:11 -03:00
}
2012-05-10 10:52:54 -03:00
static int sd_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct gspca_dev * gspca_dev =
container_of ( ctrl - > handler , struct gspca_dev , ctrl_handler ) ;
struct sd * sd = ( struct sd * ) gspca_dev ;
gspca_dev - > usb_err = 0 ;
if ( ctrl - > id = = V4L2_CID_AUTOGAIN & & ctrl - > is_new & & ctrl - > val ) {
/* when switching to autogain set defaults to make sure
we are on a valid point of the autogain gain /
exposure knee graph , and give this change time to
take effect before doing autogain . */
gspca_dev - > exposure - > val = PAC7311_EXPOSURE_DEFAULT ;
gspca_dev - > gain - > val = PAC7311_GAIN_DEFAULT ;
sd - > autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES ;
}
if ( ! gspca_dev - > streaming )
return 0 ;
switch ( ctrl - > id ) {
case V4L2_CID_CONTRAST :
setcontrast ( gspca_dev , ctrl - > val ) ;
break ;
case V4L2_CID_AUTOGAIN :
if ( gspca_dev - > exposure - > is_new | | ( ctrl - > is_new & & ctrl - > val ) )
setexposure ( gspca_dev , gspca_dev - > exposure - > val ) ;
if ( gspca_dev - > gain - > is_new | | ( ctrl - > is_new & & ctrl - > val ) )
setgain ( gspca_dev , gspca_dev - > gain - > val ) ;
break ;
case V4L2_CID_HFLIP :
2012-05-10 12:13:16 -03:00
sethvflip ( gspca_dev , sd - > hflip - > val , 1 ) ;
2012-05-10 10:52:54 -03:00
break ;
default :
return - EINVAL ;
}
return gspca_dev - > usb_err ;
}
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
. s_ctrl = sd_s_ctrl ,
} ;
/* this function is called at probe time */
static int sd_init_controls ( struct gspca_dev * gspca_dev )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
struct v4l2_ctrl_handler * hdl = & gspca_dev - > ctrl_handler ;
gspca_dev - > vdev . ctrl_handler = hdl ;
2012-05-14 14:51:22 -03:00
v4l2_ctrl_handler_init ( hdl , 5 ) ;
2012-05-10 10:52:54 -03:00
sd - > contrast = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_CONTRAST , 0 , 15 , 1 , 7 ) ;
gspca_dev - > autogain = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_AUTOGAIN , 0 , 1 , 1 , 1 ) ;
gspca_dev - > exposure = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_EXPOSURE , 2 , 63 , 1 ,
PAC7311_EXPOSURE_DEFAULT ) ;
gspca_dev - > gain = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_GAIN , 0 , 244 , 1 ,
PAC7311_GAIN_DEFAULT ) ;
sd - > hflip = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_HFLIP , 0 , 1 , 1 , 0 ) ;
if ( hdl - > error ) {
pr_err ( " Could not initialize controls \n " ) ;
return hdl - > error ;
}
v4l2_ctrl_auto_cluster ( 3 , & gspca_dev - > autogain , 0 , false ) ;
return 0 ;
}
/* -- start the camera -- */
2008-09-20 06:39:08 -03:00
static int sd_start ( struct gspca_dev * gspca_dev )
2008-06-30 15:50:11 -03:00
{
2008-09-03 16:47:21 -03:00
struct sd * sd = ( struct sd * ) gspca_dev ;
2008-09-03 17:12:14 -03:00
sd - > sof_read = 0 ;
2008-07-14 09:38:29 -03:00
2010-01-13 15:28:22 -03:00
reg_w_var ( gspca_dev , start_7311 ,
2009-11-02 08:13:21 -03:00
page4_7311 , sizeof ( page4_7311 ) ) ;
2012-05-10 10:52:54 -03:00
setcontrast ( gspca_dev , v4l2_ctrl_g_ctrl ( sd - > contrast ) ) ;
setgain ( gspca_dev , v4l2_ctrl_g_ctrl ( gspca_dev - > gain ) ) ;
setexposure ( gspca_dev , v4l2_ctrl_g_ctrl ( gspca_dev - > exposure ) ) ;
2012-05-10 12:13:16 -03:00
sethvflip ( gspca_dev , v4l2_ctrl_g_ctrl ( sd - > hflip ) , 1 ) ;
2008-06-30 15:50:11 -03:00
/* set correct resolution */
2008-07-05 11:49:20 -03:00
switch ( gspca_dev - > cam . cam_mode [ ( int ) gspca_dev - > curr_mode ] . priv ) {
2012-04-25 12:00:49 -03:00
case 2 : /* 160x120 */
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x01 ) ;
reg_w ( gspca_dev , 0x17 , 0x20 ) ;
reg_w ( gspca_dev , 0x87 , 0x10 ) ;
2008-06-30 15:50:11 -03:00
break ;
2012-04-25 12:00:49 -03:00
case 1 : /* 320x240 */
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x01 ) ;
reg_w ( gspca_dev , 0x17 , 0x30 ) ;
reg_w ( gspca_dev , 0x87 , 0x11 ) ;
2008-06-30 15:50:11 -03:00
break ;
case 0 : /* 640x480 */
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x01 ) ;
reg_w ( gspca_dev , 0x17 , 0x00 ) ;
reg_w ( gspca_dev , 0x87 , 0x12 ) ;
2008-06-30 15:50:11 -03:00
break ;
}
2008-09-03 17:12:14 -03:00
sd - > sof_read = 0 ;
sd - > autogain_ignore_frames = 0 ;
atomic_set ( & sd - > avg_lum , - 1 ) ;
2008-09-03 17:12:17 -03:00
/* start stream */
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x01 ) ;
reg_w ( gspca_dev , 0x78 , 0x05 ) ;
2009-11-02 08:13:21 -03:00
2010-01-13 15:28:22 -03:00
return gspca_dev - > usb_err ;
2008-06-30 15:50:11 -03:00
}
static void sd_stopN ( struct gspca_dev * gspca_dev )
{
2010-01-13 15:28:22 -03:00
reg_w ( gspca_dev , 0xff , 0x04 ) ;
reg_w ( gspca_dev , 0x27 , 0x80 ) ;
reg_w ( gspca_dev , 0x28 , 0xca ) ;
reg_w ( gspca_dev , 0x29 , 0x53 ) ;
reg_w ( gspca_dev , 0x2a , 0x0e ) ;
reg_w ( gspca_dev , 0xff , 0x01 ) ;
reg_w ( gspca_dev , 0x3e , 0x20 ) ;
reg_w ( gspca_dev , 0x78 , 0x44 ) ; /* Bit_0=start stream, Bit_6=LED */
reg_w ( gspca_dev , 0x78 , 0x44 ) ; /* Bit_0=start stream, Bit_6=LED */
reg_w ( gspca_dev , 0x78 , 0x44 ) ; /* Bit_0=start stream, Bit_6=LED */
2008-06-30 15:50:11 -03:00
}
2008-08-03 07:52:53 -03:00
static void do_autogain ( struct gspca_dev * gspca_dev )
2008-06-30 15:50:11 -03:00
{
2008-09-03 17:12:17 -03:00
struct sd * sd = ( struct sd * ) gspca_dev ;
int avg_lum = atomic_read ( & sd - > avg_lum ) ;
2008-09-03 17:12:18 -03:00
int desired_lum , deadzone ;
2008-09-03 17:12:17 -03:00
2012-05-10 10:52:54 -03:00
if ( avg_lum = = - 1 )
2008-09-03 17:12:17 -03:00
return ;
2012-05-10 12:13:16 -03:00
desired_lum = 170 ;
2009-11-02 08:13:21 -03:00
deadzone = 20 ;
2008-09-03 17:12:17 -03:00
if ( sd - > autogain_ignore_frames > 0 )
sd - > autogain_ignore_frames - - ;
2012-05-10 10:52:54 -03:00
else if ( gspca_coarse_grained_expo_autogain ( gspca_dev , avg_lum ,
desired_lum , deadzone ) )
2008-09-03 17:12:17 -03:00
sd - > autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES ;
2008-06-30 15:50:11 -03:00
}
2009-10-04 13:58:19 -03:00
/* JPEG header, part 1 */
2009-11-02 08:09:34 -03:00
static const unsigned char pac_jpeg_header1 [ ] = {
2009-10-04 13:58:19 -03:00
0xff , 0xd8 , /* SOI: Start of Image */
0xff , 0xc0 , /* SOF0: Start of Frame (Baseline DCT) */
0x00 , 0x11 , /* length = 17 bytes (including this length field) */
0x08 /* Precision: 8 */
/* 2 bytes is placed here: number of image lines */
/* 2 bytes is placed here: samples per line */
2008-09-03 17:12:14 -03:00
} ;
2009-10-04 13:58:19 -03:00
/* JPEG header, continued */
2009-11-02 08:09:34 -03:00
static const unsigned char pac_jpeg_header2 [ ] = {
2009-10-04 13:58:19 -03:00
0x03 , /* Number of image components: 3 */
0x01 , 0x21 , 0x00 , /* ID=1, Subsampling 1x1, Quantization table: 0 */
0x02 , 0x11 , 0x01 , /* ID=2, Subsampling 2x1, Quantization table: 1 */
0x03 , 0x11 , 0x01 , /* ID=3, Subsampling 2x1, Quantization table: 1 */
0xff , 0xda , /* SOS: Start Of Scan */
0x00 , 0x0c , /* length = 12 bytes (including this length field) */
0x03 , /* number of components: 3 */
0x01 , 0x00 , /* selector 1, table 0x00 */
0x02 , 0x11 , /* selector 2, table 0x11 */
0x03 , 0x11 , /* selector 3, table 0x11 */
0x00 , 0x3f , /* Spectral selection: 0 .. 63 */
0x00 /* Successive approximation: 0 */
2008-09-03 17:12:14 -03:00
} ;
2009-11-02 08:09:34 -03:00
static void pac_start_frame ( struct gspca_dev * gspca_dev ,
__u16 lines , __u16 samples_per_line )
{
unsigned char tmpbuf [ 4 ] ;
2009-11-13 09:21:03 -03:00
gspca_frame_add ( gspca_dev , FIRST_PACKET ,
2009-11-02 08:09:34 -03:00
pac_jpeg_header1 , sizeof ( pac_jpeg_header1 ) ) ;
tmpbuf [ 0 ] = lines > > 8 ;
tmpbuf [ 1 ] = lines & 0xff ;
tmpbuf [ 2 ] = samples_per_line > > 8 ;
tmpbuf [ 3 ] = samples_per_line & 0xff ;
2009-11-13 09:21:03 -03:00
gspca_frame_add ( gspca_dev , INTER_PACKET ,
2009-11-02 08:09:34 -03:00
tmpbuf , sizeof ( tmpbuf ) ) ;
2009-11-13 09:21:03 -03:00
gspca_frame_add ( gspca_dev , INTER_PACKET ,
2009-11-02 08:09:34 -03:00
pac_jpeg_header2 , sizeof ( pac_jpeg_header2 ) ) ;
}
2008-09-03 16:47:21 -03:00
/* this function is run at interrupt level */
2008-06-30 15:50:11 -03:00
static void sd_pkt_scan ( struct gspca_dev * gspca_dev ,
2009-11-13 09:21:03 -03:00
u8 * data , /* isoc packet */
2008-06-30 15:50:11 -03:00
int len ) /* iso packet length */
{
struct sd * sd = ( struct sd * ) gspca_dev ;
2010-06-27 03:08:19 -03:00
u8 * image ;
2008-09-03 17:12:14 -03:00
unsigned char * sof ;
2013-02-04 13:17:55 -03:00
sof = pac_find_sof ( gspca_dev , & sd - > sof_read , data , len ) ;
2008-09-03 17:12:14 -03:00
if ( sof ) {
int n , lum_offset , footer_length ;
2012-04-28 10:20:50 -03:00
/*
* 6 bytes after the FF D9 EOF marker a number of lumination
* bytes are send corresponding to different parts of the
* image , the 14 th and 15 th byte after the EOF seem to
* correspond to the center of the image .
*/
2009-11-02 08:13:21 -03:00
lum_offset = 24 + sizeof pac_sof_marker ;
footer_length = 26 ;
2008-09-03 17:12:14 -03:00
/* Finish decoding current frame */
n = ( sof - data ) - ( footer_length + sizeof pac_sof_marker ) ;
if ( n < 0 ) {
2010-06-27 03:08:19 -03:00
gspca_dev - > image_len + = n ;
2008-09-03 17:12:14 -03:00
n = 0 ;
2010-06-27 03:08:19 -03:00
} else {
gspca_frame_add ( gspca_dev , INTER_PACKET , data , n ) ;
2008-09-03 17:12:14 -03:00
}
2010-07-06 04:32:27 -03:00
image = gspca_dev - > image ;
if ( image ! = NULL
2010-06-27 03:08:19 -03:00
& & image [ gspca_dev - > image_len - 2 ] = = 0xff
& & image [ gspca_dev - > image_len - 1 ] = = 0xd9 )
gspca_frame_add ( gspca_dev , LAST_PACKET , NULL , 0 ) ;
2008-09-03 17:12:14 -03:00
n = sof - data ;
len - = n ;
data = sof ;
/* Get average lumination */
if ( gspca_dev - > last_packet_type = = LAST_PACKET & &
2008-09-03 17:12:18 -03:00
n > = lum_offset )
atomic_set ( & sd - > avg_lum , data [ - lum_offset ] +
2008-09-03 17:12:14 -03:00
data [ - lum_offset + 1 ] ) ;
2008-09-03 17:12:18 -03:00
else
2008-09-03 17:12:14 -03:00
atomic_set ( & sd - > avg_lum , - 1 ) ;
/* Start the new frame with the jpeg header */
2010-06-27 03:08:19 -03:00
pac_start_frame ( gspca_dev ,
2013-08-30 17:54:23 -03:00
gspca_dev - > pixfmt . height , gspca_dev - > pixfmt . width ) ;
2008-06-30 15:50:11 -03:00
}
2009-11-13 09:21:03 -03:00
gspca_frame_add ( gspca_dev , INTER_PACKET , data , len ) ;
2008-06-30 15:50:11 -03:00
}
2013-01-24 19:29:04 -03:00
# if IS_ENABLED(CONFIG_INPUT)
2010-01-29 11:04:19 -03:00
static int sd_int_pkt_scan ( struct gspca_dev * gspca_dev ,
u8 * data , /* interrupt packet data */
int len ) /* interrupt packet length */
{
int ret = - EINVAL ;
u8 data0 , data1 ;
if ( len = = 2 ) {
data0 = data [ 0 ] ;
data1 = data [ 1 ] ;
if ( ( data0 = = 0x00 & & data1 = = 0x11 ) | |
( data0 = = 0x22 & & data1 = = 0x33 ) | |
( data0 = = 0x44 & & data1 = = 0x55 ) | |
( data0 = = 0x66 & & data1 = = 0x77 ) | |
( data0 = = 0x88 & & data1 = = 0x99 ) | |
( data0 = = 0xaa & & data1 = = 0xbb ) | |
( data0 = = 0xcc & & data1 = = 0xdd ) | |
( data0 = = 0xee & & data1 = = 0xff ) ) {
input_report_key ( gspca_dev - > input_dev , KEY_CAMERA , 1 ) ;
input_sync ( gspca_dev - > input_dev ) ;
input_report_key ( gspca_dev - > input_dev , KEY_CAMERA , 0 ) ;
input_sync ( gspca_dev - > input_dev ) ;
ret = 0 ;
}
}
return ret ;
}
# endif
2010-01-05 12:39:02 -03:00
static const struct sd_desc sd_desc = {
2008-06-30 15:50:11 -03:00
. name = MODULE_NAME ,
. config = sd_config ,
2008-09-03 17:12:16 -03:00
. init = sd_init ,
2012-05-10 10:52:54 -03:00
. init_controls = sd_init_controls ,
2008-06-30 15:50:11 -03:00
. start = sd_start ,
. stopN = sd_stopN ,
. pkt_scan = sd_pkt_scan ,
2008-08-03 07:52:53 -03:00
. dq_callback = do_autogain ,
2013-01-24 19:29:04 -03:00
# if IS_ENABLED(CONFIG_INPUT)
2010-01-29 11:04:19 -03:00
. int_pkt_scan = sd_int_pkt_scan ,
# endif
2008-06-30 15:50:11 -03:00
} ;
/* -- module initialisation -- */
2011-01-13 05:20:29 -03:00
static const struct usb_device_id device_table [ ] = {
2009-11-02 08:13:21 -03:00
{ USB_DEVICE ( 0x093a , 0x2600 ) } ,
{ USB_DEVICE ( 0x093a , 0x2601 ) } ,
{ USB_DEVICE ( 0x093a , 0x2603 ) } ,
{ USB_DEVICE ( 0x093a , 0x2608 ) } ,
{ USB_DEVICE ( 0x093a , 0x260e ) } ,
{ USB_DEVICE ( 0x093a , 0x260f ) } ,
2008-06-30 15:50:11 -03:00
{ }
} ;
MODULE_DEVICE_TABLE ( usb , device_table ) ;
/* -- device connect -- */
2011-01-13 05:20:29 -03:00
static int sd_probe ( struct usb_interface * intf ,
2008-06-30 15:50:11 -03:00
const struct usb_device_id * id )
{
return gspca_dev_probe ( intf , id , & sd_desc , sizeof ( struct sd ) ,
THIS_MODULE ) ;
}
static struct usb_driver sd_driver = {
. name = MODULE_NAME ,
. id_table = device_table ,
. probe = sd_probe ,
. disconnect = gspca_disconnect ,
2008-09-03 16:48:10 -03:00
# ifdef CONFIG_PM
. suspend = gspca_suspend ,
. resume = gspca_resume ,
2012-06-30 06:44:47 -03:00
. reset_resume = gspca_resume ,
2008-09-03 16:48:10 -03:00
# endif
2008-06-30 15:50:11 -03:00
} ;
2011-11-18 09:46:12 -08:00
module_usb_driver ( sd_driver ) ;