2019-05-19 15:51:54 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2009-11-02 08:13:21 -03:00
/*
2012-02-27 05:15:12 -03:00
* Pixart PAC7302 driver
2009-11-02 08:13:21 -03:00
*
2012-02-27 05:15:12 -03:00
* Copyright ( C ) 2008 - 2012 Jean - Francois Moine < http : //moinejf.free.fr>
* Copyright ( C ) 2005 Thomas Kaiser thomas @ kaiser - linux . li
2009-11-02 08:13:21 -03:00
*
2010-01-28 16:35:40 -03:00
* Separated from Pixart PAC7311 library by Márton Németh
2010-01-28 16:33:38 -03:00
* Camera button input handling by Márton Németh < nm127 @ freemail . hu >
* Copyright ( C ) 2009 - 2010 Márton Németh < nm127 @ freemail . hu >
2009-11-02 08:13:21 -03:00
*/
2012-04-28 10:31:17 -03:00
/*
* Some documentation about various registers as determined by trial and error .
*
2012-09-09 15:02:21 -03:00
* Register page 0 :
*
* Address Description
2012-09-23 10:29:42 -03:00
* 0x01 Red balance control
* 0x02 Green balance control
* 0x03 Blue balance control
2012-09-09 15:02:24 -03:00
* The Windows driver uses a quadratic approach to map
* the settable values ( 0 - 200 ) on register values :
2012-09-23 10:29:42 -03:00
* min = 0x20 , default = 0x40 , max = 0x80
* 0x0f - 0x20 Color and saturation control
2012-09-09 15:02:24 -03:00
* 0xa2 - 0xab Brightness , contrast and gamma control
2012-09-09 15:02:21 -03:00
* 0xb6 Sharpness control ( bits 0 - 4 )
*
2012-04-28 10:31:17 -03:00
* Register page 1 :
*
* Address Description
* 0x78 Global control , bit 6 controls the LED ( inverted )
2012-04-27 13:00:57 -03:00
* 0x80 Compression balance , 2 interesting settings :
* 0x0f Default
* 0x50 Values > = this switch the camera to a lower compression ,
* using the same table for both luminance and chrominance .
* This gives a sharper picture . Only usable when running
* at < 15 fps ! 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
2012-04-28 10:31:17 -03:00
*
* Register page 3 :
*
* Address Description
* 0x02 Clock divider 3 - 63 , fps = 90 / val . Must be a multiple of 3 on
* the 7302 , so one of 3 , 6 , 9 , . . . , except when between 6 and 12 ?
* 0x03 Variable framerate ctrl reg2 = = 3 : 0 - > ~ 30 fps , 255 - > ~ 22f ps
* 0x04 Another var framerate ctrl reg2 = = 3 , reg3 = = 0 : 0 - > ~ 30 fps ,
* 63 - > ~ 27 fps , the 2 msb ' s must always be 1 ! !
* 0x05 Another var framerate ctrl reg2 = = 3 , reg3 = = 0 , reg4 = = 0xc0 :
* 1 - > ~ 30 fps , 2 - > ~ 20 fps
* 0x0e Exposure bits 0 - 7 , 0 - 448 , 0 = use full frame time
* 0x0f Exposure bit 8 , 0 - 448 , 448 = no exposure at all
2012-04-27 13:00:57 -03:00
* 0x10 Gain 0 - 31
* 0x12 Another gain 0 - 31 , unlike 0x10 this one seems to start with an
* amplification value of 1 rather then 0 at its lowest setting
2012-04-28 10:31:17 -03:00
* 0x21 Bitfield : 0 - 1 unused , 2 - 3 vflip / hflip , 4 - 5 unknown , 6 - 7 unused
2012-04-27 13:00:57 -03:00
* 0x80 Another framerate control , best left at 1 , moving it from 1 to
* 2 causes the framerate to become 3 / 4 th of what it was , and
* also seems to cause pixel averaging , resulting in an effective
* resolution of 320 x240 and thus a much blockier image
2012-04-28 10:31:17 -03:00
*
* The registers are accessed in the following functions :
*
* Page | Register | Function
* - - - - - + - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2012-09-23 10:29:43 -03:00
* 0 | 0x01 | setredbalance ( )
* 0 | 0x03 | setbluebalance ( )
2012-04-28 10:31:17 -03:00
* 0 | 0x0f . .0 x20 | setcolors ( )
* 0 | 0xa2 . .0 xab | setbrightcont ( )
2012-09-09 15:02:21 -03:00
* 0 | 0xb6 | setsharpness ( )
2012-04-28 10:31:17 -03:00
* 0 | 0xc6 | setwhitebalance ( )
* 0 | 0xdc | setbrightcont ( ) , setcolors ( )
* 3 | 0x02 | setexposure ( )
2012-04-28 10:12:28 -03:00
* 3 | 0x10 , 0x12 | setgain ( )
2012-04-28 10:31:17 -03:00
* 3 | 0x11 | setcolors ( ) , setgain ( ) , setexposure ( ) , sethvflip ( )
* 3 | 0x21 | sethvflip ( )
*/
2009-11-02 08:13:21 -03:00
2011-08-21 19:56:57 -03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-01-28 16:33:38 -03:00
# include <linux/input.h>
2009-11-02 08:13:21 -03:00
# include "gspca.h"
2012-02-27 05:40:47 -03:00
/* Include pac common sof detection functions */
# include "pac_common.h"
2009-11-02 08:13:21 -03:00
2012-09-23 10:29:43 -03:00
# define PAC7302_RGB_BALANCE_MIN 0
# define PAC7302_RGB_BALANCE_MAX 200
# define PAC7302_RGB_BALANCE_DEFAULT 100
# define PAC7302_GAIN_DEFAULT 15
# define PAC7302_GAIN_KNEE 42
# define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */
# define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
2012-05-14 11:16:09 -03:00
[media] gspca: don't break long lines
Due to the 80-cols restrictions, and latter due to checkpatch
warnings, several strings were broken into multiple lines. This
is not considered a good practice anymore, as it makes harder
to grep for strings at the source code.
As we're right now fixing other drivers due to KERN_CONT, we need
to be able to identify what printk strings don't end with a "\n".
It is a way easier to detect those if we don't break long lines.
So, join those continuation lines.
The patch was generated via the script below, and manually
adjusted if needed.
</script>
use Text::Tabs;
while (<>) {
if ($next ne "") {
$c=$_;
if ($c =~ /^\s+\"(.*)/) {
$c2=$1;
$next =~ s/\"\n$//;
$n = expand($next);
$funpos = index($n, '(');
$pos = index($c2, '",');
if ($funpos && $pos > 0) {
$s1 = substr $c2, 0, $pos + 2;
$s2 = ' ' x ($funpos + 1) . substr $c2, $pos + 2;
$s2 =~ s/^\s+//;
$s2 = ' ' x ($funpos + 1) . $s2 if ($s2 ne "");
print unexpand("$next$s1\n");
print unexpand("$s2\n") if ($s2 ne "");
} else {
print "$next$c2\n";
}
$next="";
next;
} else {
print $next;
}
$next="";
} else {
if (m/\"$/) {
if (!m/\\n\"$/) {
$next=$_;
next;
}
}
}
print $_;
}
</script>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-10-18 17:44:16 -02:00
MODULE_AUTHOR ( " Jean-Francois Moine <http://moinejf.free.fr>, Thomas Kaiser thomas@kaiser-linux.li " ) ;
2009-11-02 08:13:21 -03:00
MODULE_DESCRIPTION ( " Pixart PAC7302 " ) ;
MODULE_LICENSE ( " GPL " ) ;
struct sd {
struct gspca_dev gspca_dev ; /* !! must be the first item */
2012-05-14 11:16:09 -03:00
struct { /* brightness / contrast cluster */
struct v4l2_ctrl * brightness ;
struct v4l2_ctrl * contrast ;
} ;
struct v4l2_ctrl * saturation ;
struct v4l2_ctrl * white_balance ;
struct v4l2_ctrl * red_balance ;
struct v4l2_ctrl * blue_balance ;
struct { /* flip cluster */
struct v4l2_ctrl * hflip ;
struct v4l2_ctrl * vflip ;
} ;
2012-09-09 15:02:21 -03:00
struct v4l2_ctrl * sharpness ;
2009-11-26 14:28:48 -03:00
u8 flags ;
# define FL_HFLIP 0x01 /* mirrored by default */
# define FL_VFLIP 0x02 /* vertical flipped by default */
2009-11-02 08:13:21 -03:00
u8 sof_read ;
2012-02-27 05:40:47 -03:00
s8 autogain_ignore_frames ;
2009-11-02 08:13:21 -03:00
atomic_t avg_lum ;
} ;
static const struct v4l2_pix_format vga_mode [ ] = {
{ 640 , 480 , V4L2_PIX_FMT_PJPG , V4L2_FIELD_NONE ,
. bytesperline = 640 ,
. sizeimage = 640 * 480 * 3 / 8 + 590 ,
. colorspace = V4L2_COLORSPACE_JPEG ,
2012-02-27 05:15:12 -03:00
} ,
2009-11-02 08:13:21 -03:00
} ;
# define LOAD_PAGE3 255
# define END_OF_SEQUENCE 0
2012-02-27 05:15:12 -03:00
static const u8 init_7302 [ ] = {
2009-11-02 08:13:21 -03:00
/* index,value */
0xff , 0x01 , /* page 1 */
0x78 , 0x00 , /* deactivate */
0xff , 0x01 ,
0x78 , 0x40 , /* led off */
} ;
2012-02-27 05:15:12 -03:00
static const u8 start_7302 [ ] = {
2009-11-02 08:13:21 -03:00
/* index, len, [value]* */
0xff , 1 , 0x00 , /* page 0 */
0x00 , 12 , 0x01 , 0x40 , 0x40 , 0x40 , 0x01 , 0xe0 , 0x02 , 0x80 ,
0x00 , 0x00 , 0x00 , 0x00 ,
0x0d , 24 , 0x03 , 0x01 , 0x00 , 0xb5 , 0x07 , 0xcb , 0x00 , 0x00 ,
0x07 , 0xc8 , 0x00 , 0xea , 0x07 , 0xcf , 0x07 , 0xf7 ,
0x07 , 0x7e , 0x01 , 0x0b , 0x00 , 0x00 , 0x00 , 0x11 ,
0x26 , 2 , 0xaa , 0xaa ,
0x2e , 1 , 0x31 ,
0x38 , 1 , 0x01 ,
0x3a , 3 , 0x14 , 0xff , 0x5a ,
0x43 , 11 , 0x00 , 0x0a , 0x18 , 0x11 , 0x01 , 0x2c , 0x88 , 0x11 ,
0x00 , 0x54 , 0x11 ,
0x55 , 1 , 0x00 ,
2012-02-27 05:15:12 -03:00
0x62 , 4 , 0x10 , 0x1e , 0x1e , 0x18 ,
2009-11-02 08:13:21 -03:00
0x6b , 1 , 0x00 ,
0x6e , 3 , 0x08 , 0x06 , 0x00 ,
0x72 , 3 , 0x00 , 0xff , 0x00 ,
0x7d , 23 , 0x01 , 0x01 , 0x58 , 0x46 , 0x50 , 0x3c , 0x50 , 0x3c ,
0x54 , 0x46 , 0x54 , 0x56 , 0x52 , 0x50 , 0x52 , 0x50 ,
0x56 , 0x64 , 0xa4 , 0x00 , 0xda , 0x00 , 0x00 ,
0xa2 , 10 , 0x22 , 0x2c , 0x3c , 0x54 , 0x69 , 0x7c , 0x9c , 0xb9 ,
0xd2 , 0xeb ,
0xaf , 1 , 0x02 ,
0xb5 , 2 , 0x08 , 0x08 ,
0xb8 , 2 , 0x08 , 0x88 ,
0xc4 , 4 , 0xae , 0x01 , 0x04 , 0x01 ,
0xcc , 1 , 0x00 ,
0xd1 , 11 , 0x01 , 0x30 , 0x49 , 0x5e , 0x6f , 0x7f , 0x8e , 0xa9 ,
0xc1 , 0xd7 , 0xec ,
0xdc , 1 , 0x01 ,
0xff , 1 , 0x01 , /* page 1 */
0x12 , 3 , 0x02 , 0x00 , 0x01 ,
0x3e , 2 , 0x00 , 0x00 ,
0x76 , 5 , 0x01 , 0x20 , 0x40 , 0x00 , 0xf2 ,
0x7c , 1 , 0x00 ,
0x7f , 10 , 0x4b , 0x0f , 0x01 , 0x2c , 0x02 , 0x58 , 0x03 , 0x20 ,
0x02 , 0x00 ,
0x96 , 5 , 0x01 , 0x10 , 0x04 , 0x01 , 0x04 ,
0xc8 , 14 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x07 , 0x00 , 0x00 ,
0x07 , 0x00 , 0x01 , 0x07 , 0x04 , 0x01 ,
0xd8 , 1 , 0x01 ,
0xdb , 2 , 0x00 , 0x01 ,
0xde , 7 , 0x00 , 0x01 , 0x04 , 0x04 , 0x00 , 0x00 , 0x00 ,
0xe6 , 4 , 0x00 , 0x00 , 0x00 , 0x01 ,
0xeb , 1 , 0x00 ,
0xff , 1 , 0x02 , /* page 2 */
0x22 , 1 , 0x00 ,
0xff , 1 , 0x03 , /* page 3 */
0 , LOAD_PAGE3 , /* load the page 3 */
0x11 , 1 , 0x01 ,
0xff , 1 , 0x02 , /* page 2 */
0x13 , 1 , 0x00 ,
0x22 , 4 , 0x1f , 0xa4 , 0xf0 , 0x96 ,
0x27 , 2 , 0x14 , 0x0c ,
0x2a , 5 , 0xc8 , 0x00 , 0x18 , 0x12 , 0x22 ,
0x64 , 8 , 0x00 , 0x00 , 0xf0 , 0x01 , 0x14 , 0x44 , 0x44 , 0x44 ,
0x6e , 1 , 0x08 ,
0xff , 1 , 0x01 , /* page 1 */
0x78 , 1 , 0x00 ,
0 , END_OF_SEQUENCE /* end of sequence */
} ;
# define SKIP 0xaa
/* page 3 - the value SKIP says skip the index - see reg_w_page() */
2012-02-27 05:15:12 -03:00
static const u8 page3_7302 [ ] = {
2010-02-17 11:59:19 -03:00
0x90 , 0x40 , 0x03 , 0x00 , 0xc0 , 0x01 , 0x14 , 0x16 ,
2009-11-02 08:13:21 -03:00
0x14 , 0x12 , 0x00 , 0x00 , 0x00 , 0x02 , 0x33 , 0x00 ,
0x0f , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x47 , 0x01 , 0xb3 , 0x01 , 0x00 ,
0x00 , 0x08 , 0x00 , 0x00 , 0x0d , 0x00 , 0x00 , 0x21 ,
0x00 , 0x00 , 0x00 , 0x54 , 0xf4 , 0x02 , 0x52 , 0x54 ,
0xa4 , 0xb8 , 0xe0 , 0x2a , 0xf6 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x1e , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0xfc , 0x00 , 0xf2 , 0x1f , 0x04 , 0x00 , 0x00 ,
2010-01-11 15:06:12 -03:00
SKIP , 0x00 , 0x00 , 0xc0 , 0xc0 , 0x10 , 0x00 , 0x00 ,
2009-11-02 08:13:21 -03:00
0x00 , 0x40 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x40 , 0xff , 0x03 , 0x19 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xc8 , 0xc8 , 0xc8 ,
0xc8 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x50 ,
0x08 , 0x10 , 0x24 , 0x40 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x01 , 0x00 , 0x02 , 0x47 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x02 , 0xfa , 0x00 , 0x64 , 0x5a , 0x28 , 0x00 ,
0x00
} ;
2010-01-13 15:09:14 -03:00
static void reg_w_buf ( struct gspca_dev * gspca_dev ,
2012-02-27 05:15:12 -03:00
u8 index ,
2010-12-28 06:59:04 -03:00
const u8 * buffer , int len )
2009-11-02 08:13:21 -03:00
{
2009-11-05 05:35:08 -03:00
int ret ;
2010-01-13 15:09:14 -03:00
if ( gspca_dev - > usb_err < 0 )
return ;
2009-11-02 08:13:21 -03:00
memcpy ( gspca_dev - > usb_buf , buffer , len ) ;
2009-11-05 05:35:08 -03:00
ret = usb_control_msg ( gspca_dev - > dev ,
2009-11-02 08:13:21 -03:00
usb_sndctrlpipe ( gspca_dev - > dev , 0 ) ,
2010-06-24 04:50:26 -03:00
0 , /* request */
2009-11-02 08:13:21 -03:00
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , /* value */
index , gspca_dev - > usb_buf , len ,
500 ) ;
2010-01-13 15:09:14 -03:00
if ( ret < 0 ) {
2012-02-27 05:15:12 -03:00
pr_err ( " reg_w_buf failed i: %02x error %d \n " ,
2011-08-21 19:56:57 -03:00
index , ret ) ;
2010-01-13 15:09:14 -03:00
gspca_dev - > usb_err = ret ;
}
2009-11-02 08:13:21 -03:00
}
2010-01-13 15:09:14 -03:00
static void reg_w ( struct gspca_dev * gspca_dev ,
2012-02-27 05:15:12 -03:00
u8 index ,
u8 value )
2009-11-02 08:13:21 -03:00
{
2009-11-05 05:35:08 -03:00
int ret ;
2010-01-13 15:09:14 -03:00
if ( gspca_dev - > usb_err < 0 )
return ;
2009-11-02 08:13:21 -03:00
gspca_dev - > usb_buf [ 0 ] = value ;
2009-11-05 05:35:08 -03:00
ret = usb_control_msg ( gspca_dev - > dev ,
2009-11-02 08:13:21 -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 ) ;
2010-01-13 15:09:14 -03:00
if ( ret < 0 ) {
2012-02-27 05:15:12 -03:00
pr_err ( " reg_w() failed i: %02x v: %02x error %d \n " ,
2011-08-21 19:56:57 -03:00
index , value , ret ) ;
2010-01-13 15:09:14 -03:00
gspca_dev - > usb_err = ret ;
}
2009-11-02 08:13:21 -03:00
}
2010-01-13 15:09:14 -03:00
static void reg_w_seq ( struct gspca_dev * gspca_dev ,
2012-02-27 05:15:12 -03:00
const u8 * seq , int len )
2009-11-02 08:13:21 -03:00
{
while ( - - len > = 0 ) {
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , seq [ 0 ] , seq [ 1 ] ) ;
2009-11-02 08:13:21 -03:00
seq + = 2 ;
}
}
/* load the beginning of a page */
2010-01-13 15:09:14 -03:00
static void reg_w_page ( struct gspca_dev * gspca_dev ,
2012-02-27 05:15:12 -03:00
const u8 * page , int len )
2009-11-02 08:13:21 -03:00
{
int index ;
2009-11-07 05:52:02 -03:00
int ret = 0 ;
2009-11-02 08:13:21 -03:00
2010-01-13 15:09:14 -03:00
if ( gspca_dev - > usb_err < 0 )
return ;
2009-11-02 08:13:21 -03:00
for ( index = 0 ; index < len ; index + + ) {
if ( page [ index ] = = SKIP ) /* skip this index */
continue ;
gspca_dev - > usb_buf [ 0 ] = page [ index ] ;
2009-11-05 05:35:08 -03:00
ret = usb_control_msg ( gspca_dev - > dev ,
2009-11-02 08:13:21 -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 ) {
2012-02-27 05:15:12 -03:00
pr_err ( " reg_w_page() failed i: %02x v: %02x error %d \n " ,
2011-08-21 19:56:57 -03:00
index , page [ index ] , ret ) ;
2010-01-13 15:09:14 -03:00
gspca_dev - > usb_err = ret ;
2009-11-07 05:52:02 -03:00
break ;
}
2009-11-02 08:13:21 -03:00
}
}
/* output a variable sequence */
2010-01-13 15:09:14 -03:00
static void reg_w_var ( struct gspca_dev * gspca_dev ,
2012-02-27 05:15:12 -03:00
const u8 * seq ,
const u8 * page3 , unsigned int page3_len )
2009-11-02 08:13:21 -03:00
{
int index , len ;
for ( ; ; ) {
index = * seq + + ;
len = * seq + + ;
switch ( len ) {
case END_OF_SEQUENCE :
2010-01-13 15:09:14 -03:00
return ;
2009-11-02 08:13:21 -03:00
case LOAD_PAGE3 :
2010-01-13 15:09:14 -03:00
reg_w_page ( gspca_dev , page3 , page3_len ) ;
2009-11-02 08:13:21 -03:00
break ;
default :
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:09:14 -03:00
return ;
2009-11-02 08:13:21 -03:00
}
while ( len > 0 ) {
if ( len < 8 ) {
2010-01-13 15:09:14 -03:00
reg_w_buf ( gspca_dev ,
2009-11-07 05:52:02 -03:00
index , seq , len ) ;
2009-11-02 08:13:21 -03:00
seq + = len ;
break ;
}
2010-01-13 15:09:14 -03:00
reg_w_buf ( gspca_dev , index , seq , 8 ) ;
2009-11-02 08:13:21 -03:00
seq + = 8 ;
index + = 8 ;
len - = 8 ;
}
}
}
/* not reached */
}
/* this function is called at probe time for pac7302 */
static int sd_config ( struct gspca_dev * gspca_dev ,
const struct usb_device_id * id )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
struct cam * cam ;
cam = & gspca_dev - > cam ;
cam - > cam_mode = vga_mode ; /* only 640x480 */
cam - > nmodes = ARRAY_SIZE ( vga_mode ) ;
2009-11-26 14:28:48 -03:00
sd - > flags = id - > driver_info ;
2009-11-02 08:13:21 -03:00
return 0 ;
}
2010-01-13 15:09:14 -03:00
static void setbrightcont ( struct gspca_dev * gspca_dev )
2009-11-02 08:13:21 -03:00
{
struct sd * sd = ( struct sd * ) gspca_dev ;
int i , v ;
2012-02-27 05:15:12 -03:00
static const u8 max [ 10 ] =
2009-11-02 08:13:21 -03:00
{ 0x29 , 0x33 , 0x42 , 0x5a , 0x6e , 0x80 , 0x9f , 0xbb ,
0xd4 , 0xec } ;
2012-02-27 05:15:12 -03:00
static const u8 delta [ 10 ] =
2009-11-02 08:13:21 -03:00
{ 0x35 , 0x33 , 0x33 , 0x2f , 0x2a , 0x25 , 0x1e , 0x17 ,
0x11 , 0x0b } ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x00 ) ; /* page 0 */
2009-11-02 08:13:21 -03:00
for ( i = 0 ; i < 10 ; i + + ) {
v = max [ i ] ;
2014-07-17 12:31:23 -03:00
v + = ( sd - > brightness - > val - ( s32 ) sd - > brightness - > maximum )
* 150 / ( s32 ) sd - > brightness - > maximum ; /* 200 ? */
v - = delta [ i ] * sd - > contrast - > val / ( s32 ) sd - > contrast - > maximum ;
2009-11-02 08:13:21 -03:00
if ( v < 0 )
v = 0 ;
else if ( v > 0xff )
v = 0xff ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xa2 + i , v ) ;
2009-11-02 08:13:21 -03:00
}
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xdc , 0x01 ) ;
2009-11-02 08:13:21 -03:00
}
2010-01-13 15:09:14 -03:00
static void setcolors ( struct gspca_dev * gspca_dev )
2009-11-02 08:13:21 -03:00
{
struct sd * sd = ( struct sd * ) gspca_dev ;
int i , v ;
static const int a [ 9 ] =
{ 217 , - 212 , 0 , - 101 , 170 , - 67 , - 38 , - 315 , 355 } ;
static const int b [ 9 ] =
{ 19 , 106 , 0 , 19 , 106 , 1 , 19 , 106 , 1 } ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x03 ) ; /* page 3 */
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
reg_w ( gspca_dev , 0xff , 0x00 ) ; /* page 0 */
2009-11-02 08:13:21 -03:00
for ( i = 0 ; i < 9 ; i + + ) {
2014-07-17 12:31:23 -03:00
v = a [ i ] * sd - > saturation - > val / ( s32 ) sd - > saturation - > maximum ;
2012-05-14 11:16:09 -03:00
v + = b [ i ] ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0x0f + 2 * i , ( v > > 8 ) & 0x07 ) ;
reg_w ( gspca_dev , 0x0f + 2 * i + 1 , v ) ;
2009-11-02 08:13:21 -03:00
}
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xdc , 0x01 ) ;
2009-11-02 08:13:21 -03:00
}
2010-01-13 15:09:14 -03:00
static void setwhitebalance ( struct gspca_dev * gspca_dev )
2009-11-08 04:35:12 -03:00
{
struct sd * sd = ( struct sd * ) gspca_dev ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x00 ) ; /* page 0 */
2012-05-14 11:16:09 -03:00
reg_w ( gspca_dev , 0xc6 , sd - > white_balance - > val ) ;
2009-11-08 04:35:12 -03:00
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xdc , 0x01 ) ;
2009-11-08 04:35:12 -03:00
}
2012-09-23 10:29:43 -03:00
static u8 rgbbalance_ctrl_to_reg_value ( s32 rgb_ctrl_val )
{
const unsigned int k = 1000 ; /* precision factor */
unsigned int norm ;
/* Normed value [0...k] */
norm = k * ( rgb_ctrl_val - PAC7302_RGB_BALANCE_MIN )
/ ( PAC7302_RGB_BALANCE_MAX - PAC7302_RGB_BALANCE_MIN ) ;
/* Qudratic apporach improves control at small (register) values: */
return 64 * norm * norm / ( k * k ) + 32 * norm / k + 32 ;
/* Y = 64*X*X + 32*X + 32
* = > register values 0x20 - 0x80 ; Windows driver uses these limits */
/* NOTE: for full value range (0x00-0xff) use
* Y = 254 * X * X + X
* = > 254 * norm * norm / ( k * k ) + 1 * norm / k */
}
2010-01-13 15:09:14 -03:00
static void setredbalance ( struct gspca_dev * gspca_dev )
2009-11-07 15:15:56 -03:00
{
struct sd * sd = ( struct sd * ) gspca_dev ;
2012-09-23 10:29:43 -03:00
reg_w ( gspca_dev , 0xff , 0x00 ) ; /* page 0 */
reg_w ( gspca_dev , 0x01 ,
rgbbalance_ctrl_to_reg_value ( sd - > red_balance - > val ) ) ;
2009-11-07 15:15:56 -03:00
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xdc , 0x01 ) ;
2009-11-07 15:15:56 -03:00
}
2010-01-13 15:09:14 -03:00
static void setbluebalance ( struct gspca_dev * gspca_dev )
2009-11-07 15:15:56 -03:00
{
struct sd * sd = ( struct sd * ) gspca_dev ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x00 ) ; /* page 0 */
2012-09-23 10:29:43 -03:00
reg_w ( gspca_dev , 0x03 ,
rgbbalance_ctrl_to_reg_value ( sd - > blue_balance - > val ) ) ;
2009-11-07 15:15:56 -03:00
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xdc , 0x01 ) ;
2009-11-07 15:15:56 -03:00
}
2010-01-13 15:09:14 -03:00
static void setgain ( struct gspca_dev * gspca_dev )
2009-11-02 08:13:21 -03:00
{
2012-04-28 10:12:28 -03:00
u8 reg10 , reg12 ;
2012-05-14 11:16:09 -03:00
if ( gspca_dev - > gain - > val < 32 ) {
reg10 = gspca_dev - > gain - > val ;
2012-04-28 10:12:28 -03:00
reg12 = 0 ;
} else {
reg10 = 31 ;
2012-05-14 11:16:09 -03:00
reg12 = gspca_dev - > gain - > val - 31 ;
2012-04-28 10:12:28 -03:00
}
2009-11-02 08:13:21 -03:00
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x03 ) ; /* page 3 */
2012-04-28 10:12:28 -03:00
reg_w ( gspca_dev , 0x10 , reg10 ) ;
reg_w ( gspca_dev , 0x12 , reg12 ) ;
2009-11-02 08:13:21 -03:00
/* load registers to sensor (Bit 0, auto clear) */
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
2009-11-02 08:13:21 -03:00
}
2010-01-13 15:09:14 -03:00
static void setexposure ( struct gspca_dev * gspca_dev )
2009-11-02 08:13:21 -03:00
{
2012-02-27 05:15:12 -03:00
u8 clockdiv ;
u16 exposure ;
2010-02-17 11:59:19 -03:00
2012-04-28 10:31:17 -03:00
/*
* Register 2 of frame 3 contains the clock divider configuring the
* no fps according to the formula : 90 / reg . sd - > exposure is the
* desired exposure time in 0.5 ms .
*/
2012-05-14 11:16:09 -03:00
clockdiv = ( 90 * gspca_dev - > exposure - > val + 1999 ) / 2000 ;
2010-02-17 11:59:19 -03:00
2012-04-28 10:31:17 -03:00
/*
* Note clockdiv = 3 also works , but when running at 30 fps , depending
* on the scene being recorded , the camera switches to another
* quantization table for certain JPEG blocks , and we don ' t know how
* to decompress these blocks . So we cap the framerate at 15 fps .
*/
2010-02-17 11:59:19 -03:00
if ( clockdiv < 6 )
clockdiv = 6 ;
else if ( clockdiv > 63 )
clockdiv = 63 ;
2012-04-28 10:31:17 -03:00
/*
* Register 2 MUST be a multiple of 3 , except when between 6 and 12 ?
* Always round up , otherwise we cannot get the desired frametime
* using the partial frame time exposure control .
*/
2010-02-17 11:59:19 -03:00
if ( clockdiv < 6 | | clockdiv > 12 )
clockdiv = ( ( clockdiv + 2 ) / 3 ) * 3 ;
2012-04-28 10:31:17 -03:00
/*
* frame exposure time in ms = 1000 * clockdiv / 90 - >
* exposure = ( sd - > exposure / 2 ) * 448 / ( 1000 * clockdiv / 90 )
*/
2012-05-14 11:16:09 -03:00
exposure = ( gspca_dev - > exposure - > val * 45 * 448 ) / ( 1000 * clockdiv ) ;
2010-02-17 11:59:19 -03:00
/* 0 = use full frametime, 448 = no exposure, reverse it */
exposure = 448 - exposure ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x03 ) ; /* page 3 */
2010-02-17 11:59:19 -03:00
reg_w ( gspca_dev , 0x02 , clockdiv ) ;
reg_w ( gspca_dev , 0x0e , exposure & 0xff ) ;
reg_w ( gspca_dev , 0x0f , exposure > > 8 ) ;
2009-11-02 08:13:21 -03:00
/* load registers to sensor (Bit 0, auto clear) */
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
2009-11-02 08:13:21 -03:00
}
2010-01-13 15:09:14 -03:00
static void sethvflip ( struct gspca_dev * gspca_dev )
2009-11-02 08:13:21 -03:00
{
struct sd * sd = ( struct sd * ) gspca_dev ;
2009-11-26 14:28:48 -03:00
u8 data , hflip , vflip ;
2012-05-14 11:16:09 -03:00
hflip = sd - > hflip - > val ;
2009-11-26 14:28:48 -03:00
if ( sd - > flags & FL_HFLIP )
hflip = ! hflip ;
2012-05-14 11:16:09 -03:00
vflip = sd - > vflip - > val ;
2009-11-26 14:28:48 -03:00
if ( sd - > flags & FL_VFLIP )
vflip = ! vflip ;
2009-11-02 08:13:21 -03:00
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x03 ) ; /* page 3 */
2009-11-26 14:28:48 -03:00
data = ( hflip ? 0x08 : 0x00 ) | ( vflip ? 0x04 : 0x00 ) ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0x21 , data ) ;
2009-11-02 08:13:21 -03:00
/* load registers to sensor (Bit 0, auto clear) */
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0x11 , 0x01 ) ;
2009-11-02 08:13:21 -03:00
}
2012-09-09 15:02:21 -03:00
static void setsharpness ( struct gspca_dev * gspca_dev )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
reg_w ( gspca_dev , 0xff , 0x00 ) ; /* page 0 */
reg_w ( gspca_dev , 0xb6 , sd - > sharpness - > val ) ;
reg_w ( gspca_dev , 0xdc , 0x01 ) ;
}
2009-11-02 08:13:21 -03:00
/* this function is called at probe and resume time for pac7302 */
static int sd_init ( struct gspca_dev * gspca_dev )
{
2010-01-13 15:09:14 -03:00
reg_w_seq ( gspca_dev , init_7302 , sizeof ( init_7302 ) / 2 ) ;
return gspca_dev - > usb_err ;
2009-11-02 08:13:21 -03:00
}
2012-05-14 11:16:09 -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 = PAC7302_EXPOSURE_DEFAULT ;
gspca_dev - > gain - > val = PAC7302_GAIN_DEFAULT ;
sd - > autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES ;
}
if ( ! gspca_dev - > streaming )
return 0 ;
switch ( ctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
setbrightcont ( gspca_dev ) ;
break ;
case V4L2_CID_SATURATION :
setcolors ( gspca_dev ) ;
break ;
case V4L2_CID_WHITE_BALANCE_TEMPERATURE :
setwhitebalance ( gspca_dev ) ;
break ;
case V4L2_CID_RED_BALANCE :
setredbalance ( gspca_dev ) ;
break ;
case V4L2_CID_BLUE_BALANCE :
setbluebalance ( gspca_dev ) ;
break ;
case V4L2_CID_AUTOGAIN :
if ( gspca_dev - > exposure - > is_new | | ( ctrl - > is_new & & ctrl - > val ) )
setexposure ( gspca_dev ) ;
if ( gspca_dev - > gain - > is_new | | ( ctrl - > is_new & & ctrl - > val ) )
setgain ( gspca_dev ) ;
break ;
case V4L2_CID_HFLIP :
sethvflip ( gspca_dev ) ;
break ;
2012-09-09 15:02:21 -03:00
case V4L2_CID_SHARPNESS :
setsharpness ( gspca_dev ) ;
break ;
2012-05-14 11:16:09 -03:00
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-09-09 15:02:21 -03:00
v4l2_ctrl_handler_init ( hdl , 12 ) ;
2012-05-14 11:16:09 -03:00
sd - > brightness = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_BRIGHTNESS , 0 , 32 , 1 , 16 ) ;
sd - > contrast = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_CONTRAST , 0 , 255 , 1 , 127 ) ;
sd - > saturation = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_SATURATION , 0 , 255 , 1 , 127 ) ;
sd - > white_balance = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_WHITE_BALANCE_TEMPERATURE ,
2012-09-09 15:02:22 -03:00
0 , 255 , 1 , 55 ) ;
2012-05-14 11:16:09 -03:00
sd - > red_balance = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
2012-09-23 10:29:43 -03:00
V4L2_CID_RED_BALANCE ,
PAC7302_RGB_BALANCE_MIN ,
PAC7302_RGB_BALANCE_MAX ,
1 , PAC7302_RGB_BALANCE_DEFAULT ) ;
2012-05-14 11:16:09 -03:00
sd - > blue_balance = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
2012-09-23 10:29:43 -03:00
V4L2_CID_BLUE_BALANCE ,
PAC7302_RGB_BALANCE_MIN ,
PAC7302_RGB_BALANCE_MAX ,
1 , PAC7302_RGB_BALANCE_DEFAULT ) ;
2012-05-14 11:16:09 -03:00
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 , 0 , 1023 , 1 ,
PAC7302_EXPOSURE_DEFAULT ) ;
gspca_dev - > gain = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_GAIN , 0 , 62 , 1 ,
PAC7302_GAIN_DEFAULT ) ;
sd - > hflip = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_HFLIP , 0 , 1 , 1 , 0 ) ;
sd - > vflip = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_VFLIP , 0 , 1 , 1 , 0 ) ;
2012-09-09 15:02:21 -03:00
sd - > sharpness = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_SHARPNESS , 0 , 15 , 1 , 8 ) ;
2012-05-14 11:16:09 -03:00
if ( hdl - > error ) {
pr_err ( " Could not initialize controls \n " ) ;
return hdl - > error ;
}
v4l2_ctrl_cluster ( 2 , & sd - > brightness ) ;
v4l2_ctrl_auto_cluster ( 3 , & gspca_dev - > autogain , 0 , false ) ;
v4l2_ctrl_cluster ( 2 , & sd - > hflip ) ;
return 0 ;
}
/* -- start the camera -- */
2009-11-02 08:13:21 -03:00
static int sd_start ( struct gspca_dev * gspca_dev )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
2010-01-13 15:09:14 -03:00
reg_w_var ( gspca_dev , start_7302 ,
2010-01-13 08:30:30 -03:00
page3_7302 , sizeof ( page3_7302 ) ) ;
2009-11-02 08:13:21 -03:00
sd - > sof_read = 0 ;
2012-05-14 11:16:09 -03:00
sd - > autogain_ignore_frames = 0 ;
atomic_set ( & sd - > avg_lum , 270 + sd - > brightness - > val ) ;
2009-11-02 08:13:21 -03:00
/* start stream */
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x01 ) ;
reg_w ( gspca_dev , 0x78 , 0x01 ) ;
2009-11-02 08:13:21 -03:00
2010-01-13 15:09:14 -03:00
return gspca_dev - > usb_err ;
2009-11-02 08:13:21 -03:00
}
static void sd_stopN ( struct gspca_dev * gspca_dev )
{
2009-11-07 05:52:02 -03:00
2009-11-07 05:45:33 -03:00
/* stop stream */
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x01 ) ;
reg_w ( gspca_dev , 0x78 , 0x00 ) ;
2009-11-02 08:13:21 -03:00
}
/* called on streamoff with alt 0 and on disconnect for pac7302 */
static void sd_stop0 ( struct gspca_dev * gspca_dev )
{
if ( ! gspca_dev - > present )
return ;
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x01 ) ;
reg_w ( gspca_dev , 0x78 , 0x40 ) ;
2009-11-02 08:13:21 -03:00
}
static void do_autogain ( struct gspca_dev * gspca_dev )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
int avg_lum = atomic_read ( & sd - > avg_lum ) ;
2010-02-17 11:59:19 -03:00
int desired_lum ;
const int deadzone = 30 ;
2009-11-02 08:13:21 -03:00
2012-02-27 05:40:47 -03:00
if ( sd - > autogain_ignore_frames < 0 )
2009-11-02 08:13:21 -03:00
return ;
2012-02-27 05:40:47 -03:00
if ( sd - > autogain_ignore_frames > 0 ) {
2009-11-02 08:13:21 -03:00
sd - > autogain_ignore_frames - - ;
2012-02-27 05:40:47 -03:00
} else {
2012-05-14 11:16:09 -03:00
desired_lum = 270 + sd - > brightness - > val ;
2012-02-27 05:40:47 -03:00
2012-05-14 11:16:09 -03:00
if ( gspca_expo_autogain ( gspca_dev , avg_lum , desired_lum ,
deadzone , PAC7302_GAIN_KNEE ,
PAC7302_EXPOSURE_KNEE ) )
sd - > autogain_ignore_frames =
PAC_AUTOGAIN_IGNORE_FRAMES ;
2012-02-27 05:40:47 -03:00
}
2009-11-02 08:13:21 -03:00
}
2012-02-27 05:21:57 -03:00
/* JPEG header */
static const u8 jpeg_header [ ] = {
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 */
0x02 , 0x80 , /* height = 640 (image rotated) */
0x01 , 0xe0 , /* width = 480 */
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 */
2009-11-02 08:13:21 -03:00
} ;
/* this function is run at interrupt level */
static void sd_pkt_scan ( struct gspca_dev * gspca_dev ,
2009-11-13 09:21:03 -03:00
u8 * data , /* isoc packet */
2009-11-02 08:13:21 -03:00
int len ) /* iso packet length */
{
struct sd * sd = ( struct sd * ) gspca_dev ;
2010-06-27 03:08:19 -03:00
u8 * image ;
2012-02-27 05:15:12 -03:00
u8 * sof ;
2009-11-02 08:13:21 -03:00
2013-02-04 13:17:55 -03:00
sof = pac_find_sof ( gspca_dev , & sd - > sof_read , data , len ) ;
2009-11-02 08:13:21 -03:00
if ( sof ) {
int n , lum_offset , footer_length ;
2012-04-28 10:31:17 -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 = 61 + sizeof pac_sof_marker ;
footer_length = 74 ;
/* 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 ;
} else {
gspca_frame_add ( gspca_dev , INTER_PACKET , data , n ) ;
2009-11-02 08:13:21 -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 ) ;
2009-11-02 08:13:21 -03:00
n = sof - data ;
len - = n ;
data = sof ;
/* Get average lumination */
if ( gspca_dev - > last_packet_type = = LAST_PACKET & &
n > = lum_offset )
atomic_set ( & sd - > avg_lum , data [ - lum_offset ] +
data [ - lum_offset + 1 ] ) ;
/* Start the new frame with the jpeg header */
/* The PAC7302 has the image rotated 90 degrees */
2012-02-27 05:21:57 -03:00
gspca_frame_add ( gspca_dev , FIRST_PACKET ,
jpeg_header , sizeof jpeg_header ) ;
2009-11-02 08:13:21 -03:00
}
2009-11-13 09:21:03 -03:00
gspca_frame_add ( gspca_dev , INTER_PACKET , data , len ) ;
2009-11-02 08:13:21 -03:00
}
2009-11-09 07:10:46 -03:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
static int sd_dbg_s_register ( struct gspca_dev * gspca_dev ,
2013-03-24 08:28:46 -03:00
const struct v4l2_dbg_register * reg )
2009-11-09 07:10:46 -03:00
{
2012-02-27 05:15:12 -03:00
u8 index ;
u8 value ;
2009-11-09 07:10:46 -03:00
2012-04-28 10:31:17 -03:00
/*
* reg - > reg : bit0 . .15 : reserved for register index ( wIndex is 16 bit
* long on the USB bus )
*/
2013-05-29 06:59:42 -03:00
if ( reg - > match . addr = = 0 & &
2009-11-09 07:10:46 -03:00
( reg - > reg < 0x000000ff ) & &
( reg - > val < = 0x000000ff )
) {
/* Currently writing to page 0 is only supported. */
/* reg_w() only supports 8bit index */
2012-02-27 05:15:12 -03:00
index = reg - > reg ;
value = reg - > val ;
2009-11-09 07:10:46 -03:00
2012-04-28 10:31:17 -03:00
/*
* Note that there shall be no access to other page
* by any other function between the page switch and
* the actual register write .
*/
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xff , 0x00 ) ; /* page 0 */
reg_w ( gspca_dev , index , value ) ;
2009-11-09 07:10:46 -03:00
2010-01-13 15:09:14 -03:00
reg_w ( gspca_dev , 0xdc , 0x01 ) ;
2009-11-09 07:10:46 -03:00
}
2010-01-13 15:09:14 -03:00
return gspca_dev - > usb_err ;
2009-11-09 07:10:46 -03:00
}
# endif
2013-01-24 19:29:03 -03:00
# if IS_ENABLED(CONFIG_INPUT)
2010-01-28 16:33:38 -03:00
static int sd_int_pkt_scan ( struct gspca_dev * gspca_dev ,
u8 * data , /* interrupt packet data */
2013-10-20 21:34:01 -03:00
int len ) /* interrupt packet length */
2010-01-28 16:33:38 -03:00
{
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
2009-11-02 08:13:21 -03:00
/* sub-driver description for pac7302 */
2010-01-05 12:39:02 -03:00
static const struct sd_desc sd_desc = {
2012-02-27 05:15:12 -03:00
. name = KBUILD_MODNAME ,
2009-11-02 08:13:21 -03:00
. config = sd_config ,
. init = sd_init ,
2012-05-14 11:16:09 -03:00
. init_controls = sd_init_controls ,
2009-11-02 08:13:21 -03:00
. start = sd_start ,
. stopN = sd_stopN ,
. stop0 = sd_stop0 ,
. pkt_scan = sd_pkt_scan ,
. dq_callback = do_autogain ,
2009-11-09 07:10:46 -03:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
. set_register = sd_dbg_s_register ,
# endif
2013-01-24 19:29:03 -03:00
# if IS_ENABLED(CONFIG_INPUT)
2010-01-28 16:33:38 -03:00
. int_pkt_scan = sd_int_pkt_scan ,
# endif
2009-11-02 08:13:21 -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 ( 0x06f8 , 0x3009 ) } ,
2012-02-27 04:58:59 -03:00
{ USB_DEVICE ( 0x06f8 , 0x301b ) } ,
2009-11-02 08:13:21 -03:00
{ USB_DEVICE ( 0x093a , 0x2620 ) } ,
{ USB_DEVICE ( 0x093a , 0x2621 ) } ,
2009-11-26 14:28:48 -03:00
{ USB_DEVICE ( 0x093a , 0x2622 ) , . driver_info = FL_VFLIP } ,
2014-07-09 06:20:44 -03:00
{ USB_DEVICE ( 0x093a , 0x2623 ) , . driver_info = FL_VFLIP } ,
2009-11-26 14:28:48 -03:00
{ USB_DEVICE ( 0x093a , 0x2624 ) , . driver_info = FL_VFLIP } ,
2010-06-14 17:21:37 -03:00
{ USB_DEVICE ( 0x093a , 0x2625 ) } ,
2009-11-02 08:13:21 -03:00
{ USB_DEVICE ( 0x093a , 0x2626 ) } ,
2012-05-15 12:05:36 -03:00
{ USB_DEVICE ( 0x093a , 0x2627 ) , . driver_info = FL_VFLIP } ,
2009-11-02 08:13:21 -03:00
{ USB_DEVICE ( 0x093a , 0x2628 ) } ,
2009-12-02 07:04:35 -03:00
{ USB_DEVICE ( 0x093a , 0x2629 ) , . driver_info = FL_VFLIP } ,
2009-11-02 08:13:21 -03:00
{ USB_DEVICE ( 0x093a , 0x262a ) } ,
{ USB_DEVICE ( 0x093a , 0x262c ) } ,
2011-12-30 19:15:53 -03:00
{ USB_DEVICE ( 0x145f , 0x013c ) } ,
2012-09-09 15:02:19 -03:00
{ USB_DEVICE ( 0x1ae7 , 0x2001 ) } , /* SpeedLink Snappy Mic SL-6825-SBK */
2009-11-02 08:13:21 -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 ,
2009-11-02 08:13:21 -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 = {
2012-02-27 05:15:12 -03:00
. name = KBUILD_MODNAME ,
2009-11-02 08:13:21 -03:00
. id_table = device_table ,
. probe = sd_probe ,
. disconnect = gspca_disconnect ,
# ifdef CONFIG_PM
. suspend = gspca_suspend ,
. resume = gspca_resume ,
2012-06-30 06:44:47 -03:00
. reset_resume = gspca_resume ,
2009-11-02 08:13:21 -03:00
# endif
} ;
2011-11-18 09:46:12 -08:00
module_usb_driver ( sd_driver ) ;