2008-06-30 15:50:11 -03:00
/*
* Mars - Semi MR97311A library
* Copyright ( C ) 2005 < bradlch @ hotmail . com >
*
* V4L2 by Jean - Francois Moine < http : //moinejf.free.fr>
*
* 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
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# define MODULE_NAME "mars"
# include "gspca.h"
# include "jpeg.h"
MODULE_AUTHOR ( " Michel Xhaard <mxhaard@users.sourceforge.net> " ) ;
MODULE_DESCRIPTION ( " GSPCA/Mars USB Camera Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev ; /* !! must be the first item */
char qindex ;
} ;
/* V4L2 controls supported by the driver */
static struct ctrl sd_ctrls [ ] = {
} ;
2008-07-05 11:49:20 -03:00
static struct v4l2_pix_format vga_mode [ ] = {
{ 320 , 240 , V4L2_PIX_FMT_JPEG , V4L2_FIELD_NONE ,
. bytesperline = 320 ,
. sizeimage = 320 * 240 * 3 / 8 + 589 ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. priv = 2 } ,
{ 640 , 480 , V4L2_PIX_FMT_JPEG , V4L2_FIELD_NONE ,
. bytesperline = 640 ,
. sizeimage = 640 * 480 * 3 / 8 + 590 ,
. colorspace = V4L2_COLORSPACE_JPEG ,
. priv = 1 } ,
2008-06-30 15:50:11 -03:00
} ;
/* MI Register table //elvis */
enum {
REG_HW_MI_0 ,
REG_HW_MI_1 ,
REG_HW_MI_2 ,
REG_HW_MI_3 ,
REG_HW_MI_4 ,
REG_HW_MI_5 ,
REG_HW_MI_6 ,
REG_HW_MI_7 ,
REG_HW_MI_9 = 0x09 ,
REG_HW_MI_B = 0x0B ,
REG_HW_MI_C ,
REG_HW_MI_D ,
REG_HW_MI_1E = 0x1E ,
REG_HW_MI_20 = 0x20 ,
REG_HW_MI_2B = 0x2B ,
REG_HW_MI_2C ,
REG_HW_MI_2D ,
REG_HW_MI_2E ,
REG_HW_MI_35 = 0x35 ,
REG_HW_MI_5F = 0x5f ,
REG_HW_MI_60 ,
REG_HW_MI_61 ,
REG_HW_MI_62 ,
REG_HW_MI_63 ,
REG_HW_MI_64 ,
REG_HW_MI_F1 = 0xf1 ,
2008-07-14 09:38:29 -03:00
ATTR_TOTAL_MI_REG = 0xf2
2008-06-30 15:50:11 -03:00
} ;
2008-07-14 09:38:29 -03:00
/* the bytes to write are in gspca_dev->usb_buf */
static int reg_w ( struct gspca_dev * gspca_dev ,
__u16 index , int len )
2008-06-30 15:50:11 -03:00
{
int rc ;
2008-07-14 09:38:29 -03:00
rc = usb_control_msg ( gspca_dev - > dev ,
usb_sndbulkpipe ( gspca_dev - > dev , 4 ) ,
2008-06-30 15:50:11 -03:00
0x12 ,
2008-07-14 09:38:29 -03:00
0xc8 , /* ?? */
2008-06-30 15:50:11 -03:00
0 , /* value */
2008-07-14 09:38:29 -03:00
index , gspca_dev - > usb_buf , len , 500 ) ;
2008-06-30 15:50:11 -03:00
if ( rc < 0 )
2008-07-03 11:09:12 -03:00
PDEBUG ( D_ERR , " reg write [%02x] error %d " , index , rc ) ;
2008-06-30 15:50:11 -03:00
return rc ;
}
2008-07-14 09:38:29 -03:00
static void bulk_w ( struct gspca_dev * gspca_dev ,
__u16 * pch ,
__u16 Address )
{
gspca_dev - > usb_buf [ 0 ] = 0x1f ;
gspca_dev - > usb_buf [ 1 ] = 0 ; /* control byte */
gspca_dev - > usb_buf [ 2 ] = Address ;
gspca_dev - > usb_buf [ 3 ] = * pch > > 8 ; /* high byte */
gspca_dev - > usb_buf [ 4 ] = * pch ; /* low byte */
2008-06-30 15:50:11 -03:00
2008-07-14 09:38:29 -03:00
reg_w ( gspca_dev , Address , 5 ) ;
2008-06-30 15:50:11 -03:00
}
/* this function is called at probe time */
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 - > epaddr = 0x01 ;
cam - > cam_mode = vga_mode ;
cam - > nmodes = sizeof vga_mode / sizeof vga_mode [ 0 ] ;
sd - > qindex = 1 ; /* set the quantization table */
return 0 ;
}
2008-09-03 17:12:16 -03:00
/* this function is called at probe and resume time */
static int sd_init ( struct gspca_dev * gspca_dev )
2008-06-30 15:50:11 -03:00
{
return 0 ;
}
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
{
int err_code ;
2008-07-14 09:38:29 -03:00
__u8 * data ;
__u16 * MI_buf ;
2008-06-30 15:50:11 -03:00
int h_size , v_size ;
int intpipe ;
PDEBUG ( D_STREAM , " camera start, iface %d, alt 8 " , gspca_dev - > iface ) ;
2008-09-20 06:39:08 -03:00
err_code = usb_set_interface ( gspca_dev - > dev , gspca_dev - > iface , 8 ) ;
if ( err_code < 0 ) {
2008-06-30 15:50:11 -03:00
PDEBUG ( D_ERR | D_STREAM , " Set packet size: set interface error " ) ;
2008-09-20 06:39:08 -03:00
return err_code ;
2008-06-30 15:50:11 -03:00
}
2008-07-14 09:38:29 -03:00
data = gspca_dev - > usb_buf ;
2008-06-30 15:50:11 -03:00
data [ 0 ] = 0x01 ; /* address */
data [ 1 ] = 0x01 ;
2008-07-14 09:38:29 -03:00
err_code = reg_w ( gspca_dev , data [ 0 ] , 2 ) ;
2008-06-30 15:50:11 -03:00
if ( err_code < 0 )
2008-09-20 06:39:08 -03:00
return err_code ;
2008-06-30 15:50:11 -03:00
/*
Initialize the MR97113 chip register
*/
data [ 0 ] = 0x00 ; /* address */
data [ 1 ] = 0x0c | 0x01 ; /* reg 0 */
data [ 2 ] = 0x01 ; /* reg 1 */
h_size = gspca_dev - > width ;
v_size = gspca_dev - > height ;
data [ 3 ] = h_size / 8 ; /* h_size , reg 2 */
data [ 4 ] = v_size / 8 ; /* v_size , reg 3 */
data [ 5 ] = 0x30 ; /* reg 4, MI, PAS5101 :
* 0x30 for 24 mhz , 0x28 for 12 mhz */
data [ 6 ] = 4 ; /* reg 5, H start */
data [ 7 ] = 0xc0 ; /* reg 6, gamma 1.5 */
data [ 8 ] = 3 ; /* reg 7, V start */
2008-07-14 09:38:29 -03:00
/* if (h_size == 320 ) */
2008-06-30 15:50:11 -03:00
/* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */
/* else */
data [ 9 ] = 0x52 ; /* reg 8, 24MHz, no scale down */
data [ 10 ] = 0x5d ; /* reg 9, I2C device address
* [ for PAS5101 ( 0x40 ) ] [ for MI ( 0x5d ) ] */
2008-09-03 17:12:19 -03:00
err_code = reg_w ( gspca_dev , data [ 0 ] , 11 ) ;
2008-06-30 15:50:11 -03:00
if ( err_code < 0 )
2008-09-20 06:39:08 -03:00
return err_code ;
2008-06-30 15:50:11 -03:00
data [ 0 ] = 0x23 ; /* address */
data [ 1 ] = 0x09 ; /* reg 35, append frame header */
2008-07-14 09:38:29 -03:00
err_code = reg_w ( gspca_dev , data [ 0 ] , 2 ) ;
if ( err_code < 0 )
2008-09-20 06:39:08 -03:00
return err_code ;
2008-06-30 15:50:11 -03:00
2008-07-14 09:38:29 -03:00
data [ 0 ] = 0x3c ; /* address */
/* if (gspca_dev->width == 1280) */
2008-06-30 15:50:11 -03:00
/* data[1] = 200; * reg 60, pc-cam frame size
* ( unit : 4 KB ) 800 KB */
/* else */
data [ 1 ] = 50 ; /* 50 reg 60, pc-cam frame size
* ( unit : 4 KB ) 200 KB */
2008-07-14 09:38:29 -03:00
err_code = reg_w ( gspca_dev , data [ 0 ] , 2 ) ;
2008-06-30 15:50:11 -03:00
if ( err_code < 0 )
2008-09-20 06:39:08 -03:00
return err_code ;
2008-06-30 15:50:11 -03:00
if ( 0 ) { /* fixed dark-gain */
data [ 1 ] = 0 ; /* reg 94, Y Gain (1.75) */
data [ 2 ] = 0 ; /* reg 95, UV Gain (1.75) */
2008-07-01 10:03:42 -03:00
data [ 3 ] = 0x3f ; /* reg 96, Y Gain/UV Gain/disable
* auto dark - gain */
2008-06-30 15:50:11 -03:00
data [ 4 ] = 0 ; /* reg 97, set fixed dark level */
data [ 5 ] = 0 ; /* reg 98, don't care */
} else { /* auto dark-gain */
data [ 1 ] = 0 ; /* reg 94, Y Gain (auto) */
data [ 2 ] = 0 ; /* reg 95, UV Gain (1.75) */
2008-07-01 10:03:42 -03:00
data [ 3 ] = 0x78 ; /* reg 96, Y Gain/UV Gain/disable
* auto dark - gain */
2008-06-30 15:50:11 -03:00
switch ( gspca_dev - > width ) {
/* case 1280: */
/* data[4] = 154;
* reg 97 , % 3 shadow point ( unit : 256 pixel ) */
/* data[5] = 51;
* reg 98 , % 1 highlight point
* ( uint : 256 pixel ) */
/* break; */
default :
/* case 640: */
data [ 4 ] = 36 ; /* reg 97, %3 shadow point
* ( unit : 256 pixel ) */
data [ 5 ] = 12 ; /* reg 98, %1 highlight point
* ( uint : 256 pixel ) */
break ;
case 320 :
data [ 4 ] = 9 ; /* reg 97, %3 shadow point
* ( unit : 256 pixel ) */
data [ 5 ] = 3 ; /* reg 98, %1 highlight point
* ( uint : 256 pixel ) */
break ;
}
}
/* auto dark-gain */
data [ 0 ] = 0x5e ; /* address */
2008-07-14 09:38:29 -03:00
err_code = reg_w ( gspca_dev , data [ 0 ] , 6 ) ;
2008-06-30 15:50:11 -03:00
if ( err_code < 0 )
2008-09-20 06:39:08 -03:00
return err_code ;
2008-06-30 15:50:11 -03:00
data [ 0 ] = 0x67 ;
data [ 1 ] = 0x13 ; /* reg 103, first pixel B, disable sharpness */
2008-07-14 09:38:29 -03:00
err_code = reg_w ( gspca_dev , data [ 0 ] , 2 ) ;
2008-06-30 15:50:11 -03:00
if ( err_code < 0 )
2008-09-20 06:39:08 -03:00
return err_code ;
2008-06-30 15:50:11 -03:00
/*
* initialize the value of MI sensor . . .
*/
2008-07-14 09:38:29 -03:00
MI_buf = kzalloc ( ATTR_TOTAL_MI_REG * sizeof * MI_buf , GFP_KERNEL ) ;
2008-06-30 15:50:11 -03:00
MI_buf [ REG_HW_MI_1 ] = 0x000a ;
MI_buf [ REG_HW_MI_2 ] = 0x000c ;
MI_buf [ REG_HW_MI_3 ] = 0x0405 ;
MI_buf [ REG_HW_MI_4 ] = 0x0507 ;
/* mi_Attr_Reg_[REG_HW_MI_5] = 0x01ff;//13 */
MI_buf [ REG_HW_MI_5 ] = 0x0013 ; /* 13 */
MI_buf [ REG_HW_MI_6 ] = 0x001f ; /* vertical blanking */
/* mi_Attr_Reg_[REG_HW_MI_6] = 0x0400; // vertical blanking */
MI_buf [ REG_HW_MI_7 ] = 0x0002 ;
/* mi_Attr_Reg_[REG_HW_MI_9] = 0x015f; */
/* mi_Attr_Reg_[REG_HW_MI_9] = 0x030f; */
MI_buf [ REG_HW_MI_9 ] = 0x0374 ;
MI_buf [ REG_HW_MI_B ] = 0x0000 ;
MI_buf [ REG_HW_MI_C ] = 0x0000 ;
MI_buf [ REG_HW_MI_D ] = 0x0000 ;
MI_buf [ REG_HW_MI_1E ] = 0x8000 ;
/* mi_Attr_Reg_[REG_HW_MI_20] = 0x1104; */
MI_buf [ REG_HW_MI_20 ] = 0x1104 ; /* 0x111c; */
MI_buf [ REG_HW_MI_2B ] = 0x0008 ;
/* mi_Attr_Reg_[REG_HW_MI_2C] = 0x000f; */
MI_buf [ REG_HW_MI_2C ] = 0x001f ; /* lita suggest */
MI_buf [ REG_HW_MI_2D ] = 0x0008 ;
MI_buf [ REG_HW_MI_2E ] = 0x0008 ;
MI_buf [ REG_HW_MI_35 ] = 0x0051 ;
MI_buf [ REG_HW_MI_5F ] = 0x0904 ; /* fail to write */
MI_buf [ REG_HW_MI_60 ] = 0x0000 ;
MI_buf [ REG_HW_MI_61 ] = 0x0000 ;
MI_buf [ REG_HW_MI_62 ] = 0x0498 ;
MI_buf [ REG_HW_MI_63 ] = 0x0000 ;
MI_buf [ REG_HW_MI_64 ] = 0x0000 ;
MI_buf [ REG_HW_MI_F1 ] = 0x0001 ;
/* changing while setting up the different value of dx/dy */
if ( gspca_dev - > width ! = 1280 ) {
MI_buf [ 0x01 ] = 0x010a ;
MI_buf [ 0x02 ] = 0x014c ;
MI_buf [ 0x03 ] = 0x01e5 ;
MI_buf [ 0x04 ] = 0x0287 ;
}
MI_buf [ 0x20 ] = 0x1104 ;
2008-07-14 09:38:29 -03:00
bulk_w ( gspca_dev , MI_buf + 1 , 1 ) ;
bulk_w ( gspca_dev , MI_buf + 2 , 2 ) ;
bulk_w ( gspca_dev , MI_buf + 3 , 3 ) ;
bulk_w ( gspca_dev , MI_buf + 4 , 4 ) ;
bulk_w ( gspca_dev , MI_buf + 5 , 5 ) ;
bulk_w ( gspca_dev , MI_buf + 6 , 6 ) ;
bulk_w ( gspca_dev , MI_buf + 7 , 7 ) ;
bulk_w ( gspca_dev , MI_buf + 9 , 9 ) ;
bulk_w ( gspca_dev , MI_buf + 0x0b , 0x0b ) ;
bulk_w ( gspca_dev , MI_buf + 0x0c , 0x0c ) ;
bulk_w ( gspca_dev , MI_buf + 0x0d , 0x0d ) ;
bulk_w ( gspca_dev , MI_buf + 0x1e , 0x1e ) ;
bulk_w ( gspca_dev , MI_buf + 0x20 , 0x20 ) ;
bulk_w ( gspca_dev , MI_buf + 0x2b , 0x2b ) ;
bulk_w ( gspca_dev , MI_buf + 0x2c , 0x2c ) ;
bulk_w ( gspca_dev , MI_buf + 0x2d , 0x2d ) ;
bulk_w ( gspca_dev , MI_buf + 0x2e , 0x2e ) ;
bulk_w ( gspca_dev , MI_buf + 0x35 , 0x35 ) ;
bulk_w ( gspca_dev , MI_buf + 0x5f , 0x5f ) ;
bulk_w ( gspca_dev , MI_buf + 0x60 , 0x60 ) ;
bulk_w ( gspca_dev , MI_buf + 0x61 , 0x61 ) ;
bulk_w ( gspca_dev , MI_buf + 0x62 , 0x62 ) ;
bulk_w ( gspca_dev , MI_buf + 0x63 , 0x63 ) ;
bulk_w ( gspca_dev , MI_buf + 0x64 , 0x64 ) ;
bulk_w ( gspca_dev , MI_buf + 0xf1 , 0xf1 ) ;
kfree ( MI_buf ) ;
intpipe = usb_sndintpipe ( gspca_dev - > dev , 0 ) ;
err_code = usb_clear_halt ( gspca_dev - > dev , intpipe ) ;
2008-06-30 15:50:11 -03:00
data [ 0 ] = 0x00 ;
data [ 1 ] = 0x4d ; /* ISOC transfering enable... */
2008-07-14 09:38:29 -03:00
reg_w ( gspca_dev , data [ 0 ] , 2 ) ;
2008-09-20 06:39:08 -03:00
return err_code ;
2008-06-30 15:50:11 -03:00
}
static void sd_stopN ( struct gspca_dev * gspca_dev )
{
int result ;
2008-07-14 09:38:29 -03:00
gspca_dev - > usb_buf [ 0 ] = 1 ;
gspca_dev - > usb_buf [ 1 ] = 0 ;
result = reg_w ( gspca_dev , gspca_dev - > usb_buf [ 0 ] , 2 ) ;
2008-06-30 15:50:11 -03:00
if ( result < 0 )
PDEBUG ( D_ERR , " Camera Stop failed " ) ;
}
static void sd_pkt_scan ( struct gspca_dev * gspca_dev ,
struct gspca_frame * frame , /* target */
2008-07-04 11:16:16 -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 ;
int p ;
if ( len < 6 ) {
/* gspca_dev->last_packet_type = DISCARD_PACKET; */
return ;
}
for ( p = 0 ; p < len - 6 ; p + + ) {
if ( data [ 0 + p ] = = 0xff
& & data [ 1 + p ] = = 0xff
& & data [ 2 + p ] = = 0x00
& & data [ 3 + p ] = = 0xff
& & data [ 4 + p ] = = 0x96 ) {
if ( data [ 5 + p ] = = 0x64
| | data [ 5 + p ] = = 0x65
| | data [ 5 + p ] = = 0x66
| | data [ 5 + p ] = = 0x67 ) {
PDEBUG ( D_PACK , " sof offset: %d leng: %d " ,
p , len ) ;
frame = gspca_frame_add ( gspca_dev , LAST_PACKET ,
frame , data , 0 ) ;
/* put the JPEG header */
jpeg_put_header ( gspca_dev , frame ,
sd - > qindex , 0x21 ) ;
data + = 16 ;
len - = 16 ;
break ;
}
}
}
gspca_frame_add ( gspca_dev , INTER_PACKET , frame , data , len ) ;
}
/* sub-driver description */
2008-07-04 11:16:16 -03:00
static const struct sd_desc sd_desc = {
2008-06-30 15:50:11 -03:00
. name = MODULE_NAME ,
. ctrls = sd_ctrls ,
. nctrls = ARRAY_SIZE ( sd_ctrls ) ,
. config = sd_config ,
2008-09-03 17:12:16 -03:00
. init = sd_init ,
2008-06-30 15:50:11 -03:00
. start = sd_start ,
. stopN = sd_stopN ,
. pkt_scan = sd_pkt_scan ,
} ;
/* -- module initialisation -- */
2008-07-04 11:16:16 -03:00
static const __devinitdata struct usb_device_id device_table [ ] = {
2008-07-25 08:53:03 -03:00
{ USB_DEVICE ( 0x093a , 0x050f ) } ,
2008-06-30 15:50:11 -03:00
{ }
} ;
MODULE_DEVICE_TABLE ( usb , device_table ) ;
/* -- device connect -- */
static int sd_probe ( struct usb_interface * intf ,
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 ,
# endif
2008-06-30 15:50:11 -03:00
} ;
/* -- module insert / remove -- */
static int __init sd_mod_init ( void )
{
if ( usb_register ( & sd_driver ) < 0 )
return - 1 ;
2008-07-22 05:35:10 -03:00
PDEBUG ( D_PROBE , " registered " ) ;
2008-06-30 15:50:11 -03:00
return 0 ;
}
static void __exit sd_mod_exit ( void )
{
usb_deregister ( & sd_driver ) ;
PDEBUG ( D_PROBE , " deregistered " ) ;
}
module_init ( sd_mod_init ) ;
module_exit ( sd_mod_exit ) ;