2013-06-17 17:29:06 +04:00
/*
2014-08-12 01:42:22 +04:00
* Fushicai USBTV007 Audio - Video Grabber Driver
2013-06-17 17:29:06 +04:00
*
* Product web site :
* http : //www.fushicai.com/products_detail/&productId=d05449ee-b690-42f9-a661-aa7353894bed.html
*
* Following LWN articles were very useful in construction of this driver :
* Video4Linux2 API series : http : //lwn.net/Articles/203924/
* videobuf2 API explanation : http : //lwn.net/Articles/447435/
* Thanks go to Jonathan Corbet for providing this quality documentation .
* He is awesome .
*
* Copyright ( c ) 2013 Lubomir Rintel
* All rights reserved .
* No physical hardware was harmed running Windows during the
* reverse - engineering activity
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification .
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) .
*/
# include <media/v4l2-ioctl.h>
2015-09-22 16:30:29 +03:00
# include <media/videobuf2-v4l2.h>
2014-01-08 02:13:21 +04:00
# include "usbtv.h"
2013-10-21 19:01:36 +04:00
static struct usbtv_norm_params norm_params [ ] = {
{
. norm = V4L2_STD_525_60 ,
. cap_width = 720 ,
. cap_height = 480 ,
} ,
{
. norm = V4L2_STD_PAL ,
. cap_width = 720 ,
. cap_height = 576 ,
}
} ;
static int usbtv_configure_for_norm ( struct usbtv * usbtv , v4l2_std_id norm )
{
int i , ret = 0 ;
struct usbtv_norm_params * params = NULL ;
for ( i = 0 ; i < ARRAY_SIZE ( norm_params ) ; i + + ) {
if ( norm_params [ i ] . norm & norm ) {
params = & norm_params [ i ] ;
break ;
}
}
if ( params ) {
usbtv - > width = params - > cap_width ;
usbtv - > height = params - > cap_height ;
usbtv - > n_chunks = usbtv - > width * usbtv - > height
/ 4 / USBTV_CHUNK ;
usbtv - > norm = params - > norm ;
} else
ret = - EINVAL ;
return ret ;
}
2013-07-12 12:03:03 +04:00
static int usbtv_select_input ( struct usbtv * usbtv , int input )
{
int ret ;
static const u16 composite [ ] [ 2 ] = {
{ USBTV_BASE + 0x0105 , 0x0060 } ,
{ USBTV_BASE + 0x011f , 0x00f2 } ,
{ USBTV_BASE + 0x0127 , 0x0060 } ,
{ USBTV_BASE + 0x00ae , 0x0010 } ,
{ USBTV_BASE + 0x0239 , 0x0060 } ,
} ;
static const u16 svideo [ ] [ 2 ] = {
{ USBTV_BASE + 0x0105 , 0x0010 } ,
{ USBTV_BASE + 0x011f , 0x00ff } ,
{ USBTV_BASE + 0x0127 , 0x0060 } ,
{ USBTV_BASE + 0x00ae , 0x0030 } ,
{ USBTV_BASE + 0x0239 , 0x0060 } ,
} ;
switch ( input ) {
case USBTV_COMPOSITE_INPUT :
ret = usbtv_set_regs ( usbtv , composite , ARRAY_SIZE ( composite ) ) ;
break ;
case USBTV_SVIDEO_INPUT :
ret = usbtv_set_regs ( usbtv , svideo , ARRAY_SIZE ( svideo ) ) ;
break ;
default :
ret = - EINVAL ;
}
if ( ! ret )
usbtv - > input = input ;
return ret ;
}
2013-10-21 19:01:36 +04:00
static int usbtv_select_norm ( struct usbtv * usbtv , v4l2_std_id norm )
{
int ret ;
static const u16 pal [ ] [ 2 ] = {
{ USBTV_BASE + 0x001a , 0x0068 } ,
{ USBTV_BASE + 0x010e , 0x0072 } ,
{ USBTV_BASE + 0x010f , 0x00a2 } ,
{ USBTV_BASE + 0x0112 , 0x00b0 } ,
{ USBTV_BASE + 0x0117 , 0x0001 } ,
{ USBTV_BASE + 0x0118 , 0x002c } ,
{ USBTV_BASE + 0x012d , 0x0010 } ,
{ USBTV_BASE + 0x012f , 0x0020 } ,
{ USBTV_BASE + 0x024f , 0x0002 } ,
{ USBTV_BASE + 0x0254 , 0x0059 } ,
{ USBTV_BASE + 0x025a , 0x0016 } ,
{ USBTV_BASE + 0x025b , 0x0035 } ,
{ USBTV_BASE + 0x0263 , 0x0017 } ,
{ USBTV_BASE + 0x0266 , 0x0016 } ,
{ USBTV_BASE + 0x0267 , 0x0036 }
} ;
static const u16 ntsc [ ] [ 2 ] = {
{ USBTV_BASE + 0x001a , 0x0079 } ,
{ USBTV_BASE + 0x010e , 0x0068 } ,
{ USBTV_BASE + 0x010f , 0x009c } ,
{ USBTV_BASE + 0x0112 , 0x00f0 } ,
{ USBTV_BASE + 0x0117 , 0x0000 } ,
{ USBTV_BASE + 0x0118 , 0x00fc } ,
{ USBTV_BASE + 0x012d , 0x0004 } ,
{ USBTV_BASE + 0x012f , 0x0008 } ,
{ USBTV_BASE + 0x024f , 0x0001 } ,
{ USBTV_BASE + 0x0254 , 0x005f } ,
{ USBTV_BASE + 0x025a , 0x0012 } ,
{ USBTV_BASE + 0x025b , 0x0001 } ,
{ USBTV_BASE + 0x0263 , 0x001c } ,
{ USBTV_BASE + 0x0266 , 0x0011 } ,
{ USBTV_BASE + 0x0267 , 0x0005 }
} ;
ret = usbtv_configure_for_norm ( usbtv , norm ) ;
if ( ! ret ) {
if ( norm & V4L2_STD_525_60 )
ret = usbtv_set_regs ( usbtv , ntsc , ARRAY_SIZE ( ntsc ) ) ;
else if ( norm & V4L2_STD_PAL )
ret = usbtv_set_regs ( usbtv , pal , ARRAY_SIZE ( pal ) ) ;
}
return ret ;
}
2013-07-12 12:03:03 +04:00
static int usbtv_setup_capture ( struct usbtv * usbtv )
{
int ret ;
static const u16 setup [ ] [ 2 ] = {
2013-06-17 17:29:06 +04:00
/* These seem to enable the device. */
{ USBTV_BASE + 0x0008 , 0x0001 } ,
{ USBTV_BASE + 0x01d0 , 0x00ff } ,
{ USBTV_BASE + 0x01d9 , 0x0002 } ,
/* These seem to influence color parameters, such as
* brightness , etc . */
{ USBTV_BASE + 0x0239 , 0x0040 } ,
{ USBTV_BASE + 0x0240 , 0x0000 } ,
{ USBTV_BASE + 0x0241 , 0x0000 } ,
{ USBTV_BASE + 0x0242 , 0x0002 } ,
{ USBTV_BASE + 0x0243 , 0x0080 } ,
{ USBTV_BASE + 0x0244 , 0x0012 } ,
{ USBTV_BASE + 0x0245 , 0x0090 } ,
{ USBTV_BASE + 0x0246 , 0x0000 } ,
{ USBTV_BASE + 0x0278 , 0x002d } ,
{ USBTV_BASE + 0x0279 , 0x000a } ,
{ USBTV_BASE + 0x027a , 0x0032 } ,
{ 0xf890 , 0x000c } ,
{ 0xf894 , 0x0086 } ,
{ USBTV_BASE + 0x00ac , 0x00c0 } ,
{ USBTV_BASE + 0x00ad , 0x0000 } ,
{ USBTV_BASE + 0x00a2 , 0x0012 } ,
{ USBTV_BASE + 0x00a3 , 0x00e0 } ,
{ USBTV_BASE + 0x00a4 , 0x0028 } ,
{ USBTV_BASE + 0x00a5 , 0x0082 } ,
{ USBTV_BASE + 0x00a7 , 0x0080 } ,
{ USBTV_BASE + 0x0000 , 0x0014 } ,
{ USBTV_BASE + 0x0006 , 0x0003 } ,
{ USBTV_BASE + 0x0090 , 0x0099 } ,
{ USBTV_BASE + 0x0091 , 0x0090 } ,
{ USBTV_BASE + 0x0094 , 0x0068 } ,
{ USBTV_BASE + 0x0095 , 0x0070 } ,
{ USBTV_BASE + 0x009c , 0x0030 } ,
{ USBTV_BASE + 0x009d , 0x00c0 } ,
{ USBTV_BASE + 0x009e , 0x00e0 } ,
{ USBTV_BASE + 0x0019 , 0x0006 } ,
{ USBTV_BASE + 0x008c , 0x00ba } ,
{ USBTV_BASE + 0x0101 , 0x00ff } ,
{ USBTV_BASE + 0x010c , 0x00b3 } ,
{ USBTV_BASE + 0x01b2 , 0x0080 } ,
{ USBTV_BASE + 0x01b4 , 0x00a0 } ,
{ USBTV_BASE + 0x014c , 0x00ff } ,
{ USBTV_BASE + 0x014d , 0x00ca } ,
{ USBTV_BASE + 0x0113 , 0x0053 } ,
{ USBTV_BASE + 0x0119 , 0x008a } ,
{ USBTV_BASE + 0x013c , 0x0003 } ,
{ USBTV_BASE + 0x0150 , 0x009c } ,
{ USBTV_BASE + 0x0151 , 0x0071 } ,
{ USBTV_BASE + 0x0152 , 0x00c6 } ,
{ USBTV_BASE + 0x0153 , 0x0084 } ,
{ USBTV_BASE + 0x0154 , 0x00bc } ,
{ USBTV_BASE + 0x0155 , 0x00a0 } ,
{ USBTV_BASE + 0x0156 , 0x00a0 } ,
{ USBTV_BASE + 0x0157 , 0x009c } ,
{ USBTV_BASE + 0x0158 , 0x001f } ,
{ USBTV_BASE + 0x0159 , 0x0006 } ,
{ USBTV_BASE + 0x015d , 0x0000 } ,
{ USBTV_BASE + 0x0003 , 0x0004 } ,
{ USBTV_BASE + 0x0100 , 0x00d3 } ,
{ USBTV_BASE + 0x0115 , 0x0015 } ,
{ USBTV_BASE + 0x0220 , 0x002e } ,
{ USBTV_BASE + 0x0225 , 0x0008 } ,
{ USBTV_BASE + 0x024e , 0x0002 } ,
{ USBTV_BASE + 0x024e , 0x0002 } ,
{ USBTV_BASE + 0x024f , 0x0002 } ,
} ;
2013-07-12 12:03:03 +04:00
ret = usbtv_set_regs ( usbtv , setup , ARRAY_SIZE ( setup ) ) ;
if ( ret )
return ret ;
2013-06-17 17:29:06 +04:00
2013-10-21 19:01:36 +04:00
ret = usbtv_select_norm ( usbtv , usbtv - > norm ) ;
if ( ret )
return ret ;
2013-07-12 12:03:03 +04:00
ret = usbtv_select_input ( usbtv , usbtv - > input ) ;
if ( ret )
return ret ;
2013-06-17 17:29:06 +04:00
return 0 ;
}
2013-07-02 14:56:38 +04:00
/* Copy data from chunk into a frame buffer, deinterlacing the data
* into every second line . Unfortunately , they don ' t align nicely into
* 720 pixel lines , as the chunk is 240 words long , which is 480 pixels .
* Therefore , we break down the chunk into two halves before copyting ,
* so that we can interleave a line if needed . */
2014-08-21 01:03:53 +04:00
static void usbtv_chunk_to_vbuf ( u32 * frame , __be32 * src , int chunk_no , int odd )
2013-07-02 14:56:38 +04:00
{
int half ;
for ( half = 0 ; half < 2 ; half + + ) {
int part_no = chunk_no * 2 + half ;
int line = part_no / 3 ;
int part_index = ( line * 2 + ! odd ) * 3 + ( part_no % 3 ) ;
u32 * dst = & frame [ part_index * USBTV_CHUNK / 2 ] ;
2014-09-20 08:03:15 +04:00
2013-07-02 14:56:38 +04:00
memcpy ( dst , src , USBTV_CHUNK / 2 * sizeof ( * src ) ) ;
src + = USBTV_CHUNK / 2 ;
}
}
2013-06-17 17:29:06 +04:00
/* Called for each 256-byte image chunk.
* First word identifies the chunk , followed by 240 words of image
* data and padding . */
2014-08-21 01:03:53 +04:00
static void usbtv_image_chunk ( struct usbtv * usbtv , __be32 * chunk )
2013-06-17 17:29:06 +04:00
{
int frame_id , odd , chunk_no ;
u32 * frame ;
struct usbtv_buf * buf ;
unsigned long flags ;
/* Ignore corrupted lines. */
if ( ! USBTV_MAGIC_OK ( chunk ) )
return ;
frame_id = USBTV_FRAME_ID ( chunk ) ;
odd = USBTV_ODD ( chunk ) ;
chunk_no = USBTV_CHUNK_NO ( chunk ) ;
2013-10-21 19:01:36 +04:00
if ( chunk_no > = usbtv - > n_chunks )
2013-06-17 17:29:06 +04:00
return ;
/* Beginning of a frame. */
2013-07-02 14:56:39 +04:00
if ( chunk_no = = 0 ) {
2013-06-17 17:29:06 +04:00
usbtv - > frame_id = frame_id ;
2013-07-02 14:56:39 +04:00
usbtv - > chunks_done = 0 ;
}
if ( usbtv - > frame_id ! = frame_id )
return ;
2013-06-17 17:29:06 +04:00
spin_lock_irqsave ( & usbtv - > buflock , flags ) ;
if ( list_empty ( & usbtv - > bufs ) ) {
/* No free buffers. Userspace likely too slow. */
spin_unlock_irqrestore ( & usbtv - > buflock , flags ) ;
return ;
}
/* First available buffer. */
buf = list_first_entry ( & usbtv - > bufs , struct usbtv_buf , list ) ;
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 16:30:30 +03:00
frame = vb2_plane_vaddr ( & buf - > vb . vb2_buf , 0 ) ;
2013-06-17 17:29:06 +04:00
2013-07-02 14:56:38 +04:00
/* Copy the chunk data. */
usbtv_chunk_to_vbuf ( frame , & chunk [ 1 ] , chunk_no , odd ) ;
2013-07-02 14:56:39 +04:00
usbtv - > chunks_done + + ;
2013-06-17 17:29:06 +04:00
2015-12-20 14:57:17 +03:00
/* Last chunk in a field */
if ( chunk_no = = usbtv - > n_chunks - 1 ) {
/* Last chunk in a frame, signalling an end */
if ( odd & & ! usbtv - > last_odd ) {
int size = vb2_plane_size ( & buf - > vb . vb2_buf , 0 ) ;
enum vb2_buffer_state state = usbtv - > chunks_done = =
usbtv - > n_chunks ?
VB2_BUF_STATE_DONE :
VB2_BUF_STATE_ERROR ;
buf - > vb . field = V4L2_FIELD_INTERLACED ;
buf - > vb . sequence = usbtv - > sequence + + ;
buf - > vb . vb2_buf . timestamp = ktime_get_ns ( ) ;
vb2_set_plane_payload ( & buf - > vb . vb2_buf , 0 , size ) ;
vb2_buffer_done ( & buf - > vb . vb2_buf , state ) ;
list_del ( & buf - > list ) ;
}
usbtv - > last_odd = odd ;
2013-06-17 17:29:06 +04:00
}
spin_unlock_irqrestore ( & usbtv - > buflock , flags ) ;
}
/* Got image data. Each packet contains a number of 256-word chunks we
* compose the image from . */
static void usbtv_iso_cb ( struct urb * ip )
{
int ret ;
int i ;
struct usbtv * usbtv = ( struct usbtv * ) ip - > context ;
switch ( ip - > status ) {
/* All fine. */
case 0 :
break ;
/* Device disconnected or capture stopped? */
case - ENODEV :
case - ENOENT :
case - ECONNRESET :
case - ESHUTDOWN :
return ;
/* Unknown error. Retry. */
default :
dev_warn ( usbtv - > dev , " Bad response for ISO request. \n " ) ;
goto resubmit ;
}
for ( i = 0 ; i < ip - > number_of_packets ; i + + ) {
int size = ip - > iso_frame_desc [ i ] . actual_length ;
unsigned char * data = ip - > transfer_buffer +
ip - > iso_frame_desc [ i ] . offset ;
int offset ;
for ( offset = 0 ; USBTV_CHUNK_SIZE * offset < size ; offset + + )
usbtv_image_chunk ( usbtv ,
2014-08-21 01:03:53 +04:00
( __be32 * ) & data [ USBTV_CHUNK_SIZE * offset ] ) ;
2013-06-17 17:29:06 +04:00
}
resubmit :
ret = usb_submit_urb ( ip , GFP_ATOMIC ) ;
if ( ret < 0 )
dev_warn ( usbtv - > dev , " Could not resubmit ISO URB \n " ) ;
}
static struct urb * usbtv_setup_iso_transfer ( struct usbtv * usbtv )
{
struct urb * ip ;
int size = usbtv - > iso_size ;
int i ;
ip = usb_alloc_urb ( USBTV_ISOC_PACKETS , GFP_KERNEL ) ;
if ( ip = = NULL )
return NULL ;
ip - > dev = usbtv - > udev ;
ip - > context = usbtv ;
ip - > pipe = usb_rcvisocpipe ( usbtv - > udev , USBTV_VIDEO_ENDP ) ;
ip - > interval = 1 ;
ip - > transfer_flags = URB_ISO_ASAP ;
ip - > transfer_buffer = kzalloc ( size * USBTV_ISOC_PACKETS ,
GFP_KERNEL ) ;
ip - > complete = usbtv_iso_cb ;
ip - > number_of_packets = USBTV_ISOC_PACKETS ;
ip - > transfer_buffer_length = size * USBTV_ISOC_PACKETS ;
for ( i = 0 ; i < USBTV_ISOC_PACKETS ; i + + ) {
ip - > iso_frame_desc [ i ] . offset = size * i ;
ip - > iso_frame_desc [ i ] . length = size ;
}
return ip ;
}
static void usbtv_stop ( struct usbtv * usbtv )
{
int i ;
unsigned long flags ;
/* Cancel running transfers. */
for ( i = 0 ; i < USBTV_ISOC_TRANSFERS ; i + + ) {
struct urb * ip = usbtv - > isoc_urbs [ i ] ;
2014-09-20 08:03:15 +04:00
2013-06-17 17:29:06 +04:00
if ( ip = = NULL )
continue ;
usb_kill_urb ( ip ) ;
kfree ( ip - > transfer_buffer ) ;
usb_free_urb ( ip ) ;
usbtv - > isoc_urbs [ i ] = NULL ;
}
/* Return buffers to userspace. */
spin_lock_irqsave ( & usbtv - > buflock , flags ) ;
while ( ! list_empty ( & usbtv - > bufs ) ) {
struct usbtv_buf * buf = list_first_entry ( & usbtv - > bufs ,
struct usbtv_buf , list ) ;
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 16:30:30 +03:00
vb2_buffer_done ( & buf - > vb . vb2_buf , VB2_BUF_STATE_ERROR ) ;
2013-06-17 17:29:06 +04:00
list_del ( & buf - > list ) ;
}
spin_unlock_irqrestore ( & usbtv - > buflock , flags ) ;
}
static int usbtv_start ( struct usbtv * usbtv )
{
int i ;
int ret ;
2014-08-12 01:42:22 +04:00
usbtv_audio_suspend ( usbtv ) ;
2013-06-17 17:29:06 +04:00
ret = usb_set_interface ( usbtv - > udev , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
ret = usbtv_setup_capture ( usbtv ) ;
if ( ret < 0 )
return ret ;
ret = usb_set_interface ( usbtv - > udev , 0 , 1 ) ;
if ( ret < 0 )
return ret ;
2014-08-12 01:42:22 +04:00
usbtv_audio_resume ( usbtv ) ;
2013-06-17 17:29:06 +04:00
for ( i = 0 ; i < USBTV_ISOC_TRANSFERS ; i + + ) {
struct urb * ip ;
ip = usbtv_setup_iso_transfer ( usbtv ) ;
if ( ip = = NULL ) {
ret = - ENOMEM ;
goto start_fail ;
}
usbtv - > isoc_urbs [ i ] = ip ;
ret = usb_submit_urb ( ip , GFP_KERNEL ) ;
if ( ret < 0 )
goto start_fail ;
}
return 0 ;
start_fail :
usbtv_stop ( usbtv ) ;
return ret ;
}
static int usbtv_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
struct usbtv * dev = video_drvdata ( file ) ;
strlcpy ( cap - > driver , " usbtv " , sizeof ( cap - > driver ) ) ;
strlcpy ( cap - > card , " usbtv " , sizeof ( cap - > card ) ) ;
usb_make_path ( dev - > udev , cap - > bus_info , sizeof ( cap - > bus_info ) ) ;
cap - > device_caps = V4L2_CAP_VIDEO_CAPTURE ;
cap - > device_caps | = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING ;
cap - > capabilities = cap - > device_caps | V4L2_CAP_DEVICE_CAPS ;
return 0 ;
}
static int usbtv_enum_input ( struct file * file , void * priv ,
struct v4l2_input * i )
{
2013-10-21 19:01:36 +04:00
struct usbtv * dev = video_drvdata ( file ) ;
2013-07-12 12:03:03 +04:00
switch ( i - > index ) {
case USBTV_COMPOSITE_INPUT :
strlcpy ( i - > name , " Composite " , sizeof ( i - > name ) ) ;
break ;
case USBTV_SVIDEO_INPUT :
strlcpy ( i - > name , " S-Video " , sizeof ( i - > name ) ) ;
break ;
default :
2013-06-17 17:29:06 +04:00
return - EINVAL ;
2013-07-12 12:03:03 +04:00
}
2013-06-17 17:29:06 +04:00
i - > type = V4L2_INPUT_TYPE_CAMERA ;
2013-10-21 19:01:36 +04:00
i - > std = dev - > vdev . tvnorms ;
2013-06-17 17:29:06 +04:00
return 0 ;
}
static int usbtv_enum_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_fmtdesc * f )
{
if ( f - > index > 0 )
return - EINVAL ;
strlcpy ( f - > description , " 16 bpp YUY2, 4:2:2, packed " ,
sizeof ( f - > description ) ) ;
f - > pixelformat = V4L2_PIX_FMT_YUYV ;
return 0 ;
}
static int usbtv_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
{
2013-10-21 19:01:36 +04:00
struct usbtv * usbtv = video_drvdata ( file ) ;
f - > fmt . pix . width = usbtv - > width ;
f - > fmt . pix . height = usbtv - > height ;
2013-06-17 17:29:06 +04:00
f - > fmt . pix . pixelformat = V4L2_PIX_FMT_YUYV ;
f - > fmt . pix . field = V4L2_FIELD_INTERLACED ;
2013-10-21 19:01:36 +04:00
f - > fmt . pix . bytesperline = usbtv - > width * 2 ;
2013-06-17 17:29:06 +04:00
f - > fmt . pix . sizeimage = ( f - > fmt . pix . bytesperline * f - > fmt . pix . height ) ;
f - > fmt . pix . colorspace = V4L2_COLORSPACE_SMPTE170M ;
2013-10-21 19:01:36 +04:00
2013-06-17 17:29:06 +04:00
return 0 ;
}
static int usbtv_g_std ( struct file * file , void * priv , v4l2_std_id * norm )
{
2013-10-21 19:01:36 +04:00
struct usbtv * usbtv = video_drvdata ( file ) ;
* norm = usbtv - > norm ;
2013-06-17 17:29:06 +04:00
return 0 ;
}
2013-10-21 19:01:36 +04:00
static int usbtv_s_std ( struct file * file , void * priv , v4l2_std_id norm )
{
int ret = - EINVAL ;
struct usbtv * usbtv = video_drvdata ( file ) ;
if ( ( norm & V4L2_STD_525_60 ) | | ( norm & V4L2_STD_PAL ) )
ret = usbtv_select_norm ( usbtv , norm ) ;
return ret ;
}
2013-06-17 17:29:06 +04:00
static int usbtv_g_input ( struct file * file , void * priv , unsigned int * i )
{
2013-07-12 12:03:03 +04:00
struct usbtv * usbtv = video_drvdata ( file ) ;
* i = usbtv - > input ;
2013-06-17 17:29:06 +04:00
return 0 ;
}
static int usbtv_s_input ( struct file * file , void * priv , unsigned int i )
{
2013-07-12 12:03:03 +04:00
struct usbtv * usbtv = video_drvdata ( file ) ;
2014-09-20 08:03:15 +04:00
2013-07-12 12:03:03 +04:00
return usbtv_select_input ( usbtv , i ) ;
2013-06-17 17:29:06 +04:00
}
2014-02-04 13:02:02 +04:00
static struct v4l2_ioctl_ops usbtv_ioctl_ops = {
2013-06-17 17:29:06 +04:00
. vidioc_querycap = usbtv_querycap ,
. vidioc_enum_input = usbtv_enum_input ,
. vidioc_enum_fmt_vid_cap = usbtv_enum_fmt_vid_cap ,
. vidioc_g_fmt_vid_cap = usbtv_fmt_vid_cap ,
. vidioc_try_fmt_vid_cap = usbtv_fmt_vid_cap ,
. vidioc_s_fmt_vid_cap = usbtv_fmt_vid_cap ,
. vidioc_g_std = usbtv_g_std ,
. vidioc_s_std = usbtv_s_std ,
. vidioc_g_input = usbtv_g_input ,
. vidioc_s_input = usbtv_s_input ,
. vidioc_reqbufs = vb2_ioctl_reqbufs ,
. vidioc_prepare_buf = vb2_ioctl_prepare_buf ,
. vidioc_querybuf = vb2_ioctl_querybuf ,
. vidioc_create_bufs = vb2_ioctl_create_bufs ,
. vidioc_qbuf = vb2_ioctl_qbuf ,
. vidioc_dqbuf = vb2_ioctl_dqbuf ,
. vidioc_streamon = vb2_ioctl_streamon ,
. vidioc_streamoff = vb2_ioctl_streamoff ,
} ;
2014-02-04 13:02:02 +04:00
static struct v4l2_file_operations usbtv_fops = {
2013-06-17 17:29:06 +04:00
. owner = THIS_MODULE ,
. unlocked_ioctl = video_ioctl2 ,
. mmap = vb2_fop_mmap ,
. open = v4l2_fh_open ,
. release = vb2_fop_release ,
. read = vb2_fop_read ,
. poll = vb2_fop_poll ,
} ;
static int usbtv_queue_setup ( struct vb2_queue * vq ,
2015-10-28 05:50:37 +03:00
unsigned int * nbuffers ,
2013-06-17 17:29:06 +04:00
unsigned int * nplanes , unsigned int sizes [ ] , void * alloc_ctxs [ ] )
{
2013-10-21 19:01:36 +04:00
struct usbtv * usbtv = vb2_get_drv_priv ( vq ) ;
2015-04-24 10:26:01 +03:00
unsigned size = USBTV_CHUNK * usbtv - > n_chunks * 2 * sizeof ( u32 ) ;
2013-10-21 19:01:36 +04:00
2015-04-24 10:26:01 +03:00
if ( vq - > num_buffers + * nbuffers < 2 )
* nbuffers = 2 - vq - > num_buffers ;
2015-10-28 05:50:37 +03:00
if ( * nplanes )
return sizes [ 0 ] < size ? - EINVAL : 0 ;
2013-06-17 17:29:06 +04:00
* nplanes = 1 ;
2015-10-28 05:50:37 +03:00
sizes [ 0 ] = size ;
2013-06-17 17:29:06 +04:00
return 0 ;
}
static void usbtv_buf_queue ( struct vb2_buffer * vb )
{
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 16:30:30 +03:00
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
2013-06-17 17:29:06 +04:00
struct usbtv * usbtv = vb2_get_drv_priv ( vb - > vb2_queue ) ;
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 16:30:30 +03:00
struct usbtv_buf * buf = container_of ( vbuf , struct usbtv_buf , vb ) ;
2013-06-17 17:29:06 +04:00
unsigned long flags ;
if ( usbtv - > udev = = NULL ) {
vb2_buffer_done ( vb , VB2_BUF_STATE_ERROR ) ;
return ;
}
spin_lock_irqsave ( & usbtv - > buflock , flags ) ;
list_add_tail ( & buf - > list , & usbtv - > bufs ) ;
spin_unlock_irqrestore ( & usbtv - > buflock , flags ) ;
}
static int usbtv_start_streaming ( struct vb2_queue * vq , unsigned int count )
{
struct usbtv * usbtv = vb2_get_drv_priv ( vq ) ;
if ( usbtv - > udev = = NULL )
return - ENODEV ;
2015-12-20 14:57:17 +03:00
usbtv - > last_odd = 1 ;
2015-04-24 10:26:01 +03:00
usbtv - > sequence = 0 ;
2013-06-17 17:29:06 +04:00
return usbtv_start ( usbtv ) ;
}
2014-04-17 09:47:21 +04:00
static void usbtv_stop_streaming ( struct vb2_queue * vq )
2013-06-17 17:29:06 +04:00
{
struct usbtv * usbtv = vb2_get_drv_priv ( vq ) ;
2014-04-17 09:47:21 +04:00
if ( usbtv - > udev )
usbtv_stop ( usbtv ) ;
2013-06-17 17:29:06 +04:00
}
2014-02-04 13:02:02 +04:00
static struct vb2_ops usbtv_vb2_ops = {
2013-06-17 17:29:06 +04:00
. queue_setup = usbtv_queue_setup ,
. buf_queue = usbtv_buf_queue ,
. start_streaming = usbtv_start_streaming ,
. stop_streaming = usbtv_stop_streaming ,
} ;
static void usbtv_release ( struct v4l2_device * v4l2_dev )
{
struct usbtv * usbtv = container_of ( v4l2_dev , struct usbtv , v4l2_dev ) ;
v4l2_device_unregister ( & usbtv - > v4l2_dev ) ;
vb2_queue_release ( & usbtv - > vb2q ) ;
kfree ( usbtv ) ;
}
2014-01-08 02:13:21 +04:00
int usbtv_video_init ( struct usbtv * usbtv )
2013-06-17 17:29:06 +04:00
{
int ret ;
2013-10-21 19:01:36 +04:00
( void ) usbtv_configure_for_norm ( usbtv , V4L2_STD_525_60 ) ;
2013-06-17 17:29:06 +04:00
spin_lock_init ( & usbtv - > buflock ) ;
mutex_init ( & usbtv - > v4l2_lock ) ;
mutex_init ( & usbtv - > vb2q_lock ) ;
INIT_LIST_HEAD ( & usbtv - > bufs ) ;
/* videobuf2 structure */
usbtv - > vb2q . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
usbtv - > vb2q . io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ ;
usbtv - > vb2q . drv_priv = usbtv ;
usbtv - > vb2q . buf_struct_size = sizeof ( struct usbtv_buf ) ;
usbtv - > vb2q . ops = & usbtv_vb2_ops ;
usbtv - > vb2q . mem_ops = & vb2_vmalloc_memops ;
2014-02-26 02:12:19 +04:00
usbtv - > vb2q . timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC ;
2013-06-17 17:29:06 +04:00
usbtv - > vb2q . lock = & usbtv - > vb2q_lock ;
ret = vb2_queue_init ( & usbtv - > vb2q ) ;
if ( ret < 0 ) {
2014-01-08 02:13:21 +04:00
dev_warn ( usbtv - > dev , " Could not initialize videobuf2 queue \n " ) ;
return ret ;
2013-06-17 17:29:06 +04:00
}
/* v4l2 structure */
usbtv - > v4l2_dev . release = usbtv_release ;
2014-01-08 02:13:21 +04:00
ret = v4l2_device_register ( usbtv - > dev , & usbtv - > v4l2_dev ) ;
2013-06-17 17:29:06 +04:00
if ( ret < 0 ) {
2014-01-08 02:13:21 +04:00
dev_warn ( usbtv - > dev , " Could not register v4l2 device \n " ) ;
2013-06-17 17:29:06 +04:00
goto v4l2_fail ;
}
/* Video structure */
strlcpy ( usbtv - > vdev . name , " usbtv " , sizeof ( usbtv - > vdev . name ) ) ;
usbtv - > vdev . v4l2_dev = & usbtv - > v4l2_dev ;
usbtv - > vdev . release = video_device_release_empty ;
usbtv - > vdev . fops = & usbtv_fops ;
usbtv - > vdev . ioctl_ops = & usbtv_ioctl_ops ;
2013-10-21 19:01:36 +04:00
usbtv - > vdev . tvnorms = USBTV_TV_STD ;
2013-06-17 17:29:06 +04:00
usbtv - > vdev . queue = & usbtv - > vb2q ;
usbtv - > vdev . lock = & usbtv - > v4l2_lock ;
video_set_drvdata ( & usbtv - > vdev , usbtv ) ;
ret = video_register_device ( & usbtv - > vdev , VFL_TYPE_GRABBER , - 1 ) ;
if ( ret < 0 ) {
2014-01-08 02:13:21 +04:00
dev_warn ( usbtv - > dev , " Could not register video device \n " ) ;
2013-06-17 17:29:06 +04:00
goto vdev_fail ;
}
return 0 ;
vdev_fail :
v4l2_device_unregister ( & usbtv - > v4l2_dev ) ;
v4l2_fail :
vb2_queue_release ( & usbtv - > vb2q ) ;
return ret ;
}
2014-01-08 02:13:21 +04:00
void usbtv_video_free ( struct usbtv * usbtv )
2013-06-17 17:29:06 +04:00
{
mutex_lock ( & usbtv - > vb2q_lock ) ;
mutex_lock ( & usbtv - > v4l2_lock ) ;
usbtv_stop ( usbtv ) ;
video_unregister_device ( & usbtv - > vdev ) ;
v4l2_device_disconnect ( & usbtv - > v4l2_dev ) ;
mutex_unlock ( & usbtv - > v4l2_lock ) ;
mutex_unlock ( & usbtv - > vb2q_lock ) ;
v4l2_device_put ( & usbtv - > v4l2_dev ) ;
}