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 >
2008-11-22 01:37:34 -03:00
* Copyright ( C ) 2008 Andy Walls < awalls @ radix . 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 */
2008-05-12 11:21:58 -03:00
. ioctl = cx18_v4l2_ioctl ,
. 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 */
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 ] ;
2008-12-07 23:30:17 -03:00
2008-11-16 20:18:05 -03:00
mutex_init ( & s - > qlock ) ;
2008-04-28 20:24:33 -03:00
init_waitqueue_head ( & s - > waitq ) ;
s - > id = - 1 ;
cx18_queue_init ( & s - > q_free ) ;
2008-12-08 23:02:45 -03:00
cx18_queue_init ( & s - > q_busy ) ;
2008-04-28 20:24:33 -03:00
cx18_queue_init ( & s - > q_full ) ;
}
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
2009-01-10 21:54:39 -03:00
/* These four fields are always initialized. If video_dev == NULL, then
2008-04-28 20:24:33 -03:00
this stream is not in use . In that case no other fields but these
four can be used . */
2009-01-10 21:54:39 -03:00
s - > video_dev = 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 ) ;
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 ;
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 ;
2008-10-18 10:20:25 -03:00
int num , ret ;
2008-04-28 20:24:33 -03:00
/* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something?
* We need a VFL_TYPE_TS defined .
*/
if ( strcmp ( " TS " , s - > name ) = = 0 ) {
/* just return if no DVB is supported */
if ( ( cx - > card - > hw_all & CX18_HW_DVB ) = = 0 )
return 0 ;
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-01-10 21:54:39 -03:00
ret = video_register_device ( s - > video_dev , vfl_type , num ) ;
2008-10-18 10:20:25 -03:00
if ( ret < 0 ) {
2008-10-04 08:36:54 -03:00
CX18_ERR ( " Couldn't register v4l2 device for %s kernel number %d \n " ,
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-01-10 21:54:39 -03:00
num = s - > video_dev - > num ;
2008-04-28 20:24:33 -03:00
switch ( vfl_type ) {
case VFL_TYPE_GRABBER :
2008-12-07 23:30:17 -03:00
CX18_INFO ( " Registered device video%d for %s (%d x %d kB) \n " ,
num , s - > name , cx - > stream_buffers [ type ] ,
cx - > stream_buf_size [ type ] / 1024 ) ;
2008-04-28 20:24:33 -03:00
break ;
case VFL_TYPE_RADIO :
CX18_INFO ( " Registered device radio%d for %s \n " ,
2008-10-04 08:36:54 -03:00
num , 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 ] )
CX18_INFO ( " Registered device vbi%d for %s "
" (%d x %d bytes) \n " ,
num , s - > name , cx - > stream_buffers [ type ] ,
cx - > stream_buf_size [ type ] ) ;
2008-04-28 20:24:33 -03:00
else
CX18_INFO ( " Registered device vbi%d for %s \n " ,
2008-10-04 08:36:54 -03:00
num , 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 + + ) {
2008-07-18 10:07:10 -03:00
if ( cx - > streams [ type ] . dvb . enabled ) {
2008-04-28 20:24:33 -03:00
cx18_dvb_unregister ( & cx - > streams [ type ] ) ;
2008-07-18 10:07:10 -03:00
cx - > streams [ type ] . dvb . enabled = false ;
}
2008-04-28 20:24:33 -03:00
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 */
2009-02-16 02:23:25 -03:00
v4l2_subdev_call ( cx - > sd_av , video , s_fmt , & cx - > vbi . in ) ;
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.
*
* FIXME - revisit for 625 / 50 systems
*/
lines = cx - > is_60hz ? ( 21 - 4 + 1 ) * 2 : 38 ;
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-04-13 22:42:43 -03:00
static
struct cx18_queue * _cx18_stream_put_buf_fw ( struct cx18_stream * s ,
struct cx18_buffer * buf )
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 ) )
return cx18_enqueue ( s , buf , & s - > q_free ) ;
q = cx18_enqueue ( s , buf , & s - > q_busy ) ;
if ( q ! = & s - > q_busy )
return q ; /* The firmware has the max buffers it can handle */
cx18_buf_sync_for_device ( s , buf ) ;
cx18_vapi ( cx , CX18_CPU_DE_SET_MDL , 5 , s - > handle ,
( void __iomem * ) & cx - > scb - > cpu_mdl [ buf - > id ] - cx - > enc_mem ,
1 , buf - > id , s - > buf_size ) ;
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 ;
2008-12-08 23:02:45 -03:00
struct cx18_buffer * buf ;
2008-12-12 15:50:27 -03:00
if ( atomic_read ( & s - > q_free . buffers ) = = 0 | |
2008-12-14 18:52:12 -03:00
atomic_read ( & s - > q_busy . buffers ) > = 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 {
buf = cx18_dequeue ( s , & s - > q_free ) ;
if ( buf = = NULL )
break ;
2009-04-13 22:42:43 -03:00
q = _cx18_stream_put_buf_fw ( s , buf ) ;
2008-12-14 18:52:12 -03:00
} while ( atomic_read ( & s - > q_busy . buffers ) < CX18_MAX_FW_MDLS_PER_STREAM
& & q = = & s - > q_busy ) ;
2008-12-08 23:02:45 -03:00
}
2009-04-13 22:42:43 -03:00
static inline
void free_out_work_order ( struct cx18_out_work_order * order )
{
atomic_set ( & order - > pending , 0 ) ;
}
void cx18_out_work_handler ( struct work_struct * work )
{
struct cx18_out_work_order * order =
container_of ( work , struct cx18_out_work_order , work ) ;
struct cx18_stream * s = order - > s ;
struct cx18_buffer * buf = order - > buf ;
free_out_work_order ( order ) ;
if ( buf = = NULL )
_cx18_stream_load_fw_queue ( s ) ;
else
_cx18_stream_put_buf_fw ( s , buf ) ;
}
static
struct cx18_out_work_order * alloc_out_work_order ( struct cx18 * cx )
{
int i ;
struct cx18_out_work_order * order = NULL ;
for ( i = 0 ; i < CX18_MAX_OUT_WORK_ORDERS ; i + + ) {
/*
* We need " pending " to be atomic to inspect & set its contents
* 1. " pending " is only set to 1 here , but needs multiple access
* protection
* 2. work handler threads only clear " pending " and only
* on one , particular work order at a time , per handler thread .
*/
if ( atomic_add_unless ( & cx - > out_work_order [ i ] . pending , 1 , 1 ) ) {
order = & cx - > out_work_order [ i ] ;
break ;
}
}
return order ;
}
struct cx18_queue * cx18_stream_put_buf_fw ( struct cx18_stream * s ,
struct cx18_buffer * buf )
{
struct cx18 * cx = s - > cx ;
struct cx18_out_work_order * order ;
order = alloc_out_work_order ( cx ) ;
if ( order = = NULL ) {
CX18_DEBUG_WARN ( " No blank, outgoing-mailbox, deferred-work, "
" order forms available; sending buffer %u back "
" to the firmware immediately for stream %s \n " ,
buf - > id , s - > name ) ;
return _cx18_stream_put_buf_fw ( s , buf ) ;
}
order - > s = s ;
order - > buf = buf ;
queue_work ( cx - > out_work_queue , & order - > work ) ;
return NULL ;
}
void cx18_stream_load_fw_queue ( struct cx18_stream * s )
{
struct cx18 * cx = s - > cx ;
struct cx18_out_work_order * order ;
order = alloc_out_work_order ( cx ) ;
if ( order = = NULL ) {
CX18_DEBUG_WARN ( " No blank, outgoing-mailbox, deferred-work, "
" order forms available; filling the firmware "
" buffer queue immediately for stream %s \n " ,
s - > name ) ;
_cx18_stream_load_fw_queue ( s ) ;
return ;
}
order - > s = s ;
order - > buf = NULL ; /* Indicates to load the fw queue */
queue_work ( cx - > out_work_queue , & order - > work ) ;
}
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 ;
2008-12-08 23:02:45 -03:00
struct cx18_buffer * buf ;
2008-04-28 20:24:33 -03:00
int captype = 0 ;
2009-02-06 18:33:43 -03:00
struct cx18_api_func_private priv ;
2008-04-28 20:24:33 -03:00
2009-01-10 21:54:39 -03:00
if ( s - > video_dev = = NULL & & s - > dvb . enabled = = 0 )
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 ;
cx - > dualwatch_stereo_mode = cx - > params . audio_properties & 0x300 ;
cx - > search_pack_header = 0 ;
break ;
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
/*
* assign program index info .
* Mask 7 : select I / P / B , Num_req : 400 max
* FIXME - currently we have this hardcoded as disabled
*/
2008-04-28 20:24:33 -03:00
cx18_vapi_result ( cx , data , CX18_CPU_SET_INDEXTABLE , 1 , 0 ) ;
2009-02-06 18:33:43 -03:00
/* Call out to the common CX2341x API setup for user controls */
2008-11-23 19:16:44 -03:00
priv . cx = cx ;
priv . s = s ;
cx2341x_update ( & priv , cx18_api_func , NULL , & cx - > params ) ;
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 .
*/
if ( ! cx - > params . video_mute & &
test_bit ( CX18_F_I_RADIO_USER , & cx - > i_flags ) )
cx18_vapi ( cx , CX18_CPU_SET_VIDEO_MUTE , 2 , s - > handle ,
( cx - > params . 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 ) {
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 */
cx18_flush_queues ( s ) ;
mutex_lock ( & s - > qlock ) ;
2008-12-14 21:05:36 -03:00
list_for_each_entry ( buf , & s - > q_free . list , list ) {
2008-08-30 16:03:44 -03:00
cx18_writel ( cx , buf - > dma_handle ,
& cx - > scb - > cpu_mdl [ buf - > id ] . paddr ) ;
cx18_writel ( cx , s - > buf_size , & cx - > scb - > cpu_mdl [ buf - > id ] . length ) ;
2008-04-28 20:24:33 -03:00
}
2008-12-08 23:02:45 -03:00
mutex_unlock ( & s - > qlock ) ;
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 ;
}
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-01-10 21:54:39 -03:00
if ( s - > video_dev = = NULL & & s - > dvb . enabled = = 0 )
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-01-10 21:54:39 -03:00
if ( s - > video_dev = = NULL & & s - > dvb . enabled = = 0 )
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 ;
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 ;
}
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-01-10 21:54:39 -03:00
if ( s - > video_dev | | s - > dvb . enabled )
2008-11-16 01:38:19 -03:00
return s ;
}
return NULL ;
}