2008-04-28 20:24:33 -03:00
/*
* cx18 init / start / stop / exit stream functions
*
* Derived from ivtv - streams . c
*
* Copyright ( C ) 2007 Hans Verkuil < hverkuil @ xs4all . nl >
2010-05-23 18:53:35 -03:00
* Copyright ( C ) 2008 Andy Walls < awalls @ md . metrocast . net >
2008-04-28 20:24:33 -03: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 . , 59 Temple Place , Suite 330 , Boston , MA
* 02111 - 1307 USA
*/
# include "cx18-driver.h"
2008-08-30 16:03:44 -03:00
# include "cx18-io.h"
2008-04-28 20:24:33 -03:00
# include "cx18-fileops.h"
# include "cx18-mailbox.h"
# include "cx18-i2c.h"
# include "cx18-queue.h"
# include "cx18-ioctl.h"
# include "cx18-streams.h"
# include "cx18-cards.h"
# include "cx18-scb.h"
# include "cx18-dvb.h"
# define CX18_DSP0_INTERRUPT_MASK 0xd0004C
2008-12-30 06:58:20 -03:00
static struct v4l2_file_operations cx18_v4l2_enc_fops = {
2008-05-12 11:21:58 -03:00
. owner = THIS_MODULE ,
. read = cx18_v4l2_read ,
. open = cx18_v4l2_open ,
2008-06-21 08:36:31 -03:00
/* FIXME change to video_ioctl2 if serialization lock can be removed */
2010-11-19 17:04:31 -03:00
. unlocked_ioctl = cx18_v4l2_ioctl ,
2008-05-12 11:21:58 -03:00
. release = cx18_v4l2_close ,
. poll = cx18_v4l2_enc_poll ,
2008-04-28 20:24:33 -03:00
} ;
/* offset from 0 to register ts v4l2 minors on */
# define CX18_V4L2_ENC_TS_OFFSET 16
/* offset from 0 to register pcm v4l2 minors on */
# define CX18_V4L2_ENC_PCM_OFFSET 24
/* offset from 0 to register yuv v4l2 minors on */
# define CX18_V4L2_ENC_YUV_OFFSET 32
static struct {
const char * name ;
int vfl_type ;
2008-10-04 08:36:54 -03:00
int num_offset ;
2008-04-28 20:24:33 -03:00
int dma ;
enum v4l2_buf_type buf_type ;
} cx18_stream_info [ ] = {
{ /* CX18_ENC_STREAM_TYPE_MPG */
" encoder MPEG " ,
VFL_TYPE_GRABBER , 0 ,
PCI_DMA_FROMDEVICE , V4L2_BUF_TYPE_VIDEO_CAPTURE ,
} ,
{ /* CX18_ENC_STREAM_TYPE_TS */
" TS " ,
VFL_TYPE_GRABBER , - 1 ,
PCI_DMA_FROMDEVICE , V4L2_BUF_TYPE_VIDEO_CAPTURE ,
} ,
{ /* CX18_ENC_STREAM_TYPE_YUV */
" encoder YUV " ,
VFL_TYPE_GRABBER , CX18_V4L2_ENC_YUV_OFFSET ,
PCI_DMA_FROMDEVICE , V4L2_BUF_TYPE_VIDEO_CAPTURE ,
} ,
{ /* CX18_ENC_STREAM_TYPE_VBI */
" encoder VBI " ,
VFL_TYPE_VBI , 0 ,
PCI_DMA_FROMDEVICE , V4L2_BUF_TYPE_VBI_CAPTURE ,
} ,
{ /* CX18_ENC_STREAM_TYPE_PCM */
" encoder PCM audio " ,
VFL_TYPE_GRABBER , CX18_V4L2_ENC_PCM_OFFSET ,
PCI_DMA_FROMDEVICE , V4L2_BUF_TYPE_PRIVATE ,
} ,
{ /* CX18_ENC_STREAM_TYPE_IDX */
" encoder IDX " ,
VFL_TYPE_GRABBER , - 1 ,
PCI_DMA_FROMDEVICE , V4L2_BUF_TYPE_VIDEO_CAPTURE ,
} ,
{ /* CX18_ENC_STREAM_TYPE_RAD */
" encoder radio " ,
VFL_TYPE_RADIO , 0 ,
PCI_DMA_NONE , V4L2_BUF_TYPE_PRIVATE ,
} ,
} ;
static void cx18_stream_init ( struct cx18 * cx , int type )
{
struct cx18_stream * s = & cx - > streams [ type ] ;
2009-01-10 21:54:39 -03:00
struct video_device * video_dev = s - > video_dev ;
2008-04-28 20:24:33 -03:00
2009-01-10 21:54:39 -03:00
/* we need to keep video_dev, so restore it afterwards */
2008-04-28 20:24:33 -03:00
memset ( s , 0 , sizeof ( * s ) ) ;
2009-01-10 21:54:39 -03:00
s - > video_dev = video_dev ;
2008-04-28 20:24:33 -03:00
/* initialize cx18_stream fields */
2010-12-11 20:38:20 -03:00
s - > dvb = NULL ;
2008-04-28 20:24:33 -03:00
s - > cx = cx ;
s - > type = type ;
s - > name = cx18_stream_info [ type ] . name ;
2008-08-23 16:42:29 -03:00
s - > handle = CX18_INVALID_TASK_HANDLE ;
2008-04-28 20:24:33 -03:00
s - > dma = cx18_stream_info [ type ] . dma ;
2008-12-07 23:30:17 -03:00
s - > buffers = cx - > stream_buffers [ type ] ;
2008-04-28 20:24:33 -03:00
s - > buf_size = cx - > stream_buf_size [ type ] ;
2009-11-08 23:45:24 -03:00
INIT_LIST_HEAD ( & s - > buf_pool ) ;
s - > bufs_per_mdl = 1 ;
s - > mdl_size = s - > buf_size * s - > bufs_per_mdl ;
2008-12-07 23:30:17 -03:00
2008-04-28 20:24:33 -03:00
init_waitqueue_head ( & s - > waitq ) ;
s - > id = - 1 ;
2009-04-13 23:08:00 -03:00
spin_lock_init ( & s - > q_free . lock ) ;
2008-04-28 20:24:33 -03:00
cx18_queue_init ( & s - > q_free ) ;
2009-04-13 23:08:00 -03:00
spin_lock_init ( & s - > q_busy . lock ) ;
2008-12-08 23:02:45 -03:00
cx18_queue_init ( & s - > q_busy ) ;
2009-04-13 23:08:00 -03:00
spin_lock_init ( & s - > q_full . lock ) ;
2008-04-28 20:24:33 -03:00
cx18_queue_init ( & s - > q_full ) ;
2009-11-08 23:45:24 -03:00
spin_lock_init ( & s - > q_idle . lock ) ;
cx18_queue_init ( & s - > q_idle ) ;
2009-04-15 20:45:10 -03:00
INIT_WORK ( & s - > out_work_order , cx18_out_work_handler ) ;
2008-04-28 20:24:33 -03:00
}
static int cx18_prep_dev ( struct cx18 * cx , int type )
{
struct cx18_stream * s = & cx - > streams [ type ] ;
u32 cap = cx - > v4l2_cap ;
2008-10-04 08:36:54 -03:00
int num_offset = cx18_stream_info [ type ] . num_offset ;
2009-02-14 17:08:37 -03:00
int num = cx - > instance + cx18_first_minor + num_offset ;
2008-04-28 20:24:33 -03:00
2010-12-11 20:38:20 -03:00
/*
* These five fields are always initialized .
* For analog capture related streams , if video_dev = = NULL then the
* stream is not in use .
* For the TS stream , if dvb = = NULL then the stream is not in use .
* In those cases no other fields but these four can be used .
*/
2009-01-10 21:54:39 -03:00
s - > video_dev = NULL ;
2010-12-11 20:38:20 -03:00
s - > dvb = NULL ;
2008-04-28 20:24:33 -03:00
s - > cx = cx ;
s - > type = type ;
s - > name = cx18_stream_info [ type ] . name ;
/* Check whether the radio is supported */
if ( type = = CX18_ENC_STREAM_TYPE_RAD & & ! ( cap & V4L2_CAP_RADIO ) )
return 0 ;
/* Check whether VBI is supported */
if ( type = = CX18_ENC_STREAM_TYPE_VBI & &
! ( cap & ( V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE ) ) )
return 0 ;
/* User explicitly selected 0 buffers for these streams, so don't
create them . */
if ( cx18_stream_info [ type ] . dma ! = PCI_DMA_NONE & &
2008-12-07 23:30:17 -03:00
cx - > stream_buffers [ type ] = = 0 ) {
2008-04-28 20:24:33 -03:00
CX18_INFO ( " Disabled %s device \n " , cx18_stream_info [ type ] . name ) ;
return 0 ;
}
cx18_stream_init ( cx , type ) ;
2010-12-11 20:38:20 -03:00
/* Allocate the cx18_dvb struct only for the TS on cards with DTV */
if ( type = = CX18_ENC_STREAM_TYPE_TS ) {
if ( cx - > card - > hw_all & CX18_HW_DVB ) {
s - > dvb = kzalloc ( sizeof ( struct cx18_dvb ) , GFP_KERNEL ) ;
if ( s - > dvb = = NULL ) {
CX18_ERR ( " Couldn't allocate cx18_dvb structure "
" for %s \n " , s - > name ) ;
return - ENOMEM ;
}
} else {
/* Don't need buffers for the TS, if there is no DVB */
s - > buffers = 0 ;
}
}
2008-10-04 08:36:54 -03:00
if ( num_offset = = - 1 )
2008-04-28 20:24:33 -03:00
return 0 ;
/* allocate and initialize the v4l2 video device structure */
2009-01-10 21:54:39 -03:00
s - > video_dev = video_device_alloc ( ) ;
if ( s - > video_dev = = NULL ) {
2008-04-28 20:24:33 -03:00
CX18_ERR ( " Couldn't allocate v4l2 video_device for %s \n " ,
s - > name ) ;
return - ENOMEM ;
}
2009-02-14 17:08:37 -03:00
snprintf ( s - > video_dev - > name , sizeof ( s - > video_dev - > name ) , " %s %s " ,
cx - > v4l2_dev . name , s - > name ) ;
2008-04-28 20:24:33 -03:00
2009-01-10 21:54:39 -03:00
s - > video_dev - > num = num ;
2009-02-14 17:08:37 -03:00
s - > video_dev - > v4l2_dev = & cx - > v4l2_dev ;
2009-01-10 21:54:39 -03:00
s - > video_dev - > fops = & cx18_v4l2_enc_fops ;
s - > video_dev - > release = video_device_release ;
s - > video_dev - > tvnorms = V4L2_STD_ALL ;
2011-03-22 10:14:07 -03:00
set_bit ( V4L2_FL_USE_FH_PRIO , & s - > video_dev - > flags ) ;
2009-01-10 21:54:39 -03:00
cx18_set_funcs ( s - > video_dev ) ;
2008-04-28 20:24:33 -03:00
return 0 ;
}
/* Initialize v4l2 variables and register v4l2 devices */
int cx18_streams_setup ( struct cx18 * cx )
{
2008-10-18 10:20:25 -03:00
int type , ret ;
2008-04-28 20:24:33 -03:00
/* Setup V4L2 Devices */
for ( type = 0 ; type < CX18_MAX_STREAMS ; type + + ) {
/* Prepare device */
2008-10-18 10:20:25 -03:00
ret = cx18_prep_dev ( cx , type ) ;
if ( ret < 0 )
2008-04-28 20:24:33 -03:00
break ;
/* Allocate Stream */
2008-10-18 10:20:25 -03:00
ret = cx18_stream_alloc ( & cx - > streams [ type ] ) ;
if ( ret < 0 )
2008-04-28 20:24:33 -03:00
break ;
}
if ( type = = CX18_MAX_STREAMS )
return 0 ;
/* One or more streams could not be initialized. Clean 'em all up. */
2008-05-01 10:31:12 -03:00
cx18_streams_cleanup ( cx , 0 ) ;
2008-10-18 10:20:25 -03:00
return ret ;
2008-04-28 20:24:33 -03:00
}
static int cx18_reg_dev ( struct cx18 * cx , int type )
{
struct cx18_stream * s = & cx - > streams [ type ] ;
int vfl_type = cx18_stream_info [ type ] . vfl_type ;
2009-11-27 13:57:15 -03:00
const char * name ;
2008-10-18 10:20:25 -03:00
int num , ret ;
2008-04-28 20:24:33 -03:00
2010-12-11 20:38:20 -03:00
if ( type = = CX18_ENC_STREAM_TYPE_TS & & s - > dvb ! = NULL ) {
2008-10-18 10:20:25 -03:00
ret = cx18_dvb_register ( s ) ;
if ( ret < 0 ) {
2008-04-28 20:24:33 -03:00
CX18_ERR ( " DVB failed to register \n " ) ;
2008-10-18 10:20:25 -03:00
return ret ;
2008-04-28 20:24:33 -03:00
}
}
2009-01-10 21:54:39 -03:00
if ( s - > video_dev = = NULL )
2008-04-28 20:24:33 -03:00
return 0 ;
2009-01-10 21:54:39 -03:00
num = s - > video_dev - > num ;
2008-10-04 08:36:54 -03:00
/* card number + user defined offset + device offset */
if ( type ! = CX18_ENC_STREAM_TYPE_MPG ) {
struct cx18_stream * s_mpg = & cx - > streams [ CX18_ENC_STREAM_TYPE_MPG ] ;
2009-01-10 21:54:39 -03:00
if ( s_mpg - > video_dev )
num = s_mpg - > video_dev - > num
+ cx18_stream_info [ type ] . num_offset ;
2008-10-04 08:36:54 -03:00
}
2009-02-14 17:08:37 -03:00
video_set_drvdata ( s - > video_dev , s ) ;
2008-04-28 20:24:33 -03:00
/* Register device. First try the desired minor, then any free one. */
2009-09-06 07:54:00 -03:00
ret = video_register_device_no_warn ( s - > video_dev , vfl_type , num ) ;
2008-10-18 10:20:25 -03:00
if ( ret < 0 ) {
2009-06-19 11:54:00 -03:00
CX18_ERR ( " Couldn't register v4l2 device for %s (device node number %d) \n " ,
2008-10-04 08:36:54 -03:00
s - > name , num ) ;
2009-01-10 21:54:39 -03:00
video_device_release ( s - > video_dev ) ;
s - > video_dev = NULL ;
2008-10-18 10:20:25 -03:00
return ret ;
2008-04-28 20:24:33 -03:00
}
2009-11-27 13:57:15 -03:00
name = video_device_node_name ( s - > video_dev ) ;
2008-04-28 20:24:33 -03:00
switch ( vfl_type ) {
case VFL_TYPE_GRABBER :
2009-11-27 13:57:15 -03:00
CX18_INFO ( " Registered device %s for %s (%d x %d.%02d kB) \n " ,
name , s - > name , cx - > stream_buffers [ type ] ,
2009-11-09 23:55:30 -03:00
cx - > stream_buf_size [ type ] / 1024 ,
( cx - > stream_buf_size [ type ] * 100 / 1024 ) % 100 ) ;
2008-04-28 20:24:33 -03:00
break ;
case VFL_TYPE_RADIO :
2009-11-27 13:57:15 -03:00
CX18_INFO ( " Registered device %s for %s \n " , name , s - > name ) ;
2008-04-28 20:24:33 -03:00
break ;
case VFL_TYPE_VBI :
2008-12-07 23:30:17 -03:00
if ( cx - > stream_buffers [ type ] )
2009-11-27 13:57:15 -03:00
CX18_INFO ( " Registered device %s for %s "
2008-12-07 23:30:17 -03:00
" (%d x %d bytes) \n " ,
2009-11-27 13:57:15 -03:00
name , s - > name , cx - > stream_buffers [ type ] ,
2008-12-07 23:30:17 -03:00
cx - > stream_buf_size [ type ] ) ;
2008-04-28 20:24:33 -03:00
else
2009-11-27 13:57:15 -03:00
CX18_INFO ( " Registered device %s for %s \n " ,
name , s - > name ) ;
2008-04-28 20:24:33 -03:00
break ;
}
return 0 ;
}
/* Register v4l2 devices */
int cx18_streams_register ( struct cx18 * cx )
{
int type ;
2008-10-18 10:20:25 -03:00
int err ;
int ret = 0 ;
2008-04-28 20:24:33 -03:00
/* Register V4L2 devices */
2008-10-18 10:20:25 -03:00
for ( type = 0 ; type < CX18_MAX_STREAMS ; type + + ) {
err = cx18_reg_dev ( cx , type ) ;
if ( err & & ret = = 0 )
ret = err ;
}
2008-04-28 20:24:33 -03:00
2008-10-18 10:20:25 -03:00
if ( ret = = 0 )
2008-04-28 20:24:33 -03:00
return 0 ;
/* One or more streams could not be initialized. Clean 'em all up. */
2008-05-01 10:31:12 -03:00
cx18_streams_cleanup ( cx , 1 ) ;
2008-10-18 10:20:25 -03:00
return ret ;
2008-04-28 20:24:33 -03:00
}
/* Unregister v4l2 devices */
2008-05-01 10:31:12 -03:00
void cx18_streams_cleanup ( struct cx18 * cx , int unregister )
2008-04-28 20:24:33 -03:00
{
struct video_device * vdev ;
int type ;
/* Teardown all streams */
for ( type = 0 ; type < CX18_MAX_STREAMS ; type + + ) {
2009-12-31 01:35:08 -03:00
2010-12-11 20:38:20 -03:00
/* The TS has a cx18_dvb structure, not a video_device */
2009-12-31 01:35:08 -03:00
if ( type = = CX18_ENC_STREAM_TYPE_TS ) {
2010-12-11 20:38:20 -03:00
if ( cx - > streams [ type ] . dvb ! = NULL ) {
if ( unregister )
cx18_dvb_unregister ( & cx - > streams [ type ] ) ;
kfree ( cx - > streams [ type ] . dvb ) ;
cx - > streams [ type ] . dvb = NULL ;
2009-12-31 01:35:08 -03:00
cx18_stream_free ( & cx - > streams [ type ] ) ;
}
continue ;
}
/* No struct video_device, but can have buffers allocated */
if ( type = = CX18_ENC_STREAM_TYPE_IDX ) {
if ( cx - > stream_buffers [ type ] ! = 0 ) {
cx - > stream_buffers [ type ] = 0 ;
cx18_stream_free ( & cx - > streams [ type ] ) ;
}
continue ;
2008-07-18 10:07:10 -03:00
}
2008-04-28 20:24:33 -03:00
2009-12-31 01:35:08 -03:00
/* If struct video_device exists, can have buffers allocated */
2009-01-10 21:54:39 -03:00
vdev = cx - > streams [ type ] . video_dev ;
2008-04-28 20:24:33 -03:00
2009-01-10 21:54:39 -03:00
cx - > streams [ type ] . video_dev = NULL ;
2008-04-28 20:24:33 -03:00
if ( vdev = = NULL )
continue ;
cx18_stream_free ( & cx - > streams [ type ] ) ;
2008-05-01 10:31:12 -03:00
/* Unregister or release device */
if ( unregister )
video_unregister_device ( vdev ) ;
else
video_device_release ( vdev ) ;
2008-04-28 20:24:33 -03:00
}
}
static void cx18_vbi_setup ( struct cx18_stream * s )
{
struct cx18 * cx = s - > cx ;
2008-12-12 16:24:04 -03:00
int raw = cx18_raw_vbi ( cx ) ;
2008-04-28 20:24:33 -03:00
u32 data [ CX2341X_MBOX_MAX_DATA ] ;
int lines ;
if ( cx - > is_60hz ) {
cx - > vbi . count = 12 ;
cx - > vbi . start [ 0 ] = 10 ;
cx - > vbi . start [ 1 ] = 273 ;
} else { /* PAL/SECAM */
cx - > vbi . count = 18 ;
cx - > vbi . start [ 0 ] = 6 ;
cx - > vbi . start [ 1 ] = 318 ;
}
/* setup VBI registers */
2010-03-14 12:24:15 -03:00
if ( raw )
v4l2_subdev_call ( cx - > sd_av , vbi , s_raw_fmt , & cx - > vbi . in . fmt . vbi ) ;
else
v4l2_subdev_call ( cx - > sd_av , vbi , s_sliced_fmt , & cx - > vbi . in . fmt . sliced ) ;
2008-04-28 20:24:33 -03:00
2009-02-06 18:33:43 -03:00
/*
* Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
* VBI when the first analog capture channel starts , as once it starts
* ( e . g . MPEG ) , we can ' t effect any change in the Encoder Raw VBI setup
* ( i . e . for the VBI capture channels ) . We also send it for each
* analog capture channel anyway just to make sure we get the proper
* behavior
*/
2008-04-28 20:24:33 -03:00
if ( raw ) {
lines = cx - > vbi . count * 2 ;
} else {
2009-02-08 22:40:04 -03:00
/*
* For 525 / 60 systems , according to the VIP 2 & BT .656 std :
* The EAV RP code ' s Field bit toggles on line 4 , a few lines
* after the Vertcal Blank bit has already toggled .
* Tell the encoder to capture 21 - 4 + 1 = 18 lines per field ,
* since we want lines 10 through 21.
*
2009-05-10 22:14:29 -03:00
* For 625 / 50 systems , according to the VIP 2 & BT .656 std :
* The EAV RP code ' s Field bit toggles on line 1 , a few lines
* after the Vertcal Blank bit has already toggled .
2009-05-16 21:06:57 -03:00
* ( We ' ve actually set the digitizer so that the Field bit
* toggles on line 2. ) Tell the encoder to capture 23 - 2 + 1 = 22
* lines per field , since we want lines 6 through 23.
2009-02-08 22:40:04 -03:00
*/
2009-05-16 21:06:57 -03:00
lines = cx - > is_60hz ? ( 21 - 4 + 1 ) * 2 : ( 23 - 2 + 1 ) * 2 ;
2008-04-28 20:24:33 -03:00
}
data [ 0 ] = s - > handle ;
/* Lines per field */
data [ 1 ] = ( lines / 2 ) | ( ( lines / 2 ) < < 16 ) ;
/* bytes per line */
2009-01-31 00:33:02 -03:00
data [ 2 ] = ( raw ? vbi_active_samples
: ( cx - > is_60hz ? vbi_hblank_samples_60Hz
: vbi_hblank_samples_50Hz ) ) ;
2008-04-28 20:24:33 -03:00
/* Every X number of frames a VBI interrupt arrives
( frames as in 25 or 30 fps ) */
data [ 3 ] = 1 ;
2009-01-31 00:33:02 -03:00
/*
* Set the SAV / EAV RP codes to look for as start / stop points
* when in VIP - 1.1 mode
*/
2008-04-28 20:24:33 -03:00
if ( raw ) {
2009-01-31 00:33:02 -03:00
/*
* Start codes for beginning of " active " line in vertical blank
* 0x20 ( VerticalBlank )
* 0x60 ( EvenField VerticalBlank )
*/
2008-04-28 20:24:33 -03:00
data [ 4 ] = 0x20602060 ;
2009-01-31 00:33:02 -03:00
/*
* End codes for end of " active " raw lines and regular lines
* 0x30 ( VerticalBlank HorizontalBlank )
* 0x70 ( EvenField VerticalBlank HorizontalBlank )
* 0x90 ( Task HorizontalBlank )
* 0xd0 ( Task EvenField HorizontalBlank )
*/
2008-12-12 20:00:29 -03:00
data [ 5 ] = 0x307090d0 ;
2008-04-28 20:24:33 -03:00
} else {
2009-01-31 00:33:02 -03:00
/*
* End codes for active video , we want data in the hblank region
* 0xb0 ( Task 0 VerticalBlank HorizontalBlank )
* 0xf0 ( Task EvenField VerticalBlank HorizontalBlank )
*
* Since the V bit is only allowed to toggle in the EAV RP code ,
* just before the first active region line , these two
2009-02-08 22:40:04 -03:00
* are problematic :
2009-01-31 00:33:02 -03:00
* 0x90 ( Task HorizontalBlank )
* 0xd0 ( Task EvenField HorizontalBlank )
2009-02-08 22:40:04 -03:00
*
2009-02-28 20:13:50 -03:00
* We have set the digitzer such that we don ' t have to worry
* about these problem codes .
2009-01-31 00:33:02 -03:00
*/
2008-04-28 20:24:33 -03:00
data [ 4 ] = 0xB0F0B0F0 ;
2009-01-31 00:33:02 -03:00
/*
* Start codes for beginning of active line in vertical blank
* 0xa0 ( Task VerticalBlank )
* 0xe0 ( Task EvenField VerticalBlank )
*/
2008-04-28 20:24:33 -03:00
data [ 5 ] = 0xA0E0A0E0 ;
}
CX18_DEBUG_INFO ( " Setup VBI h: %d lines %x bpl %d fr %d %x %x \n " ,
data [ 0 ] , data [ 1 ] , data [ 2 ] , data [ 3 ] , data [ 4 ] , data [ 5 ] ) ;
2009-02-06 18:33:43 -03:00
cx18_api ( cx , CX18_CPU_SET_RAW_VBI_PARAM , 6 , data ) ;
2008-04-28 20:24:33 -03:00
}
2009-12-31 18:27:13 -03:00
void cx18_stream_rotate_idx_mdls ( struct cx18 * cx )
{
struct cx18_stream * s = & cx - > streams [ CX18_ENC_STREAM_TYPE_IDX ] ;
struct cx18_mdl * mdl ;
if ( ! cx18_stream_enabled ( s ) )
return ;
/* Return if the firmware is not running low on MDLs */
if ( ( atomic_read ( & s - > q_free . depth ) + atomic_read ( & s - > q_busy . depth ) ) > =
CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN )
return ;
/* Return if there are no MDLs to rotate back to the firmware */
if ( atomic_read ( & s - > q_full . depth ) < 2 )
return ;
/*
* Take the oldest IDX MDL still holding data , and discard its index
* entries by scheduling the MDL to go back to the firmware
*/
mdl = cx18_dequeue ( s , & s - > q_full ) ;
if ( mdl ! = NULL )
cx18_enqueue ( s , mdl , & s - > q_free ) ;
}
2009-04-13 22:42:43 -03:00
static
2009-11-08 23:45:24 -03:00
struct cx18_queue * _cx18_stream_put_mdl_fw ( struct cx18_stream * s ,
struct cx18_mdl * mdl )
2008-12-08 23:02:45 -03:00
{
struct cx18 * cx = s - > cx ;
struct cx18_queue * q ;
/* Don't give it to the firmware, if we're not running a capture */
if ( s - > handle = = CX18_INVALID_TASK_HANDLE | |
2009-04-13 22:42:43 -03:00
test_bit ( CX18_F_S_STOPPING , & s - > s_flags ) | |
2008-12-08 23:02:45 -03:00
! test_bit ( CX18_F_S_STREAMING , & s - > s_flags ) )
2009-11-08 23:45:24 -03:00
return cx18_enqueue ( s , mdl , & s - > q_free ) ;
2008-12-08 23:02:45 -03:00
2009-11-08 23:45:24 -03:00
q = cx18_enqueue ( s , mdl , & s - > q_busy ) ;
2008-12-08 23:02:45 -03:00
if ( q ! = & s - > q_busy )
2009-11-08 23:45:24 -03:00
return q ; /* The firmware has the max MDLs it can handle */
2008-12-08 23:02:45 -03:00
2009-11-08 23:45:24 -03:00
cx18_mdl_sync_for_device ( s , mdl ) ;
2008-12-08 23:02:45 -03:00
cx18_vapi ( cx , CX18_CPU_DE_SET_MDL , 5 , s - > handle ,
2009-11-08 23:45:24 -03:00
( void __iomem * ) & cx - > scb - > cpu_mdl [ mdl - > id ] - cx - > enc_mem ,
s - > bufs_per_mdl , mdl - > id , s - > mdl_size ) ;
2008-12-08 23:02:45 -03:00
return q ;
}
2009-04-13 22:42:43 -03:00
static
void _cx18_stream_load_fw_queue ( struct cx18_stream * s )
2008-12-08 23:02:45 -03:00
{
2008-12-12 15:50:27 -03:00
struct cx18_queue * q ;
2009-11-08 23:45:24 -03:00
struct cx18_mdl * mdl ;
2008-12-08 23:02:45 -03:00
2009-11-04 23:13:58 -03:00
if ( atomic_read ( & s - > q_free . depth ) = = 0 | |
atomic_read ( & s - > q_busy . depth ) > = CX18_MAX_FW_MDLS_PER_STREAM )
2008-12-12 15:50:27 -03:00
return ;
/* Move from q_free to q_busy notifying the firmware, until the limit */
do {
2009-11-08 23:45:24 -03:00
mdl = cx18_dequeue ( s , & s - > q_free ) ;
if ( mdl = = NULL )
2008-12-12 15:50:27 -03:00
break ;
2009-11-08 23:45:24 -03:00
q = _cx18_stream_put_mdl_fw ( s , mdl ) ;
2009-11-04 23:13:58 -03:00
} while ( atomic_read ( & s - > q_busy . depth ) < CX18_MAX_FW_MDLS_PER_STREAM
2008-12-14 18:52:12 -03:00
& & q = = & s - > q_busy ) ;
2008-12-08 23:02:45 -03:00
}
2009-04-13 22:42:43 -03:00
void cx18_out_work_handler ( struct work_struct * work )
{
2009-04-15 20:45:10 -03:00
struct cx18_stream * s =
container_of ( work , struct cx18_stream , out_work_order ) ;
2009-04-13 22:42:43 -03:00
2009-04-15 20:45:10 -03:00
_cx18_stream_load_fw_queue ( s ) ;
2009-04-13 22:42:43 -03:00
}
2009-11-08 23:45:24 -03:00
static void cx18_stream_configure_mdls ( struct cx18_stream * s )
{
cx18_unload_queues ( s ) ;
2009-11-09 23:55:30 -03:00
switch ( s - > type ) {
case CX18_ENC_STREAM_TYPE_YUV :
/*
* Height should be a multiple of 32 lines .
* Set the MDL size to the exact size needed for one frame .
* Use enough buffers per MDL to cover the MDL size
*/
2010-12-31 10:22:52 -03:00
s - > mdl_size = 720 * s - > cx - > cxhdl . height * 3 / 2 ;
2009-11-09 23:55:30 -03:00
s - > bufs_per_mdl = s - > mdl_size / s - > buf_size ;
if ( s - > mdl_size % s - > buf_size )
s - > bufs_per_mdl + + ;
break ;
2009-11-11 00:22:57 -03:00
case CX18_ENC_STREAM_TYPE_VBI :
s - > bufs_per_mdl = 1 ;
if ( cx18_raw_vbi ( s - > cx ) ) {
s - > mdl_size = ( s - > cx - > is_60hz ? 12 : 18 )
* 2 * vbi_active_samples ;
} else {
/*
* See comment in cx18_vbi_setup ( ) below about the
* extra lines we capture in sliced VBI mode due to
* the lines on which EAV RP codes toggle .
*/
s - > mdl_size = s - > cx - > is_60hz
? ( 21 - 4 + 1 ) * 2 * vbi_hblank_samples_60Hz
: ( 23 - 2 + 1 ) * 2 * vbi_hblank_samples_50Hz ;
}
break ;
2009-11-09 23:55:30 -03:00
default :
s - > bufs_per_mdl = 1 ;
s - > mdl_size = s - > buf_size * s - > bufs_per_mdl ;
break ;
}
2009-11-08 23:45:24 -03:00
cx18_load_queues ( s ) ;
}
2008-04-28 20:24:33 -03:00
int cx18_start_v4l2_encode_stream ( struct cx18_stream * s )
{
u32 data [ MAX_MB_ARGUMENTS ] ;
struct cx18 * cx = s - > cx ;
int captype = 0 ;
2009-12-31 02:14:51 -03:00
struct cx18_stream * s_idx ;
2008-04-28 20:24:33 -03:00
2009-12-31 00:26:49 -03:00
if ( ! cx18_stream_enabled ( s ) )
2008-04-28 20:24:33 -03:00
return - EINVAL ;
CX18_DEBUG_INFO ( " Start encoder stream %s \n " , s - > name ) ;
switch ( s - > type ) {
case CX18_ENC_STREAM_TYPE_MPG :
captype = CAPTURE_CHANNEL_TYPE_MPEG ;
cx - > mpg_data_received = cx - > vbi_data_inserted = 0 ;
cx - > dualwatch_jiffies = jiffies ;
2010-12-31 10:22:52 -03:00
cx - > dualwatch_stereo_mode = v4l2_ctrl_g_ctrl ( cx - > cxhdl . audio_mode ) ;
2008-04-28 20:24:33 -03:00
cx - > search_pack_header = 0 ;
break ;
2009-12-31 02:14:51 -03:00
case CX18_ENC_STREAM_TYPE_IDX :
captype = CAPTURE_CHANNEL_TYPE_INDEX ;
break ;
2008-04-28 20:24:33 -03:00
case CX18_ENC_STREAM_TYPE_TS :
captype = CAPTURE_CHANNEL_TYPE_TS ;
break ;
case CX18_ENC_STREAM_TYPE_YUV :
captype = CAPTURE_CHANNEL_TYPE_YUV ;
break ;
case CX18_ENC_STREAM_TYPE_PCM :
captype = CAPTURE_CHANNEL_TYPE_PCM ;
break ;
case CX18_ENC_STREAM_TYPE_VBI :
2009-02-06 18:33:43 -03:00
# ifdef CX18_ENCODER_PARSES_SLICED
2008-12-12 16:24:04 -03:00
captype = cx18_raw_vbi ( cx ) ?
CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI ;
2009-02-06 18:33:43 -03:00
# else
/*
* Currently we set things up so that Sliced VBI from the
* digitizer is handled as Raw VBI by the encoder
*/
captype = CAPTURE_CHANNEL_TYPE_VBI ;
# endif
2008-04-28 20:24:33 -03:00
cx - > vbi . frame = 0 ;
cx - > vbi . inserted_frame = 0 ;
memset ( cx - > vbi . sliced_mpeg_size ,
0 , sizeof ( cx - > vbi . sliced_mpeg_size ) ) ;
break ;
default :
return - EINVAL ;
}
/* Clear Streamoff flags in case left from last capture */
clear_bit ( CX18_F_S_STREAMOFF , & s - > s_flags ) ;
cx18_vapi_result ( cx , data , CX18_CREATE_TASK , 1 , CPU_CMD_MASK_CAPTURE ) ;
s - > handle = data [ 0 ] ;
cx18_vapi ( cx , CX18_CPU_SET_CHANNEL_TYPE , 2 , s - > handle , captype ) ;
2009-02-06 18:33:43 -03:00
/*
* For everything but CAPTURE_CHANNEL_TYPE_TS , play it safe and
* set up all the parameters , as it is not obvious which parameters the
* firmware shares across capture channel types and which it does not .
*
* Some of the cx18_vapi ( ) calls below apply to only certain capture
* channel types . We ' re hoping there ' s no harm in calling most of them
* anyway , as long as the values are all consistent . Setting some
* shared parameters will have no effect once an analog capture channel
* has started streaming .
*/
if ( captype ! = CAPTURE_CHANNEL_TYPE_TS ) {
2008-04-28 20:24:33 -03:00
cx18_vapi ( cx , CX18_CPU_SET_VER_CROP_LINE , 2 , s - > handle , 0 ) ;
cx18_vapi ( cx , CX18_CPU_SET_MISC_PARAMETERS , 3 , s - > handle , 3 , 1 ) ;
cx18_vapi ( cx , CX18_CPU_SET_MISC_PARAMETERS , 3 , s - > handle , 8 , 0 ) ;
cx18_vapi ( cx , CX18_CPU_SET_MISC_PARAMETERS , 3 , s - > handle , 4 , 1 ) ;
2009-02-06 18:33:43 -03:00
/*
* Audio related reset according to
* Documentation / video4linux / cx2341x / fw - encoder - api . txt
*/
if ( atomic_read ( & cx - > ana_capturing ) = = 0 )
cx18_vapi ( cx , CX18_CPU_SET_MISC_PARAMETERS , 2 ,
s - > handle , 12 ) ;
/*
* Number of lines for Field 1 & Field 2 according to
* Documentation / video4linux / cx2341x / fw - encoder - api . txt
2009-02-07 01:15:44 -03:00
* Field 1 is 312 for 625 line systems in BT .656
* Field 2 is 313 for 625 line systems in BT .656
2009-02-06 18:33:43 -03:00
*/
2008-04-28 20:24:33 -03:00
cx18_vapi ( cx , CX18_CPU_SET_CAPTURE_LINE_NO , 3 ,
2009-02-07 01:15:44 -03:00
s - > handle , 312 , 313 ) ;
2008-04-28 20:24:33 -03:00
if ( cx - > v4l2_cap & V4L2_CAP_VBI_CAPTURE )
cx18_vbi_setup ( s ) ;
2009-02-06 18:33:43 -03:00
/*
2009-12-31 02:14:51 -03:00
* Select to receive I , P , and B frame index entries , if the
* index stream is enabled . Otherwise disable index entry
* generation .
2009-02-06 18:33:43 -03:00
*/
2009-12-31 02:14:51 -03:00
s_idx = & cx - > streams [ CX18_ENC_STREAM_TYPE_IDX ] ;
2010-01-01 13:25:41 -03:00
cx18_vapi_result ( cx , data , CX18_CPU_SET_INDEXTABLE , 2 ,
s - > handle , cx18_stream_enabled ( s_idx ) ? 7 : 0 ) ;
2008-04-28 20:24:33 -03:00
2009-02-06 18:33:43 -03:00
/* Call out to the common CX2341x API setup for user controls */
2010-12-31 10:22:52 -03:00
cx - > cxhdl . priv = s ;
cx2341x_handler_setup ( & cx - > cxhdl ) ;
2009-02-06 18:33:43 -03:00
/*
* When starting a capture and we ' re set for radio ,
* ensure the video is muted , despite the user control .
*/
2010-12-31 10:22:52 -03:00
if ( ! cx - > cxhdl . video_mute & &
2009-02-06 18:33:43 -03:00
test_bit ( CX18_F_I_RADIO_USER , & cx - > i_flags ) )
cx18_vapi ( cx , CX18_CPU_SET_VIDEO_MUTE , 2 , s - > handle ,
2010-12-31 10:22:52 -03:00
( v4l2_ctrl_g_ctrl ( cx - > cxhdl . video_mute_yuv ) < < 8 ) | 1 ) ;
2008-04-28 20:24:33 -03:00
}
2008-05-25 11:21:27 -03:00
if ( atomic_read ( & cx - > tot_capturing ) = = 0 ) {
2010-12-31 10:22:52 -03:00
cx2341x_handler_set_busy ( & cx - > cxhdl , 1 ) ;
2008-04-28 20:24:33 -03:00
clear_bit ( CX18_F_I_EOS , & cx - > i_flags ) ;
2008-08-30 16:03:44 -03:00
cx18_write_reg ( cx , 7 , CX18_DSP0_INTERRUPT_MASK ) ;
2008-04-28 20:24:33 -03:00
}
cx18_vapi ( cx , CX18_CPU_DE_SET_MDL_ACK , 3 , s - > handle ,
2008-05-21 00:32:01 -03:00
( void __iomem * ) & cx - > scb - > cpu_mdl_ack [ s - > type ] [ 0 ] - cx - > enc_mem ,
( void __iomem * ) & cx - > scb - > cpu_mdl_ack [ s - > type ] [ 1 ] - cx - > enc_mem ) ;
2008-04-28 20:24:33 -03:00
2008-12-08 23:02:45 -03:00
/* Init all the cpu_mdls for this stream */
2009-11-08 23:45:24 -03:00
cx18_stream_configure_mdls ( s ) ;
2009-04-13 22:42:43 -03:00
_cx18_stream_load_fw_queue ( s ) ;
2008-12-08 23:02:45 -03:00
2008-04-28 20:24:33 -03:00
/* begin_capture */
if ( cx18_vapi ( cx , CX18_CPU_CAPTURE_START , 1 , s - > handle ) ) {
CX18_DEBUG_WARN ( " Error starting capture! \n " ) ;
2008-08-23 18:36:50 -03:00
/* Ensure we're really not capturing before releasing MDLs */
2009-04-13 22:42:43 -03:00
set_bit ( CX18_F_S_STOPPING , & s - > s_flags ) ;
2008-08-23 18:36:50 -03:00
if ( s - > type = = CX18_ENC_STREAM_TYPE_MPG )
cx18_vapi ( cx , CX18_CPU_CAPTURE_STOP , 2 , s - > handle , 1 ) ;
else
cx18_vapi ( cx , CX18_CPU_CAPTURE_STOP , 1 , s - > handle ) ;
2008-12-08 23:02:45 -03:00
clear_bit ( CX18_F_S_STREAMING , & s - > s_flags ) ;
/* FIXME - CX18_F_S_STREAMOFF as well? */
2008-08-23 18:36:50 -03:00
cx18_vapi ( cx , CX18_CPU_DE_RELEASE_MDL , 1 , s - > handle ) ;
2008-04-28 20:24:33 -03:00
cx18_vapi ( cx , CX18_DESTROY_TASK , 1 , s - > handle ) ;
2008-12-08 23:02:45 -03:00
s - > handle = CX18_INVALID_TASK_HANDLE ;
2009-04-13 22:42:43 -03:00
clear_bit ( CX18_F_S_STOPPING , & s - > s_flags ) ;
2008-12-08 23:02:45 -03:00
if ( atomic_read ( & cx - > tot_capturing ) = = 0 ) {
set_bit ( CX18_F_I_EOS , & cx - > i_flags ) ;
cx18_write_reg ( cx , 5 , CX18_DSP0_INTERRUPT_MASK ) ;
}
2008-04-28 20:24:33 -03:00
return - EINVAL ;
}
/* you're live! sit back and await interrupts :) */
2009-02-06 18:33:43 -03:00
if ( captype ! = CAPTURE_CHANNEL_TYPE_TS )
2008-05-25 11:21:27 -03:00
atomic_inc ( & cx - > ana_capturing ) ;
atomic_inc ( & cx - > tot_capturing ) ;
2008-04-28 20:24:33 -03:00
return 0 ;
}
2009-11-19 23:23:57 -03:00
EXPORT_SYMBOL ( cx18_start_v4l2_encode_stream ) ;
2008-04-28 20:24:33 -03:00
void cx18_stop_all_captures ( struct cx18 * cx )
{
int i ;
for ( i = CX18_MAX_STREAMS - 1 ; i > = 0 ; i - - ) {
struct cx18_stream * s = & cx - > streams [ i ] ;
2009-12-31 00:26:49 -03:00
if ( ! cx18_stream_enabled ( s ) )
2008-04-28 20:24:33 -03:00
continue ;
if ( test_bit ( CX18_F_S_STREAMING , & s - > s_flags ) )
cx18_stop_v4l2_encode_stream ( s , 0 ) ;
}
}
int cx18_stop_v4l2_encode_stream ( struct cx18_stream * s , int gop_end )
{
struct cx18 * cx = s - > cx ;
unsigned long then ;
2009-12-31 00:26:49 -03:00
if ( ! cx18_stream_enabled ( s ) )
2008-04-28 20:24:33 -03:00
return - EINVAL ;
/* This function assumes that you are allowed to stop the capture
and that we are actually capturing */
CX18_DEBUG_INFO ( " Stop Capture \n " ) ;
2008-05-25 11:21:27 -03:00
if ( atomic_read ( & cx - > tot_capturing ) = = 0 )
2008-04-28 20:24:33 -03:00
return 0 ;
2009-04-13 22:42:43 -03:00
set_bit ( CX18_F_S_STOPPING , & s - > s_flags ) ;
2008-04-28 20:24:33 -03:00
if ( s - > type = = CX18_ENC_STREAM_TYPE_MPG )
cx18_vapi ( cx , CX18_CPU_CAPTURE_STOP , 2 , s - > handle , ! gop_end ) ;
else
cx18_vapi ( cx , CX18_CPU_CAPTURE_STOP , 1 , s - > handle ) ;
then = jiffies ;
if ( s - > type = = CX18_ENC_STREAM_TYPE_MPG & & gop_end ) {
CX18_INFO ( " ignoring gop_end: not (yet?) supported by the firmware \n " ) ;
}
2008-05-25 11:21:27 -03:00
if ( s - > type ! = CX18_ENC_STREAM_TYPE_TS )
atomic_dec ( & cx - > ana_capturing ) ;
atomic_dec ( & cx - > tot_capturing ) ;
2008-04-28 20:24:33 -03:00
/* Clear capture and no-read bits */
clear_bit ( CX18_F_S_STREAMING , & s - > s_flags ) ;
2008-11-05 21:19:15 -03:00
/* Tell the CX23418 it can't use our buffers anymore */
cx18_vapi ( cx , CX18_CPU_DE_RELEASE_MDL , 1 , s - > handle ) ;
2008-04-28 20:24:33 -03:00
cx18_vapi ( cx , CX18_DESTROY_TASK , 1 , s - > handle ) ;
2008-08-23 16:42:29 -03:00
s - > handle = CX18_INVALID_TASK_HANDLE ;
2009-04-13 22:42:43 -03:00
clear_bit ( CX18_F_S_STOPPING , & s - > s_flags ) ;
2008-04-28 20:24:33 -03:00
2008-05-25 11:21:27 -03:00
if ( atomic_read ( & cx - > tot_capturing ) > 0 )
2008-04-28 20:24:33 -03:00
return 0 ;
2010-12-31 10:22:52 -03:00
cx2341x_handler_set_busy ( & cx - > cxhdl , 0 ) ;
2008-08-30 16:03:44 -03:00
cx18_write_reg ( cx , 5 , CX18_DSP0_INTERRUPT_MASK ) ;
2008-04-28 20:24:33 -03:00
wake_up ( & s - > waitq ) ;
return 0 ;
}
2009-11-19 23:23:57 -03:00
EXPORT_SYMBOL ( cx18_stop_v4l2_encode_stream ) ;
2008-04-28 20:24:33 -03:00
u32 cx18_find_handle ( struct cx18 * cx )
{
int i ;
/* find first available handle to be used for global settings */
for ( i = 0 ; i < CX18_MAX_STREAMS ; i + + ) {
struct cx18_stream * s = & cx - > streams [ i ] ;
2009-01-10 21:54:39 -03:00
if ( s - > video_dev & & ( s - > handle ! = CX18_INVALID_TASK_HANDLE ) )
2008-04-28 20:24:33 -03:00
return s - > handle ;
}
2008-08-23 16:42:29 -03:00
return CX18_INVALID_TASK_HANDLE ;
2008-04-28 20:24:33 -03:00
}
2008-11-16 01:38:19 -03:00
struct cx18_stream * cx18_handle_to_stream ( struct cx18 * cx , u32 handle )
{
int i ;
struct cx18_stream * s ;
if ( handle = = CX18_INVALID_TASK_HANDLE )
return NULL ;
for ( i = 0 ; i < CX18_MAX_STREAMS ; i + + ) {
s = & cx - > streams [ i ] ;
if ( s - > handle ! = handle )
continue ;
2009-12-31 00:26:49 -03:00
if ( cx18_stream_enabled ( s ) )
2008-11-16 01:38:19 -03:00
return s ;
}
return NULL ;
}