2005-04-17 02:20:36 +04:00
# include <media/saa7146_vv.h>
static int max_memory = 32 ;
module_param ( max_memory , int , 0644 ) ;
MODULE_PARM_DESC ( max_memory , " maximum memory usage for capture buffers (default: 32Mb) " ) ;
# define IS_CAPTURE_ACTIVE(fh) \
( ( ( vv - > video_status & STATUS_CAPTURE ) ! = 0 ) & & ( vv - > video_fh = = fh ) )
# define IS_OVERLAY_ACTIVE(fh) \
( ( ( vv - > video_status & STATUS_OVERLAY ) ! = 0 ) & & ( vv - > video_fh = = fh ) )
/* format descriptions for capture and preview */
static struct saa7146_format formats [ ] = {
{
. name = " RGB-8 (3-3-2) " ,
. pixelformat = V4L2_PIX_FMT_RGB332 ,
. trans = RGB08_COMPOSED ,
. depth = 8 ,
. flags = 0 ,
} , {
. name = " RGB-16 (5/B-6/G-5/R) " ,
. pixelformat = V4L2_PIX_FMT_RGB565 ,
. trans = RGB16_COMPOSED ,
. depth = 16 ,
. flags = 0 ,
} , {
. name = " RGB-24 (B-G-R) " ,
. pixelformat = V4L2_PIX_FMT_BGR24 ,
. trans = RGB24_COMPOSED ,
. depth = 24 ,
. flags = 0 ,
} , {
. name = " RGB-32 (B-G-R) " ,
. pixelformat = V4L2_PIX_FMT_BGR32 ,
. trans = RGB32_COMPOSED ,
. depth = 32 ,
. flags = 0 ,
} , {
. name = " RGB-32 (R-G-B) " ,
. pixelformat = V4L2_PIX_FMT_RGB32 ,
. trans = RGB32_COMPOSED ,
. depth = 32 ,
. flags = 0 ,
. swap = 0x2 ,
} , {
. name = " Greyscale-8 " ,
. pixelformat = V4L2_PIX_FMT_GREY ,
. trans = Y8 ,
. depth = 8 ,
. flags = 0 ,
} , {
. name = " YUV 4:2:2 planar (Y-Cb-Cr) " ,
. pixelformat = V4L2_PIX_FMT_YUV422P ,
. trans = YUV422_DECOMPOSED ,
. depth = 16 ,
. flags = FORMAT_BYTE_SWAP | FORMAT_IS_PLANAR ,
} , {
. name = " YVU 4:2:0 planar (Y-Cb-Cr) " ,
. pixelformat = V4L2_PIX_FMT_YVU420 ,
. trans = YUV420_DECOMPOSED ,
. depth = 12 ,
. flags = FORMAT_BYTE_SWAP | FORMAT_IS_PLANAR ,
} , {
. name = " YUV 4:2:0 planar (Y-Cb-Cr) " ,
. pixelformat = V4L2_PIX_FMT_YUV420 ,
. trans = YUV420_DECOMPOSED ,
. depth = 12 ,
. flags = FORMAT_IS_PLANAR ,
} , {
. name = " YUV 4:2:2 (U-Y-V-Y) " ,
. pixelformat = V4L2_PIX_FMT_UYVY ,
. trans = YUV422_COMPOSED ,
. depth = 16 ,
. flags = 0 ,
}
} ;
/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
due to this , it ' s impossible to provide additional * packed * formats , which are simply byte swapped
( like V4L2_PIX_FMT_YUYV ) . . . 8 - ( */
static int NUM_FORMATS = sizeof ( formats ) / sizeof ( struct saa7146_format ) ;
struct saa7146_format * format_by_fourcc ( struct saa7146_dev * dev , int fourcc )
{
int i , j = NUM_FORMATS ;
for ( i = 0 ; i < j ; i + + ) {
if ( formats [ i ] . pixelformat = = fourcc ) {
return formats + i ;
}
}
DEB_D ( ( " unknown pixelformat:'%4.4s' \n " , ( char * ) & fourcc ) ) ;
return NULL ;
}
static int g_fmt ( struct saa7146_fh * fh , struct v4l2_format * f )
{
struct saa7146_dev * dev = fh - > dev ;
DEB_EE ( ( " dev:%p, fh:%p \n " , dev , fh ) ) ;
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
f - > fmt . pix = fh - > video_fmt ;
return 0 ;
case V4L2_BUF_TYPE_VIDEO_OVERLAY :
f - > fmt . win = fh - > ov . win ;
return 0 ;
case V4L2_BUF_TYPE_VBI_CAPTURE :
{
f - > fmt . vbi = fh - > vbi_fmt ;
return 0 ;
}
default :
DEB_D ( ( " invalid format type '%d'. \n " , f - > type ) ) ;
return - EINVAL ;
}
}
static int try_win ( struct saa7146_dev * dev , struct v4l2_window * win )
{
struct saa7146_vv * vv = dev - > vv_data ;
enum v4l2_field field ;
int maxw , maxh ;
DEB_EE ( ( " dev:%p \n " , dev ) ) ;
if ( NULL = = vv - > ov_fb . base ) {
DEB_D ( ( " no fb base set. \n " ) ) ;
return - EINVAL ;
}
if ( NULL = = vv - > ov_fmt ) {
DEB_D ( ( " no fb fmt set. \n " ) ) ;
return - EINVAL ;
}
if ( win - > w . width < 48 | | win - > w . height < 32 ) {
DEB_D ( ( " min width/height. (%d,%d) \n " , win - > w . width , win - > w . height ) ) ;
return - EINVAL ;
}
if ( win - > clipcount > 16 ) {
DEB_D ( ( " clipcount too big. \n " ) ) ;
return - EINVAL ;
}
field = win - > field ;
maxw = vv - > standard - > h_max_out ;
maxh = vv - > standard - > v_max_out ;
if ( V4L2_FIELD_ANY = = field ) {
2005-12-12 11:37:27 +03:00
field = ( win - > w . height > maxh / 2 )
2006-01-09 20:25:34 +03:00
? V4L2_FIELD_INTERLACED
: V4L2_FIELD_TOP ;
2005-12-12 11:37:27 +03:00
}
switch ( field ) {
case V4L2_FIELD_TOP :
case V4L2_FIELD_BOTTOM :
case V4L2_FIELD_ALTERNATE :
maxh = maxh / 2 ;
break ;
case V4L2_FIELD_INTERLACED :
break ;
default : {
2005-04-17 02:20:36 +04:00
DEB_D ( ( " no known field mode '%d'. \n " , field ) ) ;
2005-12-12 11:37:27 +03:00
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
}
win - > field = field ;
if ( win - > w . width > maxw )
win - > w . width = maxw ;
if ( win - > w . height > maxh )
win - > w . height = maxh ;
return 0 ;
}
static int try_fmt ( struct saa7146_fh * fh , struct v4l2_format * f )
{
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
int err ;
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
{
struct saa7146_format * fmt ;
enum v4l2_field field ;
int maxw , maxh ;
int calc_bpl ;
DEB_EE ( ( " V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p \n " , dev , fh ) ) ;
fmt = format_by_fourcc ( dev , f - > fmt . pix . pixelformat ) ;
if ( NULL = = fmt ) {
return - EINVAL ;
}
field = f - > fmt . pix . field ;
maxw = vv - > standard - > h_max_out ;
maxh = vv - > standard - > v_max_out ;
if ( V4L2_FIELD_ANY = = field ) {
field = ( f - > fmt . pix . height > maxh / 2 )
? V4L2_FIELD_INTERLACED
: V4L2_FIELD_BOTTOM ;
}
switch ( field ) {
case V4L2_FIELD_ALTERNATE : {
vv - > last_field = V4L2_FIELD_TOP ;
maxh = maxh / 2 ;
break ;
}
case V4L2_FIELD_TOP :
case V4L2_FIELD_BOTTOM :
vv - > last_field = V4L2_FIELD_INTERLACED ;
maxh = maxh / 2 ;
break ;
case V4L2_FIELD_INTERLACED :
vv - > last_field = V4L2_FIELD_INTERLACED ;
break ;
default : {
DEB_D ( ( " no known field mode '%d'. \n " , field ) ) ;
return - EINVAL ;
}
}
f - > fmt . pix . field = field ;
if ( f - > fmt . pix . width > maxw )
f - > fmt . pix . width = maxw ;
if ( f - > fmt . pix . height > maxh )
f - > fmt . pix . height = maxh ;
calc_bpl = ( f - > fmt . pix . width * fmt - > depth ) / 8 ;
if ( f - > fmt . pix . bytesperline < calc_bpl )
f - > fmt . pix . bytesperline = calc_bpl ;
if ( f - > fmt . pix . bytesperline > ( 2 * PAGE_SIZE * fmt - > depth ) / 8 ) /* arbitrary constraint */
f - > fmt . pix . bytesperline = calc_bpl ;
f - > fmt . pix . sizeimage = f - > fmt . pix . bytesperline * f - > fmt . pix . height ;
DEB_D ( ( " w:%d, h:%d, bytesperline:%d, sizeimage:%d \n " , f - > fmt . pix . width , f - > fmt . pix . height , f - > fmt . pix . bytesperline , f - > fmt . pix . sizeimage ) ) ;
return 0 ;
}
case V4L2_BUF_TYPE_VIDEO_OVERLAY :
DEB_EE ( ( " V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p \n " , dev , fh ) ) ;
err = try_win ( dev , & f - > fmt . win ) ;
if ( 0 ! = err ) {
return err ;
}
return 0 ;
default :
DEB_EE ( ( " unknown format type '%d' \n " , f - > type ) ) ;
return - EINVAL ;
}
}
int saa7146_start_preview ( struct saa7146_fh * fh )
{
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
int ret = 0 , err = 0 ;
DEB_EE ( ( " dev:%p, fh:%p \n " , dev , fh ) ) ;
/* check if we have overlay informations */
if ( NULL = = fh - > ov . fh ) {
DEB_D ( ( " no overlay data available. try S_FMT first. \n " ) ) ;
return - EAGAIN ;
}
/* check if streaming capture is running */
if ( IS_CAPTURE_ACTIVE ( fh ) ! = 0 ) {
DEB_D ( ( " streaming capture is active. \n " ) ) ;
return - EBUSY ;
}
/* check if overlay is running */
if ( IS_OVERLAY_ACTIVE ( fh ) ! = 0 ) {
if ( vv - > video_fh = = fh ) {
DEB_D ( ( " overlay is already active. \n " ) ) ;
return 0 ;
}
DEB_D ( ( " overlay is already active in another open. \n " ) ) ;
return - EBUSY ;
}
if ( 0 = = saa7146_res_get ( fh , RESOURCE_DMA1_HPS | RESOURCE_DMA2_CLP ) ) {
DEB_D ( ( " cannot get necessary overlay resources \n " ) ) ;
return - EBUSY ;
}
err = try_win ( dev , & fh - > ov . win ) ;
if ( 0 ! = err ) {
saa7146_res_free ( vv - > video_fh , RESOURCE_DMA1_HPS | RESOURCE_DMA2_CLP ) ;
return - EBUSY ;
}
vv - > ov_data = & fh - > ov ;
DEB_D ( ( " %dx%d+%d+%d %s field=%s \n " ,
fh - > ov . win . w . width , fh - > ov . win . w . height ,
fh - > ov . win . w . left , fh - > ov . win . w . top ,
vv - > ov_fmt - > name , v4l2_field_names [ fh - > ov . win . field ] ) ) ;
if ( 0 ! = ( ret = saa7146_enable_overlay ( fh ) ) ) {
DEB_D ( ( " enabling overlay failed: %d \n " , ret ) ) ;
saa7146_res_free ( vv - > video_fh , RESOURCE_DMA1_HPS | RESOURCE_DMA2_CLP ) ;
return ret ;
}
vv - > video_status = STATUS_OVERLAY ;
vv - > video_fh = fh ;
return 0 ;
}
2006-04-11 17:26:57 +04:00
EXPORT_SYMBOL_GPL ( saa7146_start_preview ) ;
2005-04-17 02:20:36 +04:00
int saa7146_stop_preview ( struct saa7146_fh * fh )
{
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
DEB_EE ( ( " dev:%p, fh:%p \n " , dev , fh ) ) ;
/* check if streaming capture is running */
if ( IS_CAPTURE_ACTIVE ( fh ) ! = 0 ) {
DEB_D ( ( " streaming capture is active. \n " ) ) ;
return - EBUSY ;
}
/* check if overlay is running at all */
if ( ( vv - > video_status & STATUS_OVERLAY ) = = 0 ) {
DEB_D ( ( " no active overlay. \n " ) ) ;
return 0 ;
}
if ( vv - > video_fh ! = fh ) {
DEB_D ( ( " overlay is active, but in another open. \n " ) ) ;
return - EBUSY ;
}
vv - > video_status = 0 ;
vv - > video_fh = NULL ;
saa7146_disable_overlay ( fh ) ;
saa7146_res_free ( fh , RESOURCE_DMA1_HPS | RESOURCE_DMA2_CLP ) ;
return 0 ;
}
2006-04-11 17:26:57 +04:00
EXPORT_SYMBOL_GPL ( saa7146_stop_preview ) ;
2005-04-17 02:20:36 +04:00
static int s_fmt ( struct saa7146_fh * fh , struct v4l2_format * f )
{
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
int err ;
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
DEB_EE ( ( " V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p \n " , dev , fh ) ) ;
if ( IS_CAPTURE_ACTIVE ( fh ) ! = 0 ) {
DEB_EE ( ( " streaming capture is active \n " ) ) ;
return - EBUSY ;
}
err = try_fmt ( fh , f ) ;
if ( 0 ! = err )
return err ;
fh - > video_fmt = f - > fmt . pix ;
DEB_EE ( ( " set to pixelformat '%4.4s' \n " , ( char * ) & fh - > video_fmt . pixelformat ) ) ;
return 0 ;
case V4L2_BUF_TYPE_VIDEO_OVERLAY :
DEB_EE ( ( " V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p \n " , dev , fh ) ) ;
err = try_win ( dev , & f - > fmt . win ) ;
if ( 0 ! = err )
return err ;
2006-02-07 11:49:14 +03:00
mutex_lock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
fh - > ov . win = f - > fmt . win ;
fh - > ov . nclips = f - > fmt . win . clipcount ;
if ( fh - > ov . nclips > 16 )
fh - > ov . nclips = 16 ;
if ( copy_from_user ( fh - > ov . clips , f - > fmt . win . clips , sizeof ( struct v4l2_clip ) * fh - > ov . nclips ) ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
}
/* fh->ov.fh is used to indicate that we have valid overlay informations, too */
fh - > ov . fh = fh ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
/* check if our current overlay is active */
if ( IS_OVERLAY_ACTIVE ( fh ) ! = 0 ) {
saa7146_stop_preview ( fh ) ;
saa7146_start_preview ( fh ) ;
}
return 0 ;
default :
DEB_D ( ( " unknown format type '%d' \n " , f - > type ) ) ;
return - EINVAL ;
}
}
/********************************************************************************/
/* device controls */
static struct v4l2_queryctrl controls [ ] = {
{
. id = V4L2_CID_BRIGHTNESS ,
. name = " Brightness " ,
. minimum = 0 ,
. maximum = 255 ,
. step = 1 ,
. default_value = 128 ,
. type = V4L2_CTRL_TYPE_INTEGER ,
} , {
. id = V4L2_CID_CONTRAST ,
. name = " Contrast " ,
. minimum = 0 ,
. maximum = 127 ,
. step = 1 ,
. default_value = 64 ,
. type = V4L2_CTRL_TYPE_INTEGER ,
} , {
. id = V4L2_CID_SATURATION ,
. name = " Saturation " ,
. minimum = 0 ,
. maximum = 127 ,
. step = 1 ,
. default_value = 64 ,
. type = V4L2_CTRL_TYPE_INTEGER ,
} , {
. id = V4L2_CID_VFLIP ,
. name = " Vertical flip " ,
. minimum = 0 ,
. maximum = 1 ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
} , {
. id = V4L2_CID_HFLIP ,
. name = " Horizontal flip " ,
. minimum = 0 ,
. maximum = 1 ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
} ,
} ;
static int NUM_CONTROLS = sizeof ( controls ) / sizeof ( struct v4l2_queryctrl ) ;
# define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0)
static struct v4l2_queryctrl * ctrl_by_id ( int id )
{
int i ;
for ( i = 0 ; i < NUM_CONTROLS ; i + + )
if ( controls [ i ] . id = = id )
return controls + i ;
return NULL ;
}
static int get_control ( struct saa7146_fh * fh , struct v4l2_control * c )
{
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
const struct v4l2_queryctrl * ctrl ;
u32 value = 0 ;
ctrl = ctrl_by_id ( c - > id ) ;
if ( NULL = = ctrl )
return - EINVAL ;
switch ( c - > id ) {
case V4L2_CID_BRIGHTNESS :
value = saa7146_read ( dev , BCS_CTRL ) ;
c - > value = 0xff & ( value > > 24 ) ;
DEB_D ( ( " V4L2_CID_BRIGHTNESS: %d \n " , c - > value ) ) ;
break ;
case V4L2_CID_CONTRAST :
value = saa7146_read ( dev , BCS_CTRL ) ;
c - > value = 0x7f & ( value > > 16 ) ;
DEB_D ( ( " V4L2_CID_CONTRAST: %d \n " , c - > value ) ) ;
break ;
case V4L2_CID_SATURATION :
value = saa7146_read ( dev , BCS_CTRL ) ;
c - > value = 0x7f & ( value > > 0 ) ;
DEB_D ( ( " V4L2_CID_SATURATION: %d \n " , c - > value ) ) ;
break ;
case V4L2_CID_VFLIP :
c - > value = vv - > vflip ;
DEB_D ( ( " V4L2_CID_VFLIP: %d \n " , c - > value ) ) ;
break ;
case V4L2_CID_HFLIP :
c - > value = vv - > hflip ;
DEB_D ( ( " V4L2_CID_HFLIP: %d \n " , c - > value ) ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int set_control ( struct saa7146_fh * fh , struct v4l2_control * c )
{
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
const struct v4l2_queryctrl * ctrl ;
ctrl = ctrl_by_id ( c - > id ) ;
if ( NULL = = ctrl ) {
DEB_D ( ( " unknown control %d \n " , c - > id ) ) ;
return - EINVAL ;
}
2006-02-07 11:49:14 +03:00
mutex_lock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
switch ( ctrl - > type ) {
case V4L2_CTRL_TYPE_BOOLEAN :
case V4L2_CTRL_TYPE_MENU :
case V4L2_CTRL_TYPE_INTEGER :
if ( c - > value < ctrl - > minimum )
c - > value = ctrl - > minimum ;
if ( c - > value > ctrl - > maximum )
c - > value = ctrl - > maximum ;
break ;
default :
/* nothing */ ;
} ;
switch ( c - > id ) {
case V4L2_CID_BRIGHTNESS : {
u32 value = saa7146_read ( dev , BCS_CTRL ) ;
value & = 0x00ffffff ;
value | = ( c - > value < < 24 ) ;
saa7146_write ( dev , BCS_CTRL , value ) ;
saa7146_write ( dev , MC2 , MASK_22 | MASK_06 ) ;
break ;
}
case V4L2_CID_CONTRAST : {
u32 value = saa7146_read ( dev , BCS_CTRL ) ;
value & = 0xff00ffff ;
value | = ( c - > value < < 16 ) ;
saa7146_write ( dev , BCS_CTRL , value ) ;
saa7146_write ( dev , MC2 , MASK_22 | MASK_06 ) ;
break ;
}
case V4L2_CID_SATURATION : {
u32 value = saa7146_read ( dev , BCS_CTRL ) ;
value & = 0xffffff00 ;
value | = ( c - > value < < 0 ) ;
saa7146_write ( dev , BCS_CTRL , value ) ;
saa7146_write ( dev , MC2 , MASK_22 | MASK_06 ) ;
break ;
}
case V4L2_CID_HFLIP :
/* fixme: we can support changing VFLIP and HFLIP here... */
if ( IS_CAPTURE_ACTIVE ( fh ) ! = 0 ) {
DEB_D ( ( " V4L2_CID_HFLIP while active capture. \n " ) ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
vv - > hflip = c - > value ;
break ;
case V4L2_CID_VFLIP :
if ( IS_CAPTURE_ACTIVE ( fh ) ! = 0 ) {
DEB_D ( ( " V4L2_CID_VFLIP while active capture. \n " ) ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
vv - > vflip = c - > value ;
break ;
default : {
return - EINVAL ;
}
}
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
if ( IS_OVERLAY_ACTIVE ( fh ) ! = 0 ) {
saa7146_stop_preview ( fh ) ;
saa7146_start_preview ( fh ) ;
}
return 0 ;
}
/********************************************************************************/
/* common pagetable functions */
static int saa7146_pgtable_build ( struct saa7146_dev * dev , struct saa7146_buf * buf )
{
struct pci_dev * pci = dev - > pci ;
2007-08-23 23:37:49 +04:00
struct videobuf_dmabuf * dma = videobuf_to_dma ( & buf - > vb ) ;
struct scatterlist * list = dma - > sglist ;
int length = dma - > sglen ;
2005-04-17 02:20:36 +04:00
struct saa7146_format * sfmt = format_by_fourcc ( dev , buf - > fmt - > pixelformat ) ;
DEB_EE ( ( " dev:%p, buf:%p, sg_len:%d \n " , dev , buf , length ) ) ;
if ( 0 ! = IS_PLANAR ( sfmt - > trans ) ) {
struct saa7146_pgtable * pt1 = & buf - > pt [ 0 ] ;
struct saa7146_pgtable * pt2 = & buf - > pt [ 1 ] ;
struct saa7146_pgtable * pt3 = & buf - > pt [ 2 ] ;
u32 * ptr1 , * ptr2 , * ptr3 ;
u32 fill ;
int size = buf - > fmt - > width * buf - > fmt - > height ;
int i , p , m1 , m2 , m3 , o1 , o2 ;
switch ( sfmt - > depth ) {
case 12 : {
/* create some offsets inside the page table */
m1 = ( ( size + PAGE_SIZE ) / PAGE_SIZE ) - 1 ;
m2 = ( ( size + ( size / 4 ) + PAGE_SIZE ) / PAGE_SIZE ) - 1 ;
m3 = ( ( size + ( size / 2 ) + PAGE_SIZE ) / PAGE_SIZE ) - 1 ;
o1 = size % PAGE_SIZE ;
o2 = ( size + ( size / 4 ) ) % PAGE_SIZE ;
DEB_CAP ( ( " size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d \n " , size , m1 , m2 , m3 , o1 , o2 ) ) ;
break ;
}
case 16 : {
/* create some offsets inside the page table */
m1 = ( ( size + PAGE_SIZE ) / PAGE_SIZE ) - 1 ;
m2 = ( ( size + ( size / 2 ) + PAGE_SIZE ) / PAGE_SIZE ) - 1 ;
m3 = ( ( 2 * size + PAGE_SIZE ) / PAGE_SIZE ) - 1 ;
o1 = size % PAGE_SIZE ;
o2 = ( size + ( size / 2 ) ) % PAGE_SIZE ;
DEB_CAP ( ( " size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d \n " , size , m1 , m2 , m3 , o1 , o2 ) ) ;
break ;
}
default : {
return - 1 ;
}
}
ptr1 = pt1 - > cpu ;
ptr2 = pt2 - > cpu ;
ptr3 = pt3 - > cpu ;
/* walk all pages, copy all page addresses to ptr1 */
for ( i = 0 ; i < length ; i + + , list + + ) {
for ( p = 0 ; p * 4096 < list - > length ; p + + , ptr1 + + ) {
* ptr1 = cpu_to_le32 ( sg_dma_address ( list ) - list - > offset ) ;
}
}
/*
ptr1 = pt1 - > cpu ;
for ( j = 0 ; j < 40 ; j + + ) {
printk ( " ptr1 %d: 0x%08x \n " , j , ptr1 [ j ] ) ;
}
*/
/* if we have a user buffer, the first page may not be
aligned to a page boundary . */
2007-08-23 23:37:49 +04:00
pt1 - > offset = list - > offset ;
2005-04-17 02:20:36 +04:00
pt2 - > offset = pt1 - > offset + o1 ;
pt3 - > offset = pt1 - > offset + o2 ;
/* create video-dma2 page table */
ptr1 = pt1 - > cpu ;
for ( i = m1 ; i < = m2 ; i + + , ptr2 + + ) {
* ptr2 = ptr1 [ i ] ;
}
fill = * ( ptr2 - 1 ) ;
for ( ; i < 1024 ; i + + , ptr2 + + ) {
* ptr2 = fill ;
}
/* create video-dma3 page table */
ptr1 = pt1 - > cpu ;
for ( i = m2 ; i < = m3 ; i + + , ptr3 + + ) {
* ptr3 = ptr1 [ i ] ;
}
fill = * ( ptr3 - 1 ) ;
for ( ; i < 1024 ; i + + , ptr3 + + ) {
* ptr3 = fill ;
}
/* finally: finish up video-dma1 page table */
ptr1 = pt1 - > cpu + m1 ;
fill = pt1 - > cpu [ m1 ] ;
for ( i = m1 ; i < 1024 ; i + + , ptr1 + + ) {
* ptr1 = fill ;
}
/*
ptr1 = pt1 - > cpu ;
ptr2 = pt2 - > cpu ;
ptr3 = pt3 - > cpu ;
for ( j = 0 ; j < 40 ; j + + ) {
printk ( " ptr1 %d: 0x%08x \n " , j , ptr1 [ j ] ) ;
}
for ( j = 0 ; j < 40 ; j + + ) {
printk ( " ptr2 %d: 0x%08x \n " , j , ptr2 [ j ] ) ;
}
for ( j = 0 ; j < 40 ; j + + ) {
printk ( " ptr3 %d: 0x%08x \n " , j , ptr3 [ j ] ) ;
}
*/
} else {
struct saa7146_pgtable * pt = & buf - > pt [ 0 ] ;
return saa7146_pgtable_build_single ( pci , pt , list , length ) ;
}
return 0 ;
}
/********************************************************************************/
/* file operations */
static int video_begin ( struct saa7146_fh * fh )
{
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
struct saa7146_format * fmt = NULL ;
unsigned int resource ;
int ret = 0 , err = 0 ;
DEB_EE ( ( " dev:%p, fh:%p \n " , dev , fh ) ) ;
if ( ( vv - > video_status & STATUS_CAPTURE ) ! = 0 ) {
if ( vv - > video_fh = = fh ) {
DEB_S ( ( " already capturing. \n " ) ) ;
return 0 ;
}
DEB_S ( ( " already capturing in another open. \n " ) ) ;
return - EBUSY ;
}
if ( ( vv - > video_status & STATUS_OVERLAY ) ! = 0 ) {
DEB_S ( ( " warning: suspending overlay video for streaming capture. \n " ) ) ;
vv - > ov_suspend = vv - > video_fh ;
err = saa7146_stop_preview ( vv - > video_fh ) ; /* side effect: video_status is now 0, video_fh is NULL */
if ( 0 ! = err ) {
DEB_D ( ( " suspending video failed. aborting \n " ) ) ;
return err ;
}
}
fmt = format_by_fourcc ( dev , fh - > video_fmt . pixelformat ) ;
/* we need to have a valid format set here */
BUG_ON ( NULL = = fmt ) ;
if ( 0 ! = ( fmt - > flags & FORMAT_IS_PLANAR ) ) {
resource = RESOURCE_DMA1_HPS | RESOURCE_DMA2_CLP | RESOURCE_DMA3_BRS ;
} else {
resource = RESOURCE_DMA1_HPS ;
}
ret = saa7146_res_get ( fh , resource ) ;
if ( 0 = = ret ) {
DEB_S ( ( " cannot get capture resource %d \n " , resource ) ) ;
if ( vv - > ov_suspend ! = NULL ) {
saa7146_start_preview ( vv - > ov_suspend ) ;
vv - > ov_suspend = NULL ;
}
return - EBUSY ;
}
/* clear out beginning of streaming bit (rps register 0)*/
saa7146_write ( dev , MC2 , MASK_27 ) ;
/* enable rps0 irqs */
SAA7146_IER_ENABLE ( dev , MASK_27 ) ;
vv - > video_fh = fh ;
vv - > video_status = STATUS_CAPTURE ;
return 0 ;
}
static int video_end ( struct saa7146_fh * fh , struct file * file )
{
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
struct saa7146_format * fmt = NULL ;
unsigned long flags ;
unsigned int resource ;
u32 dmas = 0 ;
DEB_EE ( ( " dev:%p, fh:%p \n " , dev , fh ) ) ;
if ( ( vv - > video_status & STATUS_CAPTURE ) ! = STATUS_CAPTURE ) {
DEB_S ( ( " not capturing. \n " ) ) ;
return 0 ;
}
if ( vv - > video_fh ! = fh ) {
DEB_S ( ( " capturing, but in another open. \n " ) ) ;
return - EBUSY ;
}
fmt = format_by_fourcc ( dev , fh - > video_fmt . pixelformat ) ;
/* we need to have a valid format set here */
BUG_ON ( NULL = = fmt ) ;
if ( 0 ! = ( fmt - > flags & FORMAT_IS_PLANAR ) ) {
resource = RESOURCE_DMA1_HPS | RESOURCE_DMA2_CLP | RESOURCE_DMA3_BRS ;
dmas = MASK_22 | MASK_21 | MASK_20 ;
} else {
resource = RESOURCE_DMA1_HPS ;
dmas = MASK_22 ;
}
spin_lock_irqsave ( & dev - > slock , flags ) ;
/* disable rps0 */
saa7146_write ( dev , MC1 , MASK_28 ) ;
/* disable rps0 irqs */
SAA7146_IER_DISABLE ( dev , MASK_27 ) ;
/* shut down all used video dma transfers */
saa7146_write ( dev , MC1 , dmas ) ;
spin_unlock_irqrestore ( & dev - > slock , flags ) ;
vv - > video_fh = NULL ;
vv - > video_status = 0 ;
saa7146_res_free ( fh , resource ) ;
if ( vv - > ov_suspend ! = NULL ) {
saa7146_start_preview ( vv - > ov_suspend ) ;
vv - > ov_suspend = NULL ;
}
return 0 ;
}
/*
* This function is _not_ called directly , but from
* video_generic_ioctl ( and maybe others ) . userspace
* copying is done already , arg is a kernel pointer .
*/
int saa7146_video_do_ioctl ( struct inode * inode , struct file * file , unsigned int cmd , void * arg )
{
struct saa7146_fh * fh = file - > private_data ;
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
int err = 0 , result = 0 , ee = 0 ;
struct saa7146_use_ops * ops ;
struct videobuf_queue * q ;
/* check if extension handles the command */
for ( ee = 0 ; dev - > ext_vv_data - > ioctls [ ee ] . flags ! = 0 ; ee + + ) {
if ( cmd = = dev - > ext_vv_data - > ioctls [ ee ] . cmd )
break ;
}
if ( 0 ! = ( dev - > ext_vv_data - > ioctls [ ee ] . flags & SAA7146_EXCLUSIVE ) ) {
DEB_D ( ( " extension handles ioctl exclusive. \n " ) ) ;
result = dev - > ext_vv_data - > ioctl ( fh , cmd , arg ) ;
return result ;
}
if ( 0 ! = ( dev - > ext_vv_data - > ioctls [ ee ] . flags & SAA7146_BEFORE ) ) {
DEB_D ( ( " extension handles ioctl before. \n " ) ) ;
result = dev - > ext_vv_data - > ioctl ( fh , cmd , arg ) ;
if ( - EAGAIN ! = result ) {
return result ;
}
}
/* fixme: add handle "after" case (is it still needed?) */
switch ( fh - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE : {
ops = & saa7146_video_uops ;
q = & fh - > video_q ;
break ;
}
case V4L2_BUF_TYPE_VBI_CAPTURE : {
ops = & saa7146_vbi_uops ;
q = & fh - > vbi_q ;
break ;
}
default :
BUG ( ) ;
return 0 ;
}
switch ( cmd ) {
case VIDIOC_QUERYCAP :
{
struct v4l2_capability * cap = arg ;
memset ( cap , 0 , sizeof ( * cap ) ) ;
DEB_EE ( ( " VIDIOC_QUERYCAP \n " ) ) ;
2007-07-13 03:37:50 +04:00
strcpy ( ( char * ) cap - > driver , " saa7146 v4l2 " ) ;
strlcpy ( ( char * ) cap - > card , dev - > ext - > name , sizeof ( cap - > card ) ) ;
sprintf ( ( char * ) cap - > bus_info , " PCI:%s " , pci_name ( dev - > pci ) ) ;
2005-04-17 02:20:36 +04:00
cap - > version = SAA7146_VERSION_CODE ;
cap - > capabilities =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VIDEO_OVERLAY |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING ;
cap - > capabilities | = dev - > ext_vv_data - > capabilities ;
return 0 ;
}
case VIDIOC_G_FBUF :
{
struct v4l2_framebuffer * fb = arg ;
DEB_EE ( ( " VIDIOC_G_FBUF \n " ) ) ;
* fb = vv - > ov_fb ;
fb - > capability = V4L2_FBUF_CAP_LIST_CLIPPING ;
return 0 ;
}
case VIDIOC_S_FBUF :
{
struct v4l2_framebuffer * fb = arg ;
struct saa7146_format * fmt ;
DEB_EE ( ( " VIDIOC_S_FBUF \n " ) ) ;
if ( ! capable ( CAP_SYS_ADMIN ) & &
! capable ( CAP_SYS_RAWIO ) )
return - EPERM ;
/* check args */
fmt = format_by_fourcc ( dev , fb - > fmt . pixelformat ) ;
if ( NULL = = fmt ) {
return - EINVAL ;
}
/* planar formats are not allowed for overlay video, clipping and video dma would clash */
if ( 0 ! = ( fmt - > flags & FORMAT_IS_PLANAR ) ) {
DEB_S ( ( " planar pixelformat '%4.4s' not allowed for overlay \n " , ( char * ) & fmt - > pixelformat ) ) ;
}
/* check if overlay is running */
if ( IS_OVERLAY_ACTIVE ( fh ) ! = 0 ) {
if ( vv - > video_fh ! = fh ) {
DEB_D ( ( " refusing to change framebuffer informations while overlay is active in another open. \n " ) ) ;
return - EBUSY ;
}
}
2006-02-07 11:49:14 +03:00
mutex_lock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
/* ok, accept it */
vv - > ov_fb = * fb ;
vv - > ov_fmt = fmt ;
if ( 0 = = vv - > ov_fb . fmt . bytesperline )
vv - > ov_fb . fmt . bytesperline =
vv - > ov_fb . fmt . width * fmt - > depth / 8 ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
case VIDIOC_ENUM_FMT :
{
struct v4l2_fmtdesc * f = arg ;
int index ;
switch ( f - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
case V4L2_BUF_TYPE_VIDEO_OVERLAY : {
index = f - > index ;
if ( index < 0 | | index > = NUM_FORMATS ) {
return - EINVAL ;
}
memset ( f , 0 , sizeof ( * f ) ) ;
f - > index = index ;
2007-07-13 03:37:50 +04:00
strlcpy ( ( char * ) f - > description , formats [ index ] . name , sizeof ( f - > description ) ) ;
2005-04-17 02:20:36 +04:00
f - > pixelformat = formats [ index ] . pixelformat ;
break ;
}
default :
return - EINVAL ;
}
DEB_EE ( ( " VIDIOC_ENUM_FMT: type:%d, index:%d \n " , f - > type , f - > index ) ) ;
return 0 ;
}
case VIDIOC_QUERYCTRL :
{
const struct v4l2_queryctrl * ctrl ;
struct v4l2_queryctrl * c = arg ;
if ( ( c - > id < V4L2_CID_BASE | |
c - > id > = V4L2_CID_LASTP1 ) & &
( c - > id < V4L2_CID_PRIVATE_BASE | |
c - > id > = V4L2_CID_PRIVATE_LASTP1 ) )
return - EINVAL ;
ctrl = ctrl_by_id ( c - > id ) ;
if ( NULL = = ctrl ) {
return - EINVAL ;
/*
c - > flags = V4L2_CTRL_FLAG_DISABLED ;
return 0 ;
*/
}
DEB_EE ( ( " VIDIOC_QUERYCTRL: id:%d \n " , c - > id ) ) ;
* c = * ctrl ;
return 0 ;
}
case VIDIOC_G_CTRL : {
DEB_EE ( ( " VIDIOC_G_CTRL \n " ) ) ;
return get_control ( fh , arg ) ;
}
case VIDIOC_S_CTRL :
{
DEB_EE ( ( " VIDIOC_S_CTRL \n " ) ) ;
err = set_control ( fh , arg ) ;
return err ;
}
2005-12-12 11:37:27 +03:00
case VIDIOC_G_PARM :
{
struct v4l2_streamparm * parm = arg ;
2005-04-17 02:20:36 +04:00
if ( parm - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE ) {
return - EINVAL ;
}
2005-12-12 11:37:27 +03:00
memset ( & parm - > parm . capture , 0 , sizeof ( struct v4l2_captureparm ) ) ;
2005-04-17 02:20:36 +04:00
parm - > parm . capture . readbuffers = 1 ;
// fixme: only for PAL!
parm - > parm . capture . timeperframe . numerator = 1 ;
parm - > parm . capture . timeperframe . denominator = 25 ;
2005-12-12 11:37:27 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
case VIDIOC_G_FMT :
{
struct v4l2_format * f = arg ;
DEB_EE ( ( " VIDIOC_G_FMT \n " ) ) ;
return g_fmt ( fh , f ) ;
}
case VIDIOC_S_FMT :
{
struct v4l2_format * f = arg ;
DEB_EE ( ( " VIDIOC_S_FMT \n " ) ) ;
return s_fmt ( fh , f ) ;
}
case VIDIOC_TRY_FMT :
{
struct v4l2_format * f = arg ;
DEB_EE ( ( " VIDIOC_TRY_FMT \n " ) ) ;
return try_fmt ( fh , f ) ;
}
case VIDIOC_G_STD :
{
v4l2_std_id * id = arg ;
DEB_EE ( ( " VIDIOC_G_STD \n " ) ) ;
* id = vv - > standard - > id ;
return 0 ;
}
/* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
PAL / NTSC / SECAM . if your hardware does not ( or does more )
- - override this function in your extension */
case VIDIOC_ENUMSTD :
{
struct v4l2_standard * e = arg ;
if ( e - > index < 0 )
return - EINVAL ;
if ( e - > index < dev - > ext_vv_data - > num_stds ) {
DEB_EE ( ( " VIDIOC_ENUMSTD: index:%d \n " , e - > index ) ) ;
v4l2_video_std_construct ( e , dev - > ext_vv_data - > stds [ e - > index ] . id , dev - > ext_vv_data - > stds [ e - > index ] . name ) ;
return 0 ;
}
return - EINVAL ;
}
case VIDIOC_S_STD :
{
v4l2_std_id * id = arg ;
int found = 0 ;
int i , err ;
DEB_EE ( ( " VIDIOC_S_STD \n " ) ) ;
if ( ( vv - > video_status & STATUS_CAPTURE ) = = STATUS_CAPTURE ) {
DEB_D ( ( " cannot change video standard while streaming capture is active \n " ) ) ;
return - EBUSY ;
}
if ( ( vv - > video_status & STATUS_OVERLAY ) ! = 0 ) {
vv - > ov_suspend = vv - > video_fh ;
err = saa7146_stop_preview ( vv - > video_fh ) ; /* side effect: video_status is now 0, video_fh is NULL */
if ( 0 ! = err ) {
DEB_D ( ( " suspending video failed. aborting \n " ) ) ;
return err ;
}
}
2006-02-07 11:49:14 +03:00
mutex_lock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < dev - > ext_vv_data - > num_stds ; i + + )
if ( * id & dev - > ext_vv_data - > stds [ i ] . id )
break ;
if ( i ! = dev - > ext_vv_data - > num_stds ) {
vv - > standard = & dev - > ext_vv_data - > stds [ i ] ;
if ( NULL ! = dev - > ext_vv_data - > std_callback )
dev - > ext_vv_data - > std_callback ( dev , vv - > standard ) ;
found = 1 ;
}
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
if ( vv - > ov_suspend ! = NULL ) {
saa7146_start_preview ( vv - > ov_suspend ) ;
vv - > ov_suspend = NULL ;
}
if ( 0 = = found ) {
DEB_EE ( ( " VIDIOC_S_STD: standard not found. \n " ) ) ;
return - EINVAL ;
}
DEB_EE ( ( " VIDIOC_S_STD: set to standard to '%s' \n " , vv - > standard - > name ) ) ;
return 0 ;
}
case VIDIOC_OVERLAY :
{
int on = * ( int * ) arg ;
int err = 0 ;
DEB_D ( ( " VIDIOC_OVERLAY on:%d \n " , on ) ) ;
if ( on ! = 0 ) {
err = saa7146_start_preview ( fh ) ;
} else {
err = saa7146_stop_preview ( fh ) ;
}
return err ;
}
case VIDIOC_REQBUFS : {
struct v4l2_requestbuffers * req = arg ;
DEB_D ( ( " VIDIOC_REQBUFS, type:%d \n " , req - > type ) ) ;
return videobuf_reqbufs ( q , req ) ;
}
case VIDIOC_QUERYBUF : {
struct v4l2_buffer * buf = arg ;
DEB_D ( ( " VIDIOC_QUERYBUF, type:%d, offset:%d \n " , buf - > type , buf - > m . offset ) ) ;
return videobuf_querybuf ( q , buf ) ;
}
case VIDIOC_QBUF : {
struct v4l2_buffer * buf = arg ;
int ret = 0 ;
ret = videobuf_qbuf ( q , buf ) ;
DEB_D ( ( " VIDIOC_QBUF: ret:%d, index:%d \n " , ret , buf - > index ) ) ;
return ret ;
}
case VIDIOC_DQBUF : {
struct v4l2_buffer * buf = arg ;
int ret = 0 ;
ret = videobuf_dqbuf ( q , buf , file - > f_flags & O_NONBLOCK ) ;
DEB_D ( ( " VIDIOC_DQBUF: ret:%d, index:%d \n " , ret , buf - > index ) ) ;
return ret ;
}
case VIDIOC_STREAMON : {
int * type = arg ;
DEB_D ( ( " VIDIOC_STREAMON, type:%d \n " , * type ) ) ;
err = video_begin ( fh ) ;
if ( 0 ! = err ) {
return err ;
}
err = videobuf_streamon ( q ) ;
return err ;
}
case VIDIOC_STREAMOFF : {
int * type = arg ;
DEB_D ( ( " VIDIOC_STREAMOFF, type:%d \n " , * type ) ) ;
/* ugly: we need to copy some checks from video_end(),
because videobuf_streamoff ( ) relies on the capture running .
check and fix this */
if ( ( vv - > video_status & STATUS_CAPTURE ) ! = STATUS_CAPTURE ) {
DEB_S ( ( " not capturing. \n " ) ) ;
return 0 ;
}
if ( vv - > video_fh ! = fh ) {
DEB_S ( ( " capturing, but in another open. \n " ) ) ;
return - EBUSY ;
}
err = videobuf_streamoff ( q ) ;
if ( 0 ! = err ) {
DEB_D ( ( " warning: videobuf_streamoff() failed. \n " ) ) ;
video_end ( fh , file ) ;
} else {
err = video_end ( fh , file ) ;
}
return err ;
}
2006-09-10 16:31:37 +04:00
# ifdef CONFIG_VIDEO_V4L1_COMPAT
2005-04-17 02:20:36 +04:00
case VIDIOCGMBUF :
{
struct video_mbuf * mbuf = arg ;
struct videobuf_queue * q ;
int i ;
/* fixme: number of capture buffers and sizes for v4l apps */
int gbuffers = 2 ;
int gbufsize = 768 * 576 * 4 ;
DEB_D ( ( " VIDIOCGMBUF \n " ) ) ;
q = & fh - > video_q ;
err = videobuf_mmap_setup ( q , gbuffers , gbufsize ,
V4L2_MEMORY_MMAP ) ;
2007-11-19 21:10:48 +03:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
return err ;
2007-10-05 23:26:27 +04:00
gbuffers = err ;
2005-04-17 02:20:36 +04:00
memset ( mbuf , 0 , sizeof ( * mbuf ) ) ;
mbuf - > frames = gbuffers ;
mbuf - > size = gbuffers * gbufsize ;
for ( i = 0 ; i < gbuffers ; i + + )
mbuf - > offsets [ i ] = i * gbufsize ;
return 0 ;
}
2006-09-10 16:31:37 +04:00
# endif
2005-04-17 02:20:36 +04:00
default :
return v4l_compat_translate_ioctl ( inode , file , cmd , arg ,
saa7146_video_do_ioctl ) ;
}
return 0 ;
}
/*********************************************************************************/
/* buffer handling functions */
static int buffer_activate ( struct saa7146_dev * dev ,
struct saa7146_buf * buf ,
struct saa7146_buf * next )
{
struct saa7146_vv * vv = dev - > vv_data ;
2007-11-07 02:02:36 +03:00
buf - > vb . state = VIDEOBUF_ACTIVE ;
2005-04-17 02:20:36 +04:00
saa7146_set_capture ( dev , buf , next ) ;
mod_timer ( & vv - > video_q . timeout , jiffies + BUFFER_TIMEOUT ) ;
return 0 ;
}
static int buffer_prepare ( struct videobuf_queue * q ,
struct videobuf_buffer * vb , enum v4l2_field field )
{
struct file * file = q - > priv_data ;
struct saa7146_fh * fh = file - > private_data ;
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
struct saa7146_buf * buf = ( struct saa7146_buf * ) vb ;
int size , err = 0 ;
DEB_CAP ( ( " vbuf:%p \n " , vb ) ) ;
/* sanity checks */
if ( fh - > video_fmt . width < 48 | |
fh - > video_fmt . height < 32 | |
fh - > video_fmt . width > vv - > standard - > h_max_out | |
fh - > video_fmt . height > vv - > standard - > v_max_out ) {
DEB_D ( ( " w (%d) / h (%d) out of bounds. \n " , fh - > video_fmt . width , fh - > video_fmt . height ) ) ;
return - EINVAL ;
}
size = fh - > video_fmt . sizeimage ;
if ( 0 ! = buf - > vb . baddr & & buf - > vb . bsize < size ) {
DEB_D ( ( " size mismatch. \n " ) ) ;
return - EINVAL ;
}
DEB_CAP ( ( " buffer_prepare [size=%dx%d,bytes=%d,fields=%s] \n " ,
fh - > video_fmt . width , fh - > video_fmt . height , size , v4l2_field_names [ fh - > video_fmt . field ] ) ) ;
if ( buf - > vb . width ! = fh - > video_fmt . width | |
buf - > vb . bytesperline ! = fh - > video_fmt . bytesperline | |
buf - > vb . height ! = fh - > video_fmt . height | |
buf - > vb . size ! = size | |
buf - > vb . field ! = field | |
buf - > vb . field ! = fh - > video_fmt . field | |
buf - > fmt ! = & fh - > video_fmt ) {
2006-03-10 18:29:15 +03:00
saa7146_dma_free ( dev , q , buf ) ;
2005-04-17 02:20:36 +04:00
}
2007-11-07 02:02:36 +03:00
if ( VIDEOBUF_NEEDS_INIT = = buf - > vb . state ) {
2005-04-17 02:20:36 +04:00
struct saa7146_format * sfmt ;
buf - > vb . bytesperline = fh - > video_fmt . bytesperline ;
buf - > vb . width = fh - > video_fmt . width ;
buf - > vb . height = fh - > video_fmt . height ;
buf - > vb . size = size ;
buf - > vb . field = field ;
buf - > fmt = & fh - > video_fmt ;
buf - > vb . field = fh - > video_fmt . field ;
sfmt = format_by_fourcc ( dev , buf - > fmt - > pixelformat ) ;
if ( 0 ! = IS_PLANAR ( sfmt - > trans ) ) {
saa7146_pgtable_free ( dev - > pci , & buf - > pt [ 0 ] ) ;
saa7146_pgtable_free ( dev - > pci , & buf - > pt [ 1 ] ) ;
saa7146_pgtable_free ( dev - > pci , & buf - > pt [ 2 ] ) ;
saa7146_pgtable_alloc ( dev - > pci , & buf - > pt [ 0 ] ) ;
saa7146_pgtable_alloc ( dev - > pci , & buf - > pt [ 1 ] ) ;
saa7146_pgtable_alloc ( dev - > pci , & buf - > pt [ 2 ] ) ;
} else {
saa7146_pgtable_free ( dev - > pci , & buf - > pt [ 0 ] ) ;
saa7146_pgtable_alloc ( dev - > pci , & buf - > pt [ 0 ] ) ;
}
2006-03-10 18:29:15 +03:00
err = videobuf_iolock ( q , & buf - > vb , & vv - > ov_fb ) ;
2005-04-17 02:20:36 +04:00
if ( err )
goto oops ;
err = saa7146_pgtable_build ( dev , buf ) ;
if ( err )
goto oops ;
}
2007-11-07 02:02:36 +03:00
buf - > vb . state = VIDEOBUF_PREPARED ;
2005-04-17 02:20:36 +04:00
buf - > activate = buffer_activate ;
return 0 ;
oops :
DEB_D ( ( " error out. \n " ) ) ;
2006-03-10 18:29:15 +03:00
saa7146_dma_free ( dev , q , buf ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
static int buffer_setup ( struct videobuf_queue * q , unsigned int * count , unsigned int * size )
{
struct file * file = q - > priv_data ;
struct saa7146_fh * fh = file - > private_data ;
if ( 0 = = * count | | * count > MAX_SAA7146_CAPTURE_BUFFERS )
* count = MAX_SAA7146_CAPTURE_BUFFERS ;
* size = fh - > video_fmt . sizeimage ;
/* check if we exceed the "max_memory" parameter */
if ( ( * count * * size ) > ( max_memory * 1048576 ) ) {
* count = ( max_memory * 1048576 ) / * size ;
}
DEB_CAP ( ( " %d buffers, %d bytes each. \n " , * count , * size ) ) ;
return 0 ;
}
static void buffer_queue ( struct videobuf_queue * q , struct videobuf_buffer * vb )
{
struct file * file = q - > priv_data ;
struct saa7146_fh * fh = file - > private_data ;
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
struct saa7146_buf * buf = ( struct saa7146_buf * ) vb ;
DEB_CAP ( ( " vbuf:%p \n " , vb ) ) ;
saa7146_buffer_queue ( fh - > dev , & vv - > video_q , buf ) ;
}
static void buffer_release ( struct videobuf_queue * q , struct videobuf_buffer * vb )
{
struct file * file = q - > priv_data ;
struct saa7146_fh * fh = file - > private_data ;
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_buf * buf = ( struct saa7146_buf * ) vb ;
DEB_CAP ( ( " vbuf:%p \n " , vb ) ) ;
2006-03-10 18:29:15 +03:00
saa7146_dma_free ( dev , q , buf ) ;
2005-04-17 02:20:36 +04:00
}
static struct videobuf_queue_ops video_qops = {
. buf_setup = buffer_setup ,
. buf_prepare = buffer_prepare ,
. buf_queue = buffer_queue ,
. buf_release = buffer_release ,
} ;
/********************************************************************************/
/* file operations */
static void video_init ( struct saa7146_dev * dev , struct saa7146_vv * vv )
{
2005-12-12 11:37:27 +03:00
INIT_LIST_HEAD ( & vv - > video_q . queue ) ;
2005-04-17 02:20:36 +04:00
init_timer ( & vv - > video_q . timeout ) ;
vv - > video_q . timeout . function = saa7146_buffer_timeout ;
vv - > video_q . timeout . data = ( unsigned long ) ( & vv - > video_q ) ;
vv - > video_q . dev = dev ;
/* set some default values */
vv - > standard = & dev - > ext_vv_data - > stds [ 0 ] ;
/* FIXME: what's this? */
vv - > current_hps_source = SAA7146_HPS_SOURCE_PORT_A ;
vv - > current_hps_sync = SAA7146_HPS_SYNC_PORT_A ;
}
static int video_open ( struct saa7146_dev * dev , struct file * file )
{
struct saa7146_fh * fh = ( struct saa7146_fh * ) file - > private_data ;
struct saa7146_format * sfmt ;
fh - > video_fmt . width = 384 ;
fh - > video_fmt . height = 288 ;
fh - > video_fmt . pixelformat = V4L2_PIX_FMT_BGR24 ;
fh - > video_fmt . bytesperline = 0 ;
fh - > video_fmt . field = V4L2_FIELD_ANY ;
sfmt = format_by_fourcc ( dev , fh - > video_fmt . pixelformat ) ;
fh - > video_fmt . sizeimage = ( fh - > video_fmt . width * fh - > video_fmt . height * sfmt - > depth ) / 8 ;
2007-08-23 23:37:49 +04:00
videobuf_queue_pci_init ( & fh - > video_q , & video_qops ,
2005-04-17 02:20:36 +04:00
dev - > pci , & dev - > slock ,
V4L2_BUF_TYPE_VIDEO_CAPTURE ,
V4L2_FIELD_INTERLACED ,
sizeof ( struct saa7146_buf ) ,
file ) ;
return 0 ;
}
static void video_close ( struct saa7146_dev * dev , struct file * file )
{
struct saa7146_fh * fh = ( struct saa7146_fh * ) file - > private_data ;
struct saa7146_vv * vv = dev - > vv_data ;
2007-04-22 13:57:26 +04:00
struct videobuf_queue * q = & fh - > video_q ;
2005-04-17 02:20:36 +04:00
int err ;
if ( IS_CAPTURE_ACTIVE ( fh ) ! = 0 ) {
err = video_end ( fh , file ) ;
} else if ( IS_OVERLAY_ACTIVE ( fh ) ! = 0 ) {
err = saa7146_stop_preview ( fh ) ;
}
2007-11-14 02:05:38 +03:00
videobuf_stop ( q ) ;
2007-04-22 13:57:26 +04:00
2005-04-17 02:20:36 +04:00
/* hmm, why is this function declared void? */
/* return err */
}
static void video_irq_done ( struct saa7146_dev * dev , unsigned long st )
{
struct saa7146_vv * vv = dev - > vv_data ;
struct saa7146_dmaqueue * q = & vv - > video_q ;
spin_lock ( & dev - > slock ) ;
DEB_CAP ( ( " called. \n " ) ) ;
/* only finish the buffer if we have one... */
if ( NULL ! = q - > curr ) {
2007-11-07 02:02:36 +03:00
saa7146_buffer_finish ( dev , q , VIDEOBUF_DONE ) ;
2005-04-17 02:20:36 +04:00
}
saa7146_buffer_next ( dev , q , 0 ) ;
spin_unlock ( & dev - > slock ) ;
}
static ssize_t video_read ( struct file * file , char __user * data , size_t count , loff_t * ppos )
{
struct saa7146_fh * fh = file - > private_data ;
struct saa7146_dev * dev = fh - > dev ;
struct saa7146_vv * vv = dev - > vv_data ;
ssize_t ret = 0 ;
DEB_EE ( ( " called. \n " ) ) ;
if ( ( vv - > video_status & STATUS_CAPTURE ) ! = 0 ) {
/* fixme: should we allow read() captures while streaming capture? */
if ( vv - > video_fh = = fh ) {
DEB_S ( ( " already capturing. \n " ) ) ;
return - EBUSY ;
}
DEB_S ( ( " already capturing in another open. \n " ) ) ;
return - EBUSY ;
}
ret = video_begin ( fh ) ;
if ( 0 ! = ret ) {
goto out ;
}
ret = videobuf_read_one ( & fh - > video_q , data , count , ppos ,
file - > f_flags & O_NONBLOCK ) ;
if ( ret ! = 0 ) {
video_end ( fh , file ) ;
} else {
ret = video_end ( fh , file ) ;
}
out :
/* restart overlay if it was active before */
if ( vv - > ov_suspend ! = NULL ) {
saa7146_start_preview ( vv - > ov_suspend ) ;
vv - > ov_suspend = NULL ;
}
return ret ;
}
struct saa7146_use_ops saa7146_video_uops = {
. init = video_init ,
. open = video_open ,
. release = video_close ,
. irq_done = video_irq_done ,
. read = video_read ,
} ;