2005-11-08 21:37:07 -08:00
/*
2005-11-08 21:38:27 -08:00
em28xx - core . c - driver for Empia EM2800 / EM2820 / 2840 USB video capture devices
2005-11-08 21:37:07 -08:00
2005-11-08 21:38:25 -08:00
Copyright ( C ) 2005 Ludovico Cavedon < cavedon @ sssup . it >
Markus Rechberger < mrechberger @ gmail . com >
2005-11-08 21:37:43 -08:00
Mauro Carvalho Chehab < mchehab @ brturbo . com . br >
2005-11-08 21:38:25 -08:00
Sascha Sommer < saschasommer @ freenet . de >
2005-11-08 21:37:07 -08: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/init.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/usb.h>
# include <linux/vmalloc.h>
2005-11-08 21:38:25 -08:00
# include "em28xx.h"
2005-11-08 21:37:07 -08:00
/* #define ENABLE_DEBUG_ISOC_FRAMES */
2005-12-01 00:51:35 -08:00
static unsigned int core_debug ;
2005-11-08 21:37:07 -08:00
module_param ( core_debug , int , 0644 ) ;
MODULE_PARM_DESC ( core_debug , " enable debug messages [core] " ) ;
2005-11-08 21:38:27 -08:00
# define em28xx_coredbg(fmt, arg...) do {\
2005-11-08 21:37:43 -08:00
if ( core_debug ) \
printk ( KERN_INFO " %s %s : " fmt , \
2005-12-19 08:53:59 -02:00
dev - > name , __FUNCTION__ , # # arg ) ; } while ( 0 )
2005-11-08 21:37:07 -08:00
2005-12-01 00:51:35 -08:00
static unsigned int reg_debug ;
2005-11-08 21:37:07 -08:00
module_param ( reg_debug , int , 0644 ) ;
MODULE_PARM_DESC ( reg_debug , " enable debug messages [URB reg] " ) ;
2005-11-08 21:38:27 -08:00
# define em28xx_regdbg(fmt, arg...) do {\
2005-11-08 21:37:43 -08:00
if ( reg_debug ) \
printk ( KERN_INFO " %s %s : " fmt , \
2005-12-19 08:53:59 -02:00
dev - > name , __FUNCTION__ , # # arg ) ; } while ( 0 )
2005-11-08 21:37:07 -08:00
2005-12-01 00:51:35 -08:00
static unsigned int isoc_debug ;
2005-11-08 21:37:07 -08:00
module_param ( isoc_debug , int , 0644 ) ;
2005-11-08 21:37:29 -08:00
MODULE_PARM_DESC ( isoc_debug , " enable debug messages [isoc transfers] " ) ;
2005-11-08 21:37:07 -08:00
2005-11-08 21:38:27 -08:00
# define em28xx_isocdbg(fmt, arg...) do {\
2005-11-08 21:37:43 -08:00
if ( isoc_debug ) \
printk ( KERN_INFO " %s %s : " fmt , \
2005-12-19 08:53:59 -02:00
dev - > name , __FUNCTION__ , # # arg ) ; } while ( 0 )
2005-11-08 21:37:07 -08:00
2005-11-08 21:38:27 -08:00
static int alt = EM28XX_PINOUT ;
2005-11-08 21:37:07 -08:00
module_param ( alt , int , 0644 ) ;
MODULE_PARM_DESC ( alt , " alternate setting to use for video endpoint " ) ;
/* ------------------------------------------------------------------ */
/* debug help functions */
static const char * v4l1_ioctls [ ] = {
" 0 " , " CGAP " , " GCHAN " , " SCHAN " , " GTUNER " , " STUNER " , " GPICT " , " SPICT " ,
" CCAPTURE " , " GWIN " , " SWIN " , " GFBUF " , " SFBUF " , " KEY " , " GFREQ " ,
" SFREQ " , " GAUDIO " , " SAUDIO " , " SYNC " , " MCAPTURE " , " GMBUF " , " GUNIT " ,
" GCAPTURE " , " SCAPTURE " , " SPLAYMODE " , " SWRITEMODE " , " GPLAYINFO " ,
" SMICROCODE " , " GVBIFMT " , " SVBIFMT " } ;
# define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls)
static const char * v4l2_ioctls [ ] = {
" QUERYCAP " , " 1 " , " ENUM_PIXFMT " , " ENUM_FBUFFMT " , " G_FMT " , " S_FMT " ,
" G_COMP " , " S_COMP " , " REQBUFS " , " QUERYBUF " , " G_FBUF " , " S_FBUF " ,
" G_WIN " , " S_WIN " , " PREVIEW " , " QBUF " , " 16 " , " DQBUF " , " STREAMON " ,
" STREAMOFF " , " G_PERF " , " G_PARM " , " S_PARM " , " G_STD " , " S_STD " ,
" ENUMSTD " , " ENUMINPUT " , " G_CTRL " , " S_CTRL " , " G_TUNER " , " S_TUNER " ,
" G_FREQ " , " S_FREQ " , " G_AUDIO " , " S_AUDIO " , " 35 " , " QUERYCTRL " ,
" QUERYMENU " , " G_INPUT " , " S_INPUT " , " ENUMCVT " , " 41 " , " 42 " , " 43 " ,
" 44 " , " 45 " , " G_OUTPUT " , " S_OUTPUT " , " ENUMOUTPUT " , " G_AUDOUT " ,
" S_AUDOUT " , " ENUMFX " , " G_EFFECT " , " S_EFFECT " , " G_MODULATOR " ,
" S_MODULATOR "
} ;
# define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
2005-11-08 21:38:27 -08:00
void em28xx_print_ioctl ( char * name , unsigned int cmd )
2005-11-08 21:37:07 -08:00
{
char * dir ;
switch ( _IOC_DIR ( cmd ) ) {
case _IOC_NONE : dir = " -- " ; break ;
case _IOC_READ : dir = " r- " ; break ;
case _IOC_WRITE : dir = " -w " ; break ;
case _IOC_READ | _IOC_WRITE : dir = " rw " ; break ;
default : dir = " ?? " ; break ;
}
switch ( _IOC_TYPE ( cmd ) ) {
case ' v ' :
printk ( KERN_DEBUG " %s: ioctl 0x%08x (v4l1, %s, VIDIOC%s) \n " ,
name , cmd , dir , ( _IOC_NR ( cmd ) < V4L1_IOCTLS ) ?
v4l1_ioctls [ _IOC_NR ( cmd ) ] : " ??? " ) ;
break ;
case ' V ' :
printk ( KERN_DEBUG " %s: ioctl 0x%08x (v4l2, %s, VIDIOC_%s) \n " ,
name , cmd , dir , ( _IOC_NR ( cmd ) < V4L2_IOCTLS ) ?
v4l2_ioctls [ _IOC_NR ( cmd ) ] : " ??? " ) ;
break ;
default :
printk ( KERN_DEBUG " %s: ioctl 0x%08x (???, %s, #%d) \n " ,
name , cmd , dir , _IOC_NR ( cmd ) ) ;
}
}
2005-12-12 00:37:28 -08:00
2005-11-08 21:37:07 -08:00
/*
2005-11-08 21:38:27 -08:00
* em28xx_request_buffers ( )
2005-11-08 21:37:07 -08:00
* allocate a number of buffers
*/
2005-11-08 21:38:27 -08:00
u32 em28xx_request_buffers ( struct em28xx * dev , u32 count )
2005-11-08 21:37:07 -08:00
{
const size_t imagesize = PAGE_ALIGN ( dev - > frame_size ) ; /*needs to be page aligned cause the buffers can be mapped individually! */
void * buff = NULL ;
u32 i ;
2005-12-15 09:17:39 +00:00
em28xx_coredbg ( " requested %i buffers with size %zd " , count , imagesize ) ;
2005-11-08 21:38:27 -08:00
if ( count > EM28XX_NUM_FRAMES )
count = EM28XX_NUM_FRAMES ;
2005-11-08 21:37:07 -08:00
dev - > num_frames = count ;
while ( dev - > num_frames > 0 ) {
2005-12-12 00:37:30 -08:00
if ( ( buff = vmalloc_32 ( dev - > num_frames * imagesize ) ) ) {
memset ( buff , 0 , dev - > num_frames * imagesize ) ;
2005-11-08 21:37:07 -08:00
break ;
2005-12-12 00:37:30 -08:00
}
2005-11-08 21:37:07 -08:00
dev - > num_frames - - ;
}
for ( i = 0 ; i < dev - > num_frames ; i + + ) {
dev - > frame [ i ] . bufmem = buff + i * imagesize ;
dev - > frame [ i ] . buf . index = i ;
dev - > frame [ i ] . buf . m . offset = i * imagesize ;
dev - > frame [ i ] . buf . length = dev - > frame_size ;
dev - > frame [ i ] . buf . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
dev - > frame [ i ] . buf . sequence = 0 ;
dev - > frame [ i ] . buf . field = V4L2_FIELD_NONE ;
dev - > frame [ i ] . buf . memory = V4L2_MEMORY_MMAP ;
dev - > frame [ i ] . buf . flags = 0 ;
}
return dev - > num_frames ;
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_queue_unusedframes ( )
2005-11-08 21:37:07 -08:00
* add all frames that are not currently in use to the inbuffer queue
*/
2005-11-08 21:38:27 -08:00
void em28xx_queue_unusedframes ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
unsigned long lock_flags ;
u32 i ;
for ( i = 0 ; i < dev - > num_frames ; i + + )
if ( dev - > frame [ i ] . state = = F_UNUSED ) {
dev - > frame [ i ] . state = F_QUEUED ;
spin_lock_irqsave ( & dev - > queue_lock , lock_flags ) ;
list_add_tail ( & dev - > frame [ i ] . frame , & dev - > inqueue ) ;
spin_unlock_irqrestore ( & dev - > queue_lock , lock_flags ) ;
}
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_release_buffers ( )
2005-11-08 21:37:07 -08:00
* free frame buffers
*/
2005-11-08 21:38:27 -08:00
void em28xx_release_buffers ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
if ( dev - > num_frames ) {
2005-12-12 00:37:30 -08:00
vfree ( dev - > frame [ 0 ] . bufmem ) ;
2005-11-08 21:37:07 -08:00
dev - > num_frames = 0 ;
}
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_read_reg_req ( )
2005-11-08 21:37:07 -08:00
* reads data from the usb device specifying bRequest
*/
2005-11-08 21:38:27 -08:00
int em28xx_read_reg_req_len ( struct em28xx * dev , u8 req , u16 reg ,
2005-11-08 21:37:07 -08:00
char * buf , int len )
{
int ret , byte ;
2005-11-08 21:38:27 -08:00
em28xx_regdbg ( " req=%02x, reg=%02x " , req , reg ) ;
2005-11-08 21:37:07 -08:00
ret = usb_control_msg ( dev - > udev , usb_rcvctrlpipe ( dev - > udev , 0 ) , req ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0x0000 , reg , buf , len , HZ ) ;
if ( reg_debug ) {
printk ( ret < 0 ? " failed! \n " : " %02x values: " , ret ) ;
for ( byte = 0 ; byte < len ; byte + + ) {
printk ( " %02x " , buf [ byte ] ) ;
}
printk ( " \n " ) ;
}
return ret ;
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_read_reg_req ( )
2005-11-08 21:37:07 -08:00
* reads data from the usb device specifying bRequest
*/
2005-11-08 21:38:27 -08:00
int em28xx_read_reg_req ( struct em28xx * dev , u8 req , u16 reg )
2005-11-08 21:37:07 -08:00
{
u8 val ;
int ret ;
2005-11-08 21:38:27 -08:00
em28xx_regdbg ( " req=%02x, reg=%02x: " , req , reg ) ;
2005-11-08 21:37:07 -08:00
ret = usb_control_msg ( dev - > udev , usb_rcvctrlpipe ( dev - > udev , 0 ) , req ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0x0000 , reg , & val , 1 , HZ ) ;
if ( reg_debug )
printk ( ret < 0 ? " failed! \n " : " %02x \n " , val ) ;
if ( ret < 0 )
return ret ;
return val ;
}
2005-11-08 21:38:27 -08:00
int em28xx_read_reg ( struct em28xx * dev , u16 reg )
2005-11-08 21:37:07 -08:00
{
2005-11-08 21:38:27 -08:00
return em28xx_read_reg_req ( dev , USB_REQ_GET_STATUS , reg ) ;
2005-11-08 21:37:07 -08:00
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_write_regs_req ( )
2005-11-08 21:37:07 -08:00
* sends data to the usb device , specifying bRequest
*/
2005-11-08 21:38:27 -08:00
int em28xx_write_regs_req ( struct em28xx * dev , u8 req , u16 reg , char * buf ,
2005-11-08 21:37:07 -08:00
int len )
{
int ret ;
/*usb_control_msg seems to expect a kmalloced buffer */
unsigned char * bufs = kmalloc ( len , GFP_KERNEL ) ;
2005-11-08 21:38:27 -08:00
em28xx_regdbg ( " req=%02x reg=%02x: " , req , reg ) ;
2005-11-08 21:37:07 -08:00
if ( reg_debug ) {
int i ;
for ( i = 0 ; i < len ; + + i )
printk ( " %02x " , ( unsigned char ) buf [ i ] ) ;
printk ( " \n " ) ;
}
if ( ! bufs )
return - ENOMEM ;
memcpy ( bufs , buf , len ) ;
ret = usb_control_msg ( dev - > udev , usb_sndctrlpipe ( dev - > udev , 0 ) , req ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0x0000 , reg , bufs , len , HZ ) ;
mdelay ( 5 ) ; /* FIXME: magic number */
kfree ( bufs ) ;
return ret ;
}
2005-11-08 21:38:27 -08:00
int em28xx_write_regs ( struct em28xx * dev , u16 reg , char * buf , int len )
2005-11-08 21:37:07 -08:00
{
2005-11-08 21:38:27 -08:00
return em28xx_write_regs_req ( dev , USB_REQ_GET_STATUS , reg , buf , len ) ;
2005-11-08 21:37:07 -08:00
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_write_reg_bits ( )
2005-11-08 21:37:07 -08:00
* sets only some bits ( specified by bitmask ) of a register , by first reading
* the actual value
*/
2005-11-08 21:38:27 -08:00
int em28xx_write_reg_bits ( struct em28xx * dev , u16 reg , u8 val ,
2005-11-08 21:37:07 -08:00
u8 bitmask )
{
int oldval ;
u8 newval ;
2005-11-08 21:38:27 -08:00
if ( ( oldval = em28xx_read_reg ( dev , reg ) ) < 0 )
2005-11-08 21:37:07 -08:00
return oldval ;
newval = ( ( ( u8 ) oldval ) & ~ bitmask ) | ( val & bitmask ) ;
2005-11-08 21:38:27 -08:00
return em28xx_write_regs ( dev , reg , & newval , 1 ) ;
2005-11-08 21:37:07 -08:00
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_write_ac97 ( )
2005-11-08 21:37:07 -08:00
* write a 16 bit value to the specified AC97 address ( LSB first ! )
*/
2005-11-08 21:38:27 -08:00
int em28xx_write_ac97 ( struct em28xx * dev , u8 reg , u8 * val )
2005-11-08 21:37:07 -08:00
{
int ret ;
u8 addr = reg & 0x7f ;
2005-11-08 21:38:27 -08:00
if ( ( ret = em28xx_write_regs ( dev , AC97LSB_REG , val , 2 ) ) < 0 )
2005-11-08 21:37:07 -08:00
return ret ;
2005-11-08 21:38:27 -08:00
if ( ( ret = em28xx_write_regs ( dev , AC97ADDR_REG , & addr , 1 ) ) < 0 )
2005-11-08 21:37:07 -08:00
return ret ;
2005-11-08 21:38:27 -08:00
if ( ( ret = em28xx_read_reg ( dev , AC97BUSY_REG ) ) < 0 )
2005-11-08 21:37:07 -08:00
return ret ;
else if ( ( ( u8 ) ret ) & 0x01 ) {
2005-11-08 21:38:27 -08:00
em28xx_warn ( " AC97 command still being exectuted: not handled properly! \n " ) ;
2005-11-08 21:37:07 -08:00
}
return 0 ;
}
2005-11-08 21:38:27 -08:00
int em28xx_audio_analog_set ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
char s [ 2 ] = { 0x00 , 0x00 } ;
s [ 0 ] | = 0x1f - dev - > volume ;
s [ 1 ] | = 0x1f - dev - > volume ;
if ( dev - > mute )
s [ 1 ] | = 0x80 ;
2005-11-08 21:38:27 -08:00
return em28xx_write_ac97 ( dev , MASTER_AC97 , s ) ;
2005-11-08 21:37:07 -08:00
}
2005-11-08 21:38:27 -08:00
int em28xx_colorlevels_set_default ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
2005-11-08 21:38:27 -08:00
em28xx_write_regs ( dev , YGAIN_REG , " \x10 " , 1 ) ; /* contrast */
em28xx_write_regs ( dev , YOFFSET_REG , " \x00 " , 1 ) ; /* brightness */
em28xx_write_regs ( dev , UVGAIN_REG , " \x10 " , 1 ) ; /* saturation */
em28xx_write_regs ( dev , UOFFSET_REG , " \x00 " , 1 ) ;
em28xx_write_regs ( dev , VOFFSET_REG , " \x00 " , 1 ) ;
em28xx_write_regs ( dev , SHARPNESS_REG , " \x00 " , 1 ) ;
em28xx_write_regs ( dev , GAMMA_REG , " \x20 " , 1 ) ;
em28xx_write_regs ( dev , RGAIN_REG , " \x20 " , 1 ) ;
em28xx_write_regs ( dev , GGAIN_REG , " \x20 " , 1 ) ;
em28xx_write_regs ( dev , BGAIN_REG , " \x20 " , 1 ) ;
em28xx_write_regs ( dev , ROFFSET_REG , " \x00 " , 1 ) ;
em28xx_write_regs ( dev , GOFFSET_REG , " \x00 " , 1 ) ;
return em28xx_write_regs ( dev , BOFFSET_REG , " \x00 " , 1 ) ;
2005-11-08 21:37:07 -08:00
}
2005-11-08 21:38:27 -08:00
int em28xx_capture_start ( struct em28xx * dev , int start )
2005-11-08 21:37:07 -08:00
{
int ret ;
/* FIXME: which is the best order? */
/* video registers are sampled by VREF */
2005-11-08 21:38:27 -08:00
if ( ( ret = em28xx_write_reg_bits ( dev , USBSUSP_REG , start ? 0x10 : 0x00 ,
2005-11-08 21:37:07 -08:00
0x10 ) ) < 0 )
return ret ;
/* enable video capture */
2005-11-08 21:38:27 -08:00
return em28xx_write_regs ( dev , VINENABLE_REG , start ? " \x67 " : " \x27 " , 1 ) ;
2005-11-08 21:37:07 -08:00
}
2005-11-08 21:38:27 -08:00
int em28xx_outfmt_set_yuv422 ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
2005-11-08 21:38:27 -08:00
em28xx_write_regs ( dev , OUTFMT_REG , " \x34 " , 1 ) ;
em28xx_write_regs ( dev , VINMODE_REG , " \x10 " , 1 ) ;
return em28xx_write_regs ( dev , VINCTRL_REG , " \x11 " , 1 ) ;
2005-11-08 21:37:07 -08:00
}
2005-11-08 21:38:27 -08:00
int em28xx_accumulator_set ( struct em28xx * dev , u8 xmin , u8 xmax , u8 ymin ,
2005-11-08 21:37:07 -08:00
u8 ymax )
{
2005-11-08 21:38:27 -08:00
em28xx_coredbg ( " em28xx Scale: (%d,%d)-(%d,%d) \n " , xmin , ymin , xmax , ymax ) ;
2005-11-08 21:37:07 -08:00
2005-11-08 21:38:27 -08:00
em28xx_write_regs ( dev , XMIN_REG , & xmin , 1 ) ;
em28xx_write_regs ( dev , XMAX_REG , & xmax , 1 ) ;
em28xx_write_regs ( dev , YMIN_REG , & ymin , 1 ) ;
return em28xx_write_regs ( dev , YMAX_REG , & ymax , 1 ) ;
2005-11-08 21:37:07 -08:00
}
2005-11-08 21:38:27 -08:00
int em28xx_capture_area_set ( struct em28xx * dev , u8 hstart , u8 vstart ,
2005-11-08 21:37:07 -08:00
u16 width , u16 height )
{
u8 cwidth = width ;
u8 cheight = height ;
u8 overflow = ( height > > 7 & 0x02 ) | ( width > > 8 & 0x01 ) ;
2005-11-08 21:38:27 -08:00
em28xx_coredbg ( " em28xx Area Set: (%d,%d) \n " , ( width | ( overflow & 2 ) < < 7 ) ,
2005-11-08 21:37:07 -08:00
( height | ( overflow & 1 ) < < 8 ) ) ;
2005-11-08 21:38:27 -08:00
em28xx_write_regs ( dev , HSTART_REG , & hstart , 1 ) ;
em28xx_write_regs ( dev , VSTART_REG , & vstart , 1 ) ;
em28xx_write_regs ( dev , CWIDTH_REG , & cwidth , 1 ) ;
em28xx_write_regs ( dev , CHEIGHT_REG , & cheight , 1 ) ;
return em28xx_write_regs ( dev , OFLOW_REG , & overflow , 1 ) ;
2005-11-08 21:37:07 -08:00
}
2005-11-08 21:38:27 -08:00
int em28xx_scaler_set ( struct em28xx * dev , u16 h , u16 v )
2005-11-08 21:37:07 -08:00
{
2005-11-08 21:38:10 -08:00
u8 mode ;
/* the em2800 scaler only supports scaling down to 50% */
if ( dev - > is_em2800 )
mode = ( v ? 0x20 : 0x00 ) | ( h ? 0x10 : 0x00 ) ;
else {
u8 buf [ 2 ] ;
buf [ 0 ] = h ;
buf [ 1 ] = h > > 8 ;
2005-11-08 21:38:27 -08:00
em28xx_write_regs ( dev , HSCALELOW_REG , ( char * ) buf , 2 ) ;
2005-11-08 21:38:10 -08:00
buf [ 0 ] = v ;
buf [ 1 ] = v > > 8 ;
2005-11-08 21:38:27 -08:00
em28xx_write_regs ( dev , VSCALELOW_REG , ( char * ) buf , 2 ) ;
2005-11-08 21:38:10 -08:00
/* it seems that both H and V scalers must be active to work correctly */
mode = ( h | | v ) ? 0x30 : 0x00 ;
2005-11-08 21:37:33 -08:00
}
2005-11-08 21:38:27 -08:00
return em28xx_write_reg_bits ( dev , COMPR_REG , mode , 0x30 ) ;
2005-11-08 21:37:07 -08:00
}
/* FIXME: this only function read values from dev */
2005-11-08 21:38:27 -08:00
int em28xx_resolution_set ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
int width , height ;
width = norm_maxw ( dev ) ;
height = norm_maxh ( dev ) > > 1 ;
2005-11-08 21:38:27 -08:00
em28xx_outfmt_set_yuv422 ( dev ) ;
em28xx_accumulator_set ( dev , 1 , ( width - 4 ) > > 2 , 1 , ( height - 4 ) > > 2 ) ;
em28xx_capture_area_set ( dev , 0 , 0 , width > > 2 , height > > 2 ) ;
return em28xx_scaler_set ( dev , dev - > hscale , dev - > vscale ) ;
2005-11-08 21:37:07 -08:00
}
/******************* isoc transfer handling ****************************/
# ifdef ENABLE_DEBUG_ISOC_FRAMES
2005-11-08 21:38:27 -08:00
static void em28xx_isoc_dump ( struct urb * urb , struct pt_regs * regs )
2005-11-08 21:37:07 -08:00
{
int len = 0 ;
int ntrans = 0 ;
int i ;
printk ( KERN_DEBUG " isocIrq: sf=%d np=%d ec=%x \n " ,
urb - > start_frame , urb - > number_of_packets ,
urb - > error_count ) ;
for ( i = 0 ; i < urb - > number_of_packets ; i + + ) {
unsigned char * buf =
urb - > transfer_buffer +
urb - > iso_frame_desc [ i ] . offset ;
int alen = urb - > iso_frame_desc [ i ] . actual_length ;
if ( alen > 0 ) {
if ( buf [ 0 ] = = 0x88 ) {
ntrans + + ;
len + = alen ;
} else if ( buf [ 0 ] = = 0x22 ) {
printk ( KERN_DEBUG
" = l=%d nt=%d bpp=%d \n " ,
len - 4 * ntrans , ntrans ,
ntrans = = 0 ? 0 : len / ntrans ) ;
ntrans = 1 ;
len = alen ;
} else
printk ( KERN_DEBUG " ! \n " ) ;
}
printk ( KERN_DEBUG " n=%d s=%d al=%d %x \n " , i ,
urb - > iso_frame_desc [ i ] . status ,
urb - > iso_frame_desc [ i ] . actual_length ,
( unsigned int )
* ( ( unsigned char * ) ( urb - > transfer_buffer +
urb - > iso_frame_desc [ i ] .
offset ) ) ) ;
}
}
# endif
2005-11-08 21:38:27 -08:00
static inline int em28xx_isoc_video ( struct em28xx * dev , struct em28xx_frame_t * * f ,
2005-11-08 21:37:07 -08:00
unsigned long * lock_flags , unsigned char buf )
{
if ( ! ( buf & 0x01 ) ) {
if ( ( * f ) - > state = = F_GRABBING ) {
/*previous frame is incomplete */
if ( ( * f ) - > fieldbytesused < dev - > field_size ) {
( * f ) - > state = F_ERROR ;
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " dropping incomplete bottom field (%i missing bytes) " ,
2005-11-08 21:37:07 -08:00
dev - > field_size - ( * f ) - > fieldbytesused ) ;
} else {
( * f ) - > state = F_DONE ;
( * f ) - > buf . bytesused = dev - > frame_size ;
}
}
if ( ( * f ) - > state = = F_DONE | | ( * f ) - > state = = F_ERROR ) {
/* move current frame to outqueue and get next free buffer from inqueue */
spin_lock_irqsave ( & dev - > queue_lock , * lock_flags ) ;
list_move_tail ( & ( * f ) - > frame , & dev - > outqueue ) ;
if ( ! list_empty ( & dev - > inqueue ) )
( * f ) = list_entry ( dev - > inqueue . next ,
2005-11-08 21:38:27 -08:00
struct em28xx_frame_t , frame ) ;
2005-11-08 21:37:07 -08:00
else
( * f ) = NULL ;
spin_unlock_irqrestore ( & dev - > queue_lock , * lock_flags ) ;
}
if ( ! ( * f ) ) {
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " new frame but no buffer is free " ) ;
2005-11-08 21:37:07 -08:00
return - 1 ;
}
do_gettimeofday ( & ( * f ) - > buf . timestamp ) ;
( * f ) - > buf . sequence = + + dev - > frame_count ;
( * f ) - > buf . field = V4L2_FIELD_INTERLACED ;
( * f ) - > state = F_GRABBING ;
( * f ) - > buf . bytesused = 0 ;
( * f ) - > top_field = 1 ;
( * f ) - > fieldbytesused = 0 ;
} else {
/* acquiring bottom field */
if ( ( * f ) - > state = = F_GRABBING ) {
if ( ! ( * f ) - > top_field ) {
( * f ) - > state = F_ERROR ;
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " unexpected begin of bottom field; discarding it " ) ;
2005-11-08 21:37:07 -08:00
} else if ( ( * f ) - > fieldbytesused < dev - > field_size - 172 ) {
( * f ) - > state = F_ERROR ;
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " dropping incomplete top field (%i missing bytes) " ,
2005-11-08 21:37:07 -08:00
dev - > field_size - ( * f ) - > fieldbytesused ) ;
} else {
( * f ) - > top_field = 0 ;
( * f ) - > fieldbytesused = 0 ;
}
}
}
return ( 0 ) ;
}
2005-11-08 21:38:27 -08:00
static inline void em28xx_isoc_video_copy ( struct em28xx * dev ,
struct em28xx_frame_t * * f , unsigned char * buf , int len )
2005-11-08 21:37:07 -08:00
{
void * fieldstart , * startwrite , * startread ;
int linesdone , currlinedone , offset , lencopy , remain ;
2005-11-08 21:37:24 -08:00
if ( dev - > frame_size ! = ( * f ) - > buf . length ) {
2005-11-08 21:38:27 -08:00
em28xx_err ( " frame_size %i and buf.length %i are different!!! \n " , dev - > frame_size , ( * f ) - > buf . length ) ;
2005-11-08 21:37:24 -08:00
return ;
}
2005-11-08 21:37:07 -08:00
if ( ( * f ) - > fieldbytesused + len > dev - > field_size )
len = dev - > field_size - ( * f ) - > fieldbytesused ;
2005-11-08 21:38:16 -08:00
if ( buf [ 0 ] ! = 0x88 & & buf [ 0 ] ! = 0x22 ) {
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " frame is not complete \n " ) ;
2005-11-08 21:38:16 -08:00
startread = buf ;
len + = 4 ;
} else
startread = buf + 4 ;
2005-11-08 21:37:07 -08:00
remain = len ;
2005-11-08 21:38:16 -08:00
2005-11-08 21:37:07 -08:00
if ( ( * f ) - > top_field )
fieldstart = ( * f ) - > bufmem ;
else
fieldstart = ( * f ) - > bufmem + dev - > bytesperline ;
linesdone = ( * f ) - > fieldbytesused / dev - > bytesperline ;
currlinedone = ( * f ) - > fieldbytesused % dev - > bytesperline ;
offset = linesdone * dev - > bytesperline * 2 + currlinedone ;
startwrite = fieldstart + offset ;
lencopy = dev - > bytesperline - currlinedone ;
lencopy = lencopy > remain ? remain : lencopy ;
memcpy ( startwrite , startread , lencopy ) ;
remain - = lencopy ;
while ( remain > 0 ) {
startwrite + = lencopy + dev - > bytesperline ;
startread + = lencopy ;
if ( dev - > bytesperline > remain )
lencopy = remain ;
else
lencopy = dev - > bytesperline ;
memcpy ( startwrite , startread , lencopy ) ;
remain - = lencopy ;
}
( * f ) - > fieldbytesused + = len ;
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_isoIrq ( )
2005-11-08 21:37:07 -08:00
* handles the incoming isoc urbs and fills the frames from our inqueue
*/
2005-11-08 21:38:27 -08:00
void em28xx_isocIrq ( struct urb * urb , struct pt_regs * regs )
2005-11-08 21:37:07 -08:00
{
2005-11-08 21:38:27 -08:00
struct em28xx * dev = urb - > context ;
2005-11-08 21:37:07 -08:00
int i , status ;
2005-11-08 21:38:27 -08:00
struct em28xx_frame_t * * f ;
2005-11-08 21:37:07 -08:00
unsigned long lock_flags ;
if ( ! dev )
return ;
# ifdef ENABLE_DEBUG_ISOC_FRAMES
if ( isoc_debug > 1 )
2005-11-08 21:38:27 -08:00
em28xx_isoc_dump ( urb , regs ) ;
2005-11-08 21:37:07 -08:00
# endif
if ( urb - > status = = - ENOENT )
return ;
f = & dev - > frame_current ;
if ( dev - > stream = = STREAM_INTERRUPT ) {
dev - > stream = STREAM_OFF ;
if ( ( * f ) )
( * f ) - > state = F_QUEUED ;
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " stream interrupted " ) ;
2005-11-08 21:37:07 -08:00
wake_up_interruptible ( & dev - > wait_stream ) ;
}
if ( ( dev - > state & DEV_DISCONNECTED ) | | ( dev - > state & DEV_MISCONFIGURED ) )
return ;
if ( dev - > stream = = STREAM_ON & & ! list_empty ( & dev - > inqueue ) ) {
if ( ! ( * f ) )
( * f ) = list_entry ( dev - > inqueue . next ,
2005-11-08 21:38:27 -08:00
struct em28xx_frame_t , frame ) ;
2005-11-08 21:37:07 -08:00
for ( i = 0 ; i < urb - > number_of_packets ; i + + ) {
unsigned char * buf = urb - > transfer_buffer +
urb - > iso_frame_desc [ i ] . offset ;
int len = urb - > iso_frame_desc [ i ] . actual_length - 4 ;
if ( urb - > iso_frame_desc [ i ] . status ) {
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " data error: [%d] len=%d, status=%d " , i ,
2005-11-08 21:37:07 -08:00
urb - > iso_frame_desc [ i ] . actual_length ,
urb - > iso_frame_desc [ i ] . status ) ;
2005-11-08 21:38:16 -08:00
if ( urb - > iso_frame_desc [ i ] . status ! = - EPROTO )
continue ;
2005-11-08 21:37:07 -08:00
}
if ( urb - > iso_frame_desc [ i ] . actual_length < = 0 ) {
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " packet %d is empty " , i ) ;
2005-11-08 21:37:07 -08:00
continue ;
}
if ( urb - > iso_frame_desc [ i ] . actual_length >
dev - > max_pkt_size ) {
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " packet bigger than packet size " ) ;
2005-11-08 21:37:07 -08:00
continue ;
}
/*new frame */
if ( buf [ 0 ] = = 0x22 & & buf [ 1 ] = = 0x5a ) {
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " Video frame, length=%i! " , len ) ;
2005-11-08 21:37:07 -08:00
2005-11-08 21:38:27 -08:00
if ( em28xx_isoc_video ( dev , f , & lock_flags , buf [ 2 ] ) )
2005-11-08 21:37:07 -08:00
break ;
} else if ( buf [ 0 ] = = 0x33 & & buf [ 1 ] = = 0x95 & & buf [ 2 ] = = 0x00 ) {
2005-11-08 21:38:27 -08:00
em28xx_isocdbg ( " VBI HEADER!!! " ) ;
2005-11-08 21:37:07 -08:00
}
/* actual copying */
if ( ( * f ) - > state = = F_GRABBING ) {
2005-11-08 21:38:27 -08:00
em28xx_isoc_video_copy ( dev , f , buf , len ) ;
2005-11-08 21:37:07 -08:00
}
}
}
for ( i = 0 ; i < urb - > number_of_packets ; i + + ) {
urb - > iso_frame_desc [ i ] . status = 0 ;
urb - > iso_frame_desc [ i ] . actual_length = 0 ;
}
urb - > status = 0 ;
if ( ( status = usb_submit_urb ( urb , GFP_ATOMIC ) ) ) {
2005-11-08 21:38:27 -08:00
em28xx_errdev ( " resubmit of urb failed (error=%i) \n " , status ) ;
2005-11-08 21:37:07 -08:00
dev - > state | = DEV_MISCONFIGURED ;
}
wake_up_interruptible ( & dev - > wait_frame ) ;
return ;
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_uninit_isoc ( )
* deallocates the buffers and urbs allocated during em28xx_init_iosc ( )
2005-11-08 21:37:07 -08:00
*/
2005-11-08 21:38:27 -08:00
void em28xx_uninit_isoc ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
int i ;
2005-11-08 21:38:27 -08:00
for ( i = 0 ; i < EM28XX_NUM_BUFS ; i + + ) {
2005-11-08 21:37:07 -08:00
if ( dev - > urb [ i ] ) {
usb_kill_urb ( dev - > urb [ i ] ) ;
2005-11-08 21:37:46 -08:00
if ( dev - > transfer_buffer [ i ] ) {
2005-11-08 21:38:27 -08:00
usb_buffer_free ( dev - > udev , ( EM28XX_NUM_PACKETS * dev - > max_pkt_size ) , dev - > transfer_buffer [ i ] , dev - > urb [ i ] - > transfer_dma ) ;
2005-11-08 21:37:46 -08:00
}
2005-11-08 21:37:07 -08:00
usb_free_urb ( dev - > urb [ i ] ) ;
}
dev - > urb [ i ] = NULL ;
dev - > transfer_buffer [ i ] = NULL ;
}
2005-11-08 21:38:27 -08:00
em28xx_capture_start ( dev , 0 ) ;
2005-11-08 21:37:07 -08:00
}
/*
2005-11-08 21:38:27 -08:00
* em28xx_init_isoc ( )
2005-11-08 21:37:07 -08:00
* allocates transfer buffers and submits the urbs for isoc transfer
*/
2005-11-08 21:38:27 -08:00
int em28xx_init_isoc ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
/* change interface to 3 which allowes the biggest packet sizes */
int i , errCode ;
2005-11-08 21:38:27 -08:00
const int sb_size = EM28XX_NUM_PACKETS * dev - > max_pkt_size ;
2005-11-08 21:37:07 -08:00
/* reset streaming vars */
dev - > frame_current = NULL ;
dev - > frame_count = 0 ;
/* allocate urbs */
2005-11-08 21:38:27 -08:00
for ( i = 0 ; i < EM28XX_NUM_BUFS ; i + + ) {
2005-11-08 21:37:07 -08:00
struct urb * urb ;
int j , k ;
/* allocate transfer buffer */
2005-11-08 21:38:27 -08:00
urb = usb_alloc_urb ( EM28XX_NUM_PACKETS , GFP_KERNEL ) ;
2005-11-08 21:37:46 -08:00
if ( ! urb ) {
2005-11-08 21:38:27 -08:00
em28xx_errdev ( " cannot alloc urb %i \n " , i ) ;
em28xx_uninit_isoc ( dev ) ;
2005-11-08 21:37:46 -08:00
return - ENOMEM ;
}
dev - > transfer_buffer [ i ] = usb_buffer_alloc ( dev - > udev , sb_size , GFP_KERNEL , & urb - > transfer_dma ) ;
2005-11-08 21:37:07 -08:00
if ( ! dev - > transfer_buffer [ i ] ) {
2005-11-08 21:38:27 -08:00
em28xx_errdev
2005-11-08 21:37:07 -08:00
( " unable to allocate %i bytes for transfer buffer %i \n " ,
sb_size , i ) ;
2005-11-08 21:38:27 -08:00
em28xx_uninit_isoc ( dev ) ;
2005-11-08 21:37:07 -08:00
return - ENOMEM ;
}
memset ( dev - > transfer_buffer [ i ] , 0 , sb_size ) ;
2005-11-08 21:37:46 -08:00
urb - > dev = dev - > udev ;
urb - > context = dev ;
urb - > pipe = usb_rcvisocpipe ( dev - > udev , 0x82 ) ;
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > interval = 1 ;
urb - > transfer_buffer = dev - > transfer_buffer [ i ] ;
2005-11-08 21:38:27 -08:00
urb - > complete = em28xx_isocIrq ;
urb - > number_of_packets = EM28XX_NUM_PACKETS ;
2005-11-08 21:37:46 -08:00
urb - > transfer_buffer_length = sb_size ;
2005-11-08 21:38:27 -08:00
for ( j = k = 0 ; j < EM28XX_NUM_PACKETS ;
2005-11-08 21:37:46 -08:00
j + + , k + = dev - > max_pkt_size ) {
urb - > iso_frame_desc [ j ] . offset = k ;
urb - > iso_frame_desc [ j ] . length =
dev - > max_pkt_size ;
2005-11-08 21:37:07 -08:00
}
2005-11-08 21:37:46 -08:00
dev - > urb [ i ] = urb ;
2005-11-08 21:37:07 -08:00
}
/* submit urbs */
2005-11-08 21:38:27 -08:00
for ( i = 0 ; i < EM28XX_NUM_BUFS ; i + + ) {
2005-11-08 21:37:07 -08:00
errCode = usb_submit_urb ( dev - > urb [ i ] , GFP_KERNEL ) ;
if ( errCode ) {
2005-11-08 21:38:27 -08:00
em28xx_errdev ( " submit of urb %i failed (error=%i) \n " , i ,
2005-11-08 21:37:07 -08:00
errCode ) ;
2005-11-08 21:38:27 -08:00
em28xx_uninit_isoc ( dev ) ;
2005-11-08 21:37:07 -08:00
return errCode ;
}
}
return 0 ;
}
2005-11-08 21:38:27 -08:00
int em28xx_set_alternate ( struct em28xx * dev )
2005-11-08 21:37:07 -08:00
{
int errCode , prev_alt = dev - > alt ;
dev - > alt = alt ;
if ( dev - > alt = = 0 ) {
int i ;
2005-11-08 21:38:52 -08:00
for ( i = 0 ; i < dev - > num_alt ; i + + )
2005-11-08 21:38:43 -08:00
if ( dev - > alt_max_pkt_size [ i ] > dev - > alt_max_pkt_size [ dev - > alt ] )
dev - > alt = i ;
2005-11-08 21:37:07 -08:00
}
if ( dev - > alt ! = prev_alt ) {
dev - > max_pkt_size = dev - > alt_max_pkt_size [ dev - > alt ] ;
2005-11-08 21:38:52 -08:00
em28xx_coredbg ( " setting alternate %d with wMaxPacketSize=%u \n " , dev - > alt ,
2005-11-08 21:37:07 -08:00
dev - > max_pkt_size ) ;
errCode = usb_set_interface ( dev - > udev , 0 , dev - > alt ) ;
if ( errCode < 0 ) {
2005-11-08 21:38:52 -08:00
em28xx_errdev ( " cannot change alternate number to %d (error=%i) \n " ,
dev - > alt , errCode ) ;
2005-11-08 21:37:07 -08:00
return errCode ;
}
}
return 0 ;
}