2009-06-09 05:55:37 -03:00
/*
* vpif - display - VPIF display driver
* Display driver for TI DaVinci VPIF
*
* Copyright ( C ) 2009 Texas Instruments Incorporated - http : //www.ti.com/
*
* 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 version 2.
*
* This program is distributed . as is . WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/workqueue.h>
# include <linux/string.h>
# include <linux/videodev2.h>
# include <linux/wait.h>
# include <linux/time.h>
# include <linux/i2c.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# include <linux/version.h>
# include <asm/irq.h>
# include <asm/page.h>
# include <media/adv7343.h>
# include <media/v4l2-device.h>
# include <media/v4l2-ioctl.h>
# include <mach/dm646x.h>
# include "vpif_display.h"
# include "vpif.h"
MODULE_DESCRIPTION ( " TI DaVinci VPIF Display driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
# define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
# define vpif_dbg(level, debug, fmt, arg...) \
v4l2_dbg ( level , debug , & vpif_obj . v4l2_dev , fmt , # # arg )
static int debug = 1 ;
static u32 ch2_numbuffers = 3 ;
static u32 ch3_numbuffers = 3 ;
static u32 ch2_bufsize = 1920 * 1080 * 2 ;
static u32 ch3_bufsize = 720 * 576 * 2 ;
module_param ( debug , int , 0644 ) ;
module_param ( ch2_numbuffers , uint , S_IRUGO ) ;
module_param ( ch3_numbuffers , uint , S_IRUGO ) ;
module_param ( ch2_bufsize , uint , S_IRUGO ) ;
module_param ( ch3_bufsize , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( debug , " Debug level 0-1 " ) ;
MODULE_PARM_DESC ( ch2_numbuffers , " Channel2 buffer count (default:3) " ) ;
MODULE_PARM_DESC ( ch3_numbuffers , " Channel3 buffer count (default:3) " ) ;
MODULE_PARM_DESC ( ch2_bufsize , " Channel2 buffer size (default:1920 x 1080 x 2) " ) ;
MODULE_PARM_DESC ( ch3_bufsize , " Channel3 buffer size (default:720 x 576 x 2) " ) ;
static struct vpif_config_params config_params = {
. min_numbuffers = 3 ,
. numbuffers [ 0 ] = 3 ,
. numbuffers [ 1 ] = 3 ,
. min_bufsize [ 0 ] = 720 * 480 * 2 ,
. min_bufsize [ 1 ] = 720 * 480 * 2 ,
. channel_bufsize [ 0 ] = 1920 * 1080 * 2 ,
. channel_bufsize [ 1 ] = 720 * 576 * 2 ,
} ;
static struct vpif_device vpif_obj = { { NULL } } ;
static struct device * vpif_dev ;
static const struct vpif_channel_config_params ch_params [ ] = {
{
" NTSC " , 720 , 480 , 30 , 0 , 1 , 268 , 1440 , 1 , 23 , 263 , 266 ,
286 , 525 , 525 , 0 , 1 , 0 , V4L2_STD_525_60 ,
} ,
{
" PAL " , 720 , 576 , 25 , 0 , 1 , 280 , 1440 , 1 , 23 , 311 , 313 ,
336 , 624 , 625 , 0 , 1 , 0 , V4L2_STD_625_50 ,
} ,
} ;
/*
* vpif_uservirt_to_phys : This function is used to convert user
* space virtual address to physical address .
*/
static u32 vpif_uservirt_to_phys ( u32 virtp )
{
struct mm_struct * mm = current - > mm ;
unsigned long physp = 0 ;
struct vm_area_struct * vma ;
vma = find_vma ( mm , virtp ) ;
/* For kernel direct-mapped memory, take the easy way */
if ( virtp > = PAGE_OFFSET ) {
physp = virt_to_phys ( ( void * ) virtp ) ;
} else if ( vma & & ( vma - > vm_flags & VM_IO ) & & ( vma - > vm_pgoff ) ) {
/* this will catch, kernel-allocated, mmaped-to-usermode addr */
physp = ( vma - > vm_pgoff < < PAGE_SHIFT ) + ( virtp - vma - > vm_start ) ;
} else {
/* otherwise, use get_user_pages() for general userland pages */
int res , nr_pages = 1 ;
struct page * pages ;
down_read ( & current - > mm - > mmap_sem ) ;
res = get_user_pages ( current , current - > mm ,
virtp , nr_pages , 1 , 0 , & pages , NULL ) ;
up_read ( & current - > mm - > mmap_sem ) ;
if ( res = = nr_pages ) {
physp = __pa ( page_address ( & pages [ 0 ] ) +
( virtp & ~ PAGE_MASK ) ) ;
} else {
vpif_err ( " get_user_pages failed \n " ) ;
return 0 ;
}
}
return physp ;
}
/*
* buffer_prepare : This is the callback function called from videobuf_qbuf ( )
* function the buffer is prepared and user space virtual address is converted
* into physical address
*/
static int vpif_buffer_prepare ( struct videobuf_queue * q ,
struct videobuf_buffer * vb ,
enum v4l2_field field )
{
struct vpif_fh * fh = q - > priv_data ;
struct common_obj * common ;
unsigned long addr ;
common = & fh - > channel - > common [ VPIF_VIDEO_INDEX ] ;
if ( VIDEOBUF_NEEDS_INIT = = vb - > state ) {
vb - > width = common - > width ;
vb - > height = common - > height ;
vb - > size = vb - > width * vb - > height ;
vb - > field = field ;
}
vb - > state = VIDEOBUF_PREPARED ;
/* if user pointer memory mechanism is used, get the physical
* address of the buffer */
if ( V4L2_MEMORY_USERPTR = = common - > memory ) {
if ( ! vb - > baddr ) {
vpif_err ( " buffer_address is 0 \n " ) ;
return - EINVAL ;
}
vb - > boff = vpif_uservirt_to_phys ( vb - > baddr ) ;
if ( ! ISALIGNED ( vb - > boff ) )
goto buf_align_exit ;
}
addr = vb - > boff ;
if ( q - > streaming & & ( V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ! = q - > type ) ) {
if ( ! ISALIGNED ( addr + common - > ytop_off ) | |
! ISALIGNED ( addr + common - > ybtm_off ) | |
! ISALIGNED ( addr + common - > ctop_off ) | |
! ISALIGNED ( addr + common - > cbtm_off ) )
goto buf_align_exit ;
}
return 0 ;
buf_align_exit :
vpif_err ( " buffer offset not aligned to 8 bytes \n " ) ;
return - EINVAL ;
}
/*
* vpif_buffer_setup : This function allocates memory for the buffers
*/
static int vpif_buffer_setup ( struct videobuf_queue * q , unsigned int * count ,
unsigned int * size )
{
struct vpif_fh * fh = q - > priv_data ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( V4L2_MEMORY_MMAP ! = common - > memory )
return 0 ;
* size = config_params . channel_bufsize [ ch - > channel_id ] ;
if ( * count < config_params . min_numbuffers )
* count = config_params . min_numbuffers ;
return 0 ;
}
/*
* vpif_buffer_queue : This function adds the buffer to DMA queue
*/
static void vpif_buffer_queue ( struct videobuf_queue * q ,
struct videobuf_buffer * vb )
{
struct vpif_fh * fh = q - > priv_data ;
struct common_obj * common ;
common = & fh - > channel - > common [ VPIF_VIDEO_INDEX ] ;
/* add the buffer to the DMA queue */
list_add_tail ( & vb - > queue , & common - > dma_queue ) ;
vb - > state = VIDEOBUF_QUEUED ;
}
/*
* vpif_buffer_release : This function is called from the videobuf layer to
* free memory allocated to the buffers
*/
static void vpif_buffer_release ( struct videobuf_queue * q ,
struct videobuf_buffer * vb )
{
struct vpif_fh * fh = q - > priv_data ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common ;
unsigned int buf_size = 0 ;
common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
videobuf_dma_contig_free ( q , vb ) ;
vb - > state = VIDEOBUF_NEEDS_INIT ;
if ( V4L2_MEMORY_MMAP ! = common - > memory )
return ;
buf_size = config_params . channel_bufsize [ ch - > channel_id ] ;
}
static struct videobuf_queue_ops video_qops = {
. buf_setup = vpif_buffer_setup ,
. buf_prepare = vpif_buffer_prepare ,
. buf_queue = vpif_buffer_queue ,
. buf_release = vpif_buffer_release ,
} ;
static u8 channel_first_int [ VPIF_NUMOBJECTS ] [ 2 ] = { { 1 , 1 } } ;
static void process_progressive_mode ( struct common_obj * common )
{
unsigned long addr = 0 ;
/* Get the next buffer from buffer queue */
common - > next_frm = list_entry ( common - > dma_queue . next ,
struct videobuf_buffer , queue ) ;
/* Remove that buffer from the buffer queue */
list_del ( & common - > next_frm - > queue ) ;
/* Mark status of the buffer as active */
common - > next_frm - > state = VIDEOBUF_ACTIVE ;
/* Set top and bottom field addrs in VPIF registers */
addr = videobuf_to_dma_contig ( common - > next_frm ) ;
common - > set_addr ( addr + common - > ytop_off ,
addr + common - > ybtm_off ,
addr + common - > ctop_off ,
addr + common - > cbtm_off ) ;
}
static void process_interlaced_mode ( int fid , struct common_obj * common )
{
/* device field id and local field id are in sync */
/* If this is even field */
if ( 0 = = fid ) {
if ( common - > cur_frm = = common - > next_frm )
return ;
/* one frame is displayed If next frame is
* available , release cur_frm and move on */
/* Copy frame display time */
do_gettimeofday ( & common - > cur_frm - > ts ) ;
/* Change status of the cur_frm */
common - > cur_frm - > state = VIDEOBUF_DONE ;
/* unlock semaphore on cur_frm */
wake_up_interruptible ( & common - > cur_frm - > done ) ;
/* Make cur_frm pointing to next_frm */
common - > cur_frm = common - > next_frm ;
} else if ( 1 = = fid ) { /* odd field */
if ( list_empty ( & common - > dma_queue )
| | ( common - > cur_frm ! = common - > next_frm ) ) {
return ;
}
/* one field is displayed configure the next
* frame if it is available else hold on current
* frame */
/* Get next from the buffer queue */
process_progressive_mode ( common ) ;
}
}
/*
* vpif_channel_isr : It changes status of the displayed buffer , takes next
* buffer from the queue and sets its address in VPIF registers
*/
static irqreturn_t vpif_channel_isr ( int irq , void * dev_id )
{
struct vpif_device * dev = & vpif_obj ;
struct channel_obj * ch ;
struct common_obj * common ;
enum v4l2_field field ;
int fid = - 1 , i ;
int channel_id = 0 ;
channel_id = * ( int * ) ( dev_id ) ;
ch = dev - > dev [ channel_id ] ;
field = ch - > common [ VPIF_VIDEO_INDEX ] . fmt . fmt . pix . field ;
for ( i = 0 ; i < VPIF_NUMOBJECTS ; i + + ) {
common = & ch - > common [ i ] ;
/* If streaming is started in this channel */
if ( 0 = = common - > started )
continue ;
if ( 1 = = ch - > vpifparams . std_info . frm_fmt ) {
if ( list_empty ( & common - > dma_queue ) )
continue ;
/* Progressive mode */
if ( ! channel_first_int [ i ] [ channel_id ] ) {
/* Mark status of the cur_frm to
* done and unlock semaphore on it */
do_gettimeofday ( & common - > cur_frm - > ts ) ;
common - > cur_frm - > state = VIDEOBUF_DONE ;
wake_up_interruptible ( & common - > cur_frm - > done ) ;
/* Make cur_frm pointing to next_frm */
common - > cur_frm = common - > next_frm ;
}
channel_first_int [ i ] [ channel_id ] = 0 ;
process_progressive_mode ( common ) ;
} else {
/* Interlaced mode */
/* If it is first interrupt, ignore it */
if ( channel_first_int [ i ] [ channel_id ] ) {
channel_first_int [ i ] [ channel_id ] = 0 ;
continue ;
}
if ( 0 = = i ) {
ch - > field_id ^ = 1 ;
/* Get field id from VPIF registers */
fid = vpif_channel_getfid ( ch - > channel_id + 2 ) ;
/* If fid does not match with stored field id */
if ( fid ! = ch - > field_id ) {
/* Make them in sync */
if ( 0 = = fid )
ch - > field_id = fid ;
return IRQ_HANDLED ;
}
}
process_interlaced_mode ( fid , common ) ;
}
}
return IRQ_HANDLED ;
}
static int vpif_get_std_info ( struct channel_obj * ch )
{
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct video_obj * vid_ch = & ch - > video ;
struct vpif_params * vpifparams = & ch - > vpifparams ;
struct vpif_channel_config_params * std_info = & vpifparams - > std_info ;
const struct vpif_channel_config_params * config ;
int index ;
std_info - > stdid = vid_ch - > stdid ;
if ( ! std_info )
return - 1 ;
for ( index = 0 ; index < ARRAY_SIZE ( ch_params ) ; index + + ) {
config = & ch_params [ index ] ;
if ( config - > stdid & std_info - > stdid ) {
memcpy ( std_info , config , sizeof ( * config ) ) ;
break ;
}
}
if ( index = = ARRAY_SIZE ( ch_params ) )
return - 1 ;
common - > fmt . fmt . pix . width = std_info - > width ;
common - > fmt . fmt . pix . height = std_info - > height ;
vpif_dbg ( 1 , debug , " Pixel details: Width = %d,Height = %d \n " ,
common - > fmt . fmt . pix . width , common - > fmt . fmt . pix . height ) ;
/* Set height and width paramateres */
ch - > common [ VPIF_VIDEO_INDEX ] . height = std_info - > height ;
ch - > common [ VPIF_VIDEO_INDEX ] . width = std_info - > width ;
return 0 ;
}
/*
* vpif_calculate_offsets : This function calculates buffers offset for Y and C
* in the top and bottom field
*/
static void vpif_calculate_offsets ( struct channel_obj * ch )
{
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct vpif_params * vpifparams = & ch - > vpifparams ;
enum v4l2_field field = common - > fmt . fmt . pix . field ;
struct video_obj * vid_ch = & ch - > video ;
unsigned int hpitch , vpitch , sizeimage ;
if ( V4L2_FIELD_ANY = = common - > fmt . fmt . pix . field ) {
if ( ch - > vpifparams . std_info . frm_fmt )
vid_ch - > buf_field = V4L2_FIELD_NONE ;
else
vid_ch - > buf_field = V4L2_FIELD_INTERLACED ;
} else {
vid_ch - > buf_field = common - > fmt . fmt . pix . field ;
}
if ( V4L2_MEMORY_USERPTR = = common - > memory )
sizeimage = common - > fmt . fmt . pix . sizeimage ;
else
sizeimage = config_params . channel_bufsize [ ch - > channel_id ] ;
hpitch = common - > fmt . fmt . pix . bytesperline ;
vpitch = sizeimage / ( hpitch * 2 ) ;
if ( ( V4L2_FIELD_NONE = = vid_ch - > buf_field ) | |
( V4L2_FIELD_INTERLACED = = vid_ch - > buf_field ) ) {
common - > ytop_off = 0 ;
common - > ybtm_off = hpitch ;
common - > ctop_off = sizeimage / 2 ;
common - > cbtm_off = sizeimage / 2 + hpitch ;
} else if ( V4L2_FIELD_SEQ_TB = = vid_ch - > buf_field ) {
common - > ytop_off = 0 ;
common - > ybtm_off = sizeimage / 4 ;
common - > ctop_off = sizeimage / 2 ;
common - > cbtm_off = common - > ctop_off + sizeimage / 4 ;
} else if ( V4L2_FIELD_SEQ_BT = = vid_ch - > buf_field ) {
common - > ybtm_off = 0 ;
common - > ytop_off = sizeimage / 4 ;
common - > cbtm_off = sizeimage / 2 ;
common - > ctop_off = common - > cbtm_off + sizeimage / 4 ;
}
if ( ( V4L2_FIELD_NONE = = vid_ch - > buf_field ) | |
( V4L2_FIELD_INTERLACED = = vid_ch - > buf_field ) ) {
vpifparams - > video_params . storage_mode = 1 ;
} else {
vpifparams - > video_params . storage_mode = 0 ;
}
if ( ch - > vpifparams . std_info . frm_fmt = = 1 ) {
vpifparams - > video_params . hpitch =
common - > fmt . fmt . pix . bytesperline ;
} else {
if ( ( field = = V4L2_FIELD_ANY ) | |
( field = = V4L2_FIELD_INTERLACED ) )
vpifparams - > video_params . hpitch =
common - > fmt . fmt . pix . bytesperline * 2 ;
else
vpifparams - > video_params . hpitch =
common - > fmt . fmt . pix . bytesperline ;
}
ch - > vpifparams . video_params . stdid = ch - > vpifparams . std_info . stdid ;
}
static void vpif_config_format ( struct channel_obj * ch )
{
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
common - > fmt . fmt . pix . field = V4L2_FIELD_ANY ;
if ( config_params . numbuffers [ ch - > channel_id ] = = 0 )
common - > memory = V4L2_MEMORY_USERPTR ;
else
common - > memory = V4L2_MEMORY_MMAP ;
common - > fmt . fmt . pix . sizeimage =
config_params . channel_bufsize [ ch - > channel_id ] ;
common - > fmt . fmt . pix . pixelformat = V4L2_PIX_FMT_YUV422P ;
common - > fmt . type = V4L2_BUF_TYPE_VIDEO_OUTPUT ;
}
static int vpif_check_format ( struct channel_obj * ch ,
struct v4l2_pix_format * pixfmt )
{
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
enum v4l2_field field = pixfmt - > field ;
u32 sizeimage , hpitch , vpitch ;
if ( pixfmt - > pixelformat ! = V4L2_PIX_FMT_YUV422P )
goto invalid_fmt_exit ;
if ( ! ( VPIF_VALID_FIELD ( field ) ) )
goto invalid_fmt_exit ;
if ( pixfmt - > bytesperline < = 0 )
goto invalid_pitch_exit ;
if ( V4L2_MEMORY_USERPTR = = common - > memory )
sizeimage = pixfmt - > sizeimage ;
else
sizeimage = config_params . channel_bufsize [ ch - > channel_id ] ;
if ( vpif_get_std_info ( ch ) ) {
vpif_err ( " Error getting the standard info \n " ) ;
return - EINVAL ;
}
hpitch = pixfmt - > bytesperline ;
vpitch = sizeimage / ( hpitch * 2 ) ;
/* Check for valid value of pitch */
if ( ( hpitch < ch - > vpifparams . std_info . width ) | |
( vpitch < ch - > vpifparams . std_info . height ) )
goto invalid_pitch_exit ;
/* Check for 8 byte alignment */
if ( ! ISALIGNED ( hpitch ) ) {
vpif_err ( " invalid pitch alignment \n " ) ;
return - EINVAL ;
}
pixfmt - > width = common - > fmt . fmt . pix . width ;
pixfmt - > height = common - > fmt . fmt . pix . height ;
return 0 ;
invalid_fmt_exit :
vpif_err ( " invalid field format \n " ) ;
return - EINVAL ;
invalid_pitch_exit :
vpif_err ( " invalid pitch \n " ) ;
return - EINVAL ;
}
static void vpif_config_addr ( struct channel_obj * ch , int muxmode )
{
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id ) {
common - > set_addr = ch3_set_videobuf_addr ;
} else {
if ( 2 = = muxmode )
common - > set_addr = ch2_set_videobuf_addr_yc_nmux ;
else
common - > set_addr = ch2_set_videobuf_addr ;
}
}
/*
* vpif_mmap : It is used to map kernel space buffers into user spaces
*/
static int vpif_mmap ( struct file * filep , struct vm_area_struct * vma )
{
struct vpif_fh * fh = filep - > private_data ;
struct common_obj * common = & fh - > channel - > common [ VPIF_VIDEO_INDEX ] ;
return videobuf_mmap_mapper ( & common - > buffer_queue , vma ) ;
}
/*
* vpif_poll : It is used for select / poll system call
*/
static unsigned int vpif_poll ( struct file * filep , poll_table * wait )
{
struct vpif_fh * fh = filep - > private_data ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( common - > started )
return videobuf_poll_stream ( filep , & common - > buffer_queue , wait ) ;
return 0 ;
}
/*
* vpif_open : It creates object of file handle structure and stores it in
* private_data member of filepointer
*/
static int vpif_open ( struct file * filep )
{
struct video_device * vdev = video_devdata ( filep ) ;
struct channel_obj * ch = NULL ;
struct vpif_fh * fh = NULL ;
ch = video_get_drvdata ( vdev ) ;
/* Allocate memory for the file handle object */
fh = kmalloc ( sizeof ( struct vpif_fh ) , GFP_KERNEL ) ;
if ( fh = = NULL ) {
vpif_err ( " unable to allocate memory for file handle object \n " ) ;
return - ENOMEM ;
}
/* store pointer to fh in private_data member of filep */
filep - > private_data = fh ;
fh - > channel = ch ;
fh - > initialized = 0 ;
if ( ! ch - > initialized ) {
fh - > initialized = 1 ;
ch - > initialized = 1 ;
memset ( & ch - > vpifparams , 0 , sizeof ( ch - > vpifparams ) ) ;
}
/* Increment channel usrs counter */
atomic_inc ( & ch - > usrs ) ;
/* Set io_allowed[VPIF_VIDEO_INDEX] member to false */
fh - > io_allowed [ VPIF_VIDEO_INDEX ] = 0 ;
/* Initialize priority of this instance to default priority */
fh - > prio = V4L2_PRIORITY_UNSET ;
v4l2_prio_open ( & ch - > prio , & fh - > prio ) ;
return 0 ;
}
/*
* vpif_release : This function deletes buffer queue , frees the buffers and
* the vpif file handle
*/
static int vpif_release ( struct file * filep )
{
struct vpif_fh * fh = filep - > private_data ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
2009-06-22 09:02:55 -03:00
if ( mutex_lock_interruptible ( & common - > lock ) )
return - ERESTARTSYS ;
2009-06-09 05:55:37 -03:00
/* if this instance is doing IO */
if ( fh - > io_allowed [ VPIF_VIDEO_INDEX ] ) {
/* Reset io_usrs member of channel object */
common - > io_usrs = 0 ;
/* Disable channel */
if ( VPIF_CHANNEL2_VIDEO = = ch - > channel_id ) {
enable_channel2 ( 0 ) ;
channel2_intr_enable ( 0 ) ;
}
if ( ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id ) | |
( 2 = = common - > started ) ) {
enable_channel3 ( 0 ) ;
channel3_intr_enable ( 0 ) ;
}
common - > started = 0 ;
/* Free buffers allocated */
videobuf_queue_cancel ( & common - > buffer_queue ) ;
videobuf_mmap_free ( & common - > buffer_queue ) ;
common - > numbuffers =
config_params . numbuffers [ ch - > channel_id ] ;
}
mutex_unlock ( & common - > lock ) ;
/* Decrement channel usrs counter */
atomic_dec ( & ch - > usrs ) ;
/* If this file handle has initialize encoder device, reset it */
if ( fh - > initialized )
ch - > initialized = 0 ;
/* Close the priority */
v4l2_prio_close ( & ch - > prio , & fh - > prio ) ;
filep - > private_data = NULL ;
fh - > initialized = 0 ;
kfree ( fh ) ;
return 0 ;
}
/* functions implementing ioctls */
static int vpif_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
2009-09-16 14:30:53 -03:00
struct vpif_display_config * config = vpif_dev - > platform_data ;
2009-06-09 05:55:37 -03:00
cap - > version = VPIF_DISPLAY_VERSION_CODE ;
cap - > capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING ;
strlcpy ( cap - > driver , " vpif display " , sizeof ( cap - > driver ) ) ;
strlcpy ( cap - > bus_info , " Platform " , sizeof ( cap - > bus_info ) ) ;
strlcpy ( cap - > card , config - > card_name , sizeof ( cap - > card ) ) ;
return 0 ;
}
static int vpif_enum_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_fmtdesc * fmt )
{
if ( fmt - > index ! = 0 ) {
vpif_err ( " Invalid format index \n " ) ;
return - EINVAL ;
}
/* Fill in the information about format */
fmt - > type = V4L2_BUF_TYPE_VIDEO_OUTPUT ;
strcpy ( fmt - > description , " YCbCr4:2:2 YC Planar " ) ;
fmt - > pixelformat = V4L2_PIX_FMT_YUV422P ;
return 0 ;
}
static int vpif_g_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
/* Check the validity of the buffer type */
if ( common - > fmt . type ! = fmt - > type )
return - EINVAL ;
/* Fill in the information about format */
2009-06-22 09:02:55 -03:00
if ( mutex_lock_interruptible ( & common - > lock ) )
return - ERESTARTSYS ;
2009-06-09 05:55:37 -03:00
if ( vpif_get_std_info ( ch ) ) {
vpif_err ( " Error getting the standard info \n " ) ;
return - EINVAL ;
}
* fmt = common - > fmt ;
mutex_unlock ( & common - > lock ) ;
return 0 ;
}
static int vpif_s_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
{
struct vpif_fh * fh = priv ;
struct v4l2_pix_format * pixfmt ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
int ret = 0 ;
if ( ( VPIF_CHANNEL2_VIDEO = = ch - > channel_id )
| | ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id ) ) {
if ( ! fh - > initialized ) {
vpif_dbg ( 1 , debug , " Channel Busy \n " ) ;
return - EBUSY ;
}
/* Check for the priority */
ret = v4l2_prio_check ( & ch - > prio , & fh - > prio ) ;
if ( 0 ! = ret )
return ret ;
fh - > initialized = 1 ;
}
if ( common - > started ) {
vpif_dbg ( 1 , debug , " Streaming in progress \n " ) ;
return - EBUSY ;
}
pixfmt = & fmt - > fmt . pix ;
/* Check for valid field format */
ret = vpif_check_format ( ch , pixfmt ) ;
if ( ret )
return ret ;
/* store the pix format in the channel object */
common - > fmt . fmt . pix = * pixfmt ;
/* store the format in the channel object */
2009-06-22 09:02:55 -03:00
if ( mutex_lock_interruptible ( & common - > lock ) )
return - ERESTARTSYS ;
2009-06-09 05:55:37 -03:00
common - > fmt = * fmt ;
mutex_unlock ( & common - > lock ) ;
return 0 ;
}
static int vpif_try_fmt_vid_out ( struct file * file , void * priv ,
struct v4l2_format * fmt )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct v4l2_pix_format * pixfmt = & fmt - > fmt . pix ;
int ret = 0 ;
ret = vpif_check_format ( ch , pixfmt ) ;
if ( ret ) {
* pixfmt = common - > fmt . fmt . pix ;
pixfmt - > sizeimage = pixfmt - > width * pixfmt - > height * 2 ;
}
return ret ;
}
static int vpif_reqbufs ( struct file * file , void * priv ,
struct v4l2_requestbuffers * reqbuf )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common ;
enum v4l2_field field ;
u8 index = 0 ;
int ret = 0 ;
/* This file handle has not initialized the channel,
It is not allowed to do settings */
if ( ( VPIF_CHANNEL2_VIDEO = = ch - > channel_id )
| | ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id ) ) {
if ( ! fh - > initialized ) {
vpif_err ( " Channel Busy \n " ) ;
return - EBUSY ;
}
}
if ( V4L2_BUF_TYPE_VIDEO_OUTPUT ! = reqbuf - > type )
return - EINVAL ;
index = VPIF_VIDEO_INDEX ;
common = & ch - > common [ index ] ;
2009-06-22 09:02:55 -03:00
if ( mutex_lock_interruptible ( & common - > lock ) )
return - ERESTARTSYS ;
2009-06-09 05:55:37 -03:00
if ( common - > fmt . type ! = reqbuf - > type ) {
ret = - EINVAL ;
goto reqbuf_exit ;
}
if ( 0 ! = common - > io_usrs ) {
ret = - EBUSY ;
goto reqbuf_exit ;
}
if ( reqbuf - > type = = V4L2_BUF_TYPE_VIDEO_OUTPUT ) {
if ( common - > fmt . fmt . pix . field = = V4L2_FIELD_ANY )
field = V4L2_FIELD_INTERLACED ;
else
field = common - > fmt . fmt . pix . field ;
} else {
field = V4L2_VBI_INTERLACED ;
}
/* Initialize videobuf queue as per the buffer type */
videobuf_queue_dma_contig_init ( & common - > buffer_queue ,
& video_qops , NULL ,
& common - > irqlock ,
reqbuf - > type , field ,
sizeof ( struct videobuf_buffer ) , fh ) ;
/* Set io allowed member of file handle to TRUE */
fh - > io_allowed [ index ] = 1 ;
/* Increment io usrs member of channel object to 1 */
common - > io_usrs = 1 ;
/* Store type of memory requested in channel object */
common - > memory = reqbuf - > memory ;
INIT_LIST_HEAD ( & common - > dma_queue ) ;
/* Allocate buffers */
ret = videobuf_reqbufs ( & common - > buffer_queue , reqbuf ) ;
reqbuf_exit :
mutex_unlock ( & common - > lock ) ;
return ret ;
}
static int vpif_querybuf ( struct file * file , void * priv ,
struct v4l2_buffer * tbuf )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( common - > fmt . type ! = tbuf - > type )
return - EINVAL ;
return videobuf_querybuf ( & common - > buffer_queue , tbuf ) ;
}
static int vpif_qbuf ( struct file * file , void * priv , struct v4l2_buffer * buf )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct v4l2_buffer tbuf = * buf ;
struct videobuf_buffer * buf1 ;
unsigned long addr = 0 ;
unsigned long flags ;
int ret = 0 ;
if ( common - > fmt . type ! = tbuf . type )
return - EINVAL ;
if ( ! fh - > io_allowed [ VPIF_VIDEO_INDEX ] ) {
vpif_err ( " fh->io_allowed \n " ) ;
return - EACCES ;
}
if ( ! ( list_empty ( & common - > dma_queue ) ) | |
( common - > cur_frm ! = common - > next_frm ) | |
! ( common - > started ) | |
( common - > started & & ( 0 = = ch - > field_id ) ) )
return videobuf_qbuf ( & common - > buffer_queue , buf ) ;
/* bufferqueue is empty store buffer address in VPIF registers */
mutex_lock ( & common - > buffer_queue . vb_lock ) ;
buf1 = common - > buffer_queue . bufs [ tbuf . index ] ;
if ( buf1 - > memory ! = tbuf . memory ) {
vpif_err ( " invalid buffer type \n " ) ;
goto qbuf_exit ;
}
if ( ( buf1 - > state = = VIDEOBUF_QUEUED ) | |
( buf1 - > state = = VIDEOBUF_ACTIVE ) ) {
vpif_err ( " invalid state \n " ) ;
goto qbuf_exit ;
}
switch ( buf1 - > memory ) {
case V4L2_MEMORY_MMAP :
if ( buf1 - > baddr = = 0 )
goto qbuf_exit ;
break ;
case V4L2_MEMORY_USERPTR :
if ( tbuf . length < buf1 - > bsize )
goto qbuf_exit ;
if ( ( VIDEOBUF_NEEDS_INIT ! = buf1 - > state )
& & ( buf1 - > baddr ! = tbuf . m . userptr ) )
vpif_buffer_release ( & common - > buffer_queue , buf1 ) ;
buf1 - > baddr = tbuf . m . userptr ;
break ;
default :
goto qbuf_exit ;
}
local_irq_save ( flags ) ;
ret = vpif_buffer_prepare ( & common - > buffer_queue , buf1 ,
common - > buffer_queue . field ) ;
if ( ret < 0 ) {
local_irq_restore ( flags ) ;
goto qbuf_exit ;
}
buf1 - > state = VIDEOBUF_ACTIVE ;
addr = buf1 - > boff ;
common - > next_frm = buf1 ;
if ( tbuf . type ! = V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ) {
common - > set_addr ( ( addr + common - > ytop_off ) ,
( addr + common - > ybtm_off ) ,
( addr + common - > ctop_off ) ,
( addr + common - > cbtm_off ) ) ;
}
local_irq_restore ( flags ) ;
list_add_tail ( & buf1 - > stream , & common - > buffer_queue . stream ) ;
mutex_unlock ( & common - > buffer_queue . vb_lock ) ;
return 0 ;
qbuf_exit :
mutex_unlock ( & common - > buffer_queue . vb_lock ) ;
return - EINVAL ;
}
static int vpif_s_std ( struct file * file , void * priv , v4l2_std_id * std_id )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
int ret = 0 ;
if ( ! ( * std_id & DM646X_V4L2_STD ) )
return - EINVAL ;
if ( common - > started ) {
vpif_err ( " streaming in progress \n " ) ;
return - EBUSY ;
}
/* Call encoder subdevice function to set the standard */
2009-06-22 09:02:55 -03:00
if ( mutex_lock_interruptible ( & common - > lock ) )
return - ERESTARTSYS ;
2009-06-09 05:55:37 -03:00
ch - > video . stdid = * std_id ;
/* Get the information about the standard */
if ( vpif_get_std_info ( ch ) ) {
vpif_err ( " Error getting the standard info \n " ) ;
return - EINVAL ;
}
if ( ( ch - > vpifparams . std_info . width *
ch - > vpifparams . std_info . height * 2 ) >
config_params . channel_bufsize [ ch - > channel_id ] ) {
vpif_err ( " invalid std for this size \n " ) ;
ret = - EINVAL ;
goto s_std_exit ;
}
common - > fmt . fmt . pix . bytesperline = common - > fmt . fmt . pix . width ;
/* Configure the default format information */
vpif_config_format ( ch ) ;
ret = v4l2_device_call_until_err ( & vpif_obj . v4l2_dev , 1 , video ,
s_std_output , * std_id ) ;
if ( ret < 0 ) {
vpif_err ( " Failed to set output standard \n " ) ;
goto s_std_exit ;
}
ret = v4l2_device_call_until_err ( & vpif_obj . v4l2_dev , 1 , core ,
s_std , * std_id ) ;
if ( ret < 0 )
vpif_err ( " Failed to set standard for sub devices \n " ) ;
s_std_exit :
mutex_unlock ( & common - > lock ) ;
return ret ;
}
static int vpif_g_std ( struct file * file , void * priv , v4l2_std_id * std )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
* std = ch - > video . stdid ;
return 0 ;
}
static int vpif_dqbuf ( struct file * file , void * priv , struct v4l2_buffer * p )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
return videobuf_dqbuf ( & common - > buffer_queue , p ,
( file - > f_flags & O_NONBLOCK ) ) ;
}
static int vpif_streamon ( struct file * file , void * priv ,
enum v4l2_buf_type buftype )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
struct channel_obj * oth_ch = vpif_obj . dev [ ! ch - > channel_id ] ;
struct vpif_params * vpif = & ch - > vpifparams ;
2009-09-16 14:30:53 -03:00
struct vpif_display_config * vpif_config_data =
2009-06-09 05:55:37 -03:00
vpif_dev - > platform_data ;
unsigned long addr = 0 ;
int ret = 0 ;
if ( buftype ! = V4L2_BUF_TYPE_VIDEO_OUTPUT ) {
vpif_err ( " buffer type not supported \n " ) ;
return - EINVAL ;
}
if ( ! fh - > io_allowed [ VPIF_VIDEO_INDEX ] ) {
vpif_err ( " fh->io_allowed \n " ) ;
return - EACCES ;
}
/* If Streaming is already started, return error */
if ( common - > started ) {
vpif_err ( " channel->started \n " ) ;
return - EBUSY ;
}
if ( ( ch - > channel_id = = VPIF_CHANNEL2_VIDEO
& & oth_ch - > common [ VPIF_VIDEO_INDEX ] . started & &
ch - > vpifparams . std_info . ycmux_mode = = 0 )
| | ( ( ch - > channel_id = = VPIF_CHANNEL3_VIDEO )
& & ( 2 = = oth_ch - > common [ VPIF_VIDEO_INDEX ] . started ) ) ) {
vpif_err ( " other channel is using \n " ) ;
return - EBUSY ;
}
ret = vpif_check_format ( ch , & common - > fmt . fmt . pix ) ;
if ( ret < 0 )
return ret ;
/* Call videobuf_streamon to start streaming in videobuf */
ret = videobuf_streamon ( & common - > buffer_queue ) ;
if ( ret < 0 ) {
vpif_err ( " videobuf_streamon \n " ) ;
return ret ;
}
2009-06-22 09:02:55 -03:00
if ( mutex_lock_interruptible ( & common - > lock ) )
return - ERESTARTSYS ;
2009-06-09 05:55:37 -03:00
/* If buffer queue is empty, return error */
if ( list_empty ( & common - > dma_queue ) ) {
vpif_err ( " buffer queue is empty \n " ) ;
ret = - EIO ;
goto streamon_exit ;
}
/* Get the next frame from the buffer queue */
common - > next_frm = common - > cur_frm =
list_entry ( common - > dma_queue . next ,
struct videobuf_buffer , queue ) ;
list_del ( & common - > cur_frm - > queue ) ;
/* Mark state of the current frame to active */
common - > cur_frm - > state = VIDEOBUF_ACTIVE ;
/* Initialize field_id and started member */
ch - > field_id = 0 ;
common - > started = 1 ;
if ( buftype = = V4L2_BUF_TYPE_VIDEO_OUTPUT ) {
addr = common - > cur_frm - > boff ;
/* Calculate the offset for Y and C data in the buffer */
vpif_calculate_offsets ( ch ) ;
if ( ( ch - > vpifparams . std_info . frm_fmt & &
( ( common - > fmt . fmt . pix . field ! = V4L2_FIELD_NONE )
& & ( common - > fmt . fmt . pix . field ! = V4L2_FIELD_ANY ) ) )
| | ( ! ch - > vpifparams . std_info . frm_fmt
& & ( common - > fmt . fmt . pix . field = = V4L2_FIELD_NONE ) ) ) {
vpif_err ( " conflict in field format and std format \n " ) ;
ret = - EINVAL ;
goto streamon_exit ;
}
/* clock settings */
ret =
vpif_config_data - > set_clock ( ch - > vpifparams . std_info . ycmux_mode ,
ch - > vpifparams . std_info . hd_sd ) ;
if ( ret < 0 ) {
vpif_err ( " can't set clock \n " ) ;
goto streamon_exit ;
}
/* set the parameters and addresses */
ret = vpif_set_video_params ( vpif , ch - > channel_id + 2 ) ;
if ( ret < 0 )
goto streamon_exit ;
common - > started = ret ;
vpif_config_addr ( ch , ret ) ;
common - > set_addr ( ( addr + common - > ytop_off ) ,
( addr + common - > ybtm_off ) ,
( addr + common - > ctop_off ) ,
( addr + common - > cbtm_off ) ) ;
/* Set interrupt for both the fields in VPIF
Register enable channel in VPIF register */
if ( VPIF_CHANNEL2_VIDEO = = ch - > channel_id ) {
channel2_intr_assert ( ) ;
channel2_intr_enable ( 1 ) ;
enable_channel2 ( 1 ) ;
}
if ( ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id )
| | ( common - > started = = 2 ) ) {
channel3_intr_assert ( ) ;
channel3_intr_enable ( 1 ) ;
enable_channel3 ( 1 ) ;
}
channel_first_int [ VPIF_VIDEO_INDEX ] [ ch - > channel_id ] = 1 ;
}
streamon_exit :
mutex_unlock ( & common - > lock ) ;
return ret ;
}
static int vpif_streamoff ( struct file * file , void * priv ,
enum v4l2_buf_type buftype )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( buftype ! = V4L2_BUF_TYPE_VIDEO_OUTPUT ) {
vpif_err ( " buffer type not supported \n " ) ;
return - EINVAL ;
}
if ( ! fh - > io_allowed [ VPIF_VIDEO_INDEX ] ) {
vpif_err ( " fh->io_allowed \n " ) ;
return - EACCES ;
}
if ( ! common - > started ) {
vpif_err ( " channel->started \n " ) ;
return - EINVAL ;
}
2009-06-22 09:02:55 -03:00
if ( mutex_lock_interruptible ( & common - > lock ) )
return - ERESTARTSYS ;
2009-06-09 05:55:37 -03:00
if ( buftype = = V4L2_BUF_TYPE_VIDEO_OUTPUT ) {
/* disable channel */
if ( VPIF_CHANNEL2_VIDEO = = ch - > channel_id ) {
enable_channel2 ( 0 ) ;
channel2_intr_enable ( 0 ) ;
}
if ( ( VPIF_CHANNEL3_VIDEO = = ch - > channel_id ) | |
( 2 = = common - > started ) ) {
enable_channel3 ( 0 ) ;
channel3_intr_enable ( 0 ) ;
}
}
common - > started = 0 ;
mutex_unlock ( & common - > lock ) ;
return videobuf_streamoff ( & common - > buffer_queue ) ;
}
static int vpif_cropcap ( struct file * file , void * priv ,
struct v4l2_cropcap * crop )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
if ( V4L2_BUF_TYPE_VIDEO_OUTPUT ! = crop - > type )
return - EINVAL ;
crop - > bounds . left = crop - > bounds . top = 0 ;
crop - > defrect . left = crop - > defrect . top = 0 ;
crop - > defrect . height = crop - > bounds . height = common - > height ;
crop - > defrect . width = crop - > bounds . width = common - > width ;
return 0 ;
}
static int vpif_enum_output ( struct file * file , void * fh ,
struct v4l2_output * output )
{
2009-09-16 14:30:53 -03:00
struct vpif_display_config * config = vpif_dev - > platform_data ;
2009-06-09 05:55:37 -03:00
if ( output - > index > = config - > output_count ) {
vpif_dbg ( 1 , debug , " Invalid output index \n " ) ;
return - EINVAL ;
}
strcpy ( output - > name , config - > output [ output - > index ] ) ;
output - > type = V4L2_OUTPUT_TYPE_ANALOG ;
output - > std = DM646X_V4L2_STD ;
return 0 ;
}
static int vpif_s_output ( struct file * file , void * priv , unsigned int i )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct video_obj * vid_ch = & ch - > video ;
struct common_obj * common = & ch - > common [ VPIF_VIDEO_INDEX ] ;
int ret = 0 ;
2009-06-22 09:02:55 -03:00
if ( mutex_lock_interruptible ( & common - > lock ) )
return - ERESTARTSYS ;
2009-06-09 05:55:37 -03:00
if ( common - > started ) {
vpif_err ( " Streaming in progress \n " ) ;
ret = - EBUSY ;
goto s_output_exit ;
}
ret = v4l2_device_call_until_err ( & vpif_obj . v4l2_dev , 1 , video ,
s_routing , 0 , i , 0 ) ;
if ( ret < 0 )
vpif_err ( " Failed to set output standard \n " ) ;
vid_ch - > output_id = i ;
s_output_exit :
mutex_unlock ( & common - > lock ) ;
return ret ;
}
static int vpif_g_output ( struct file * file , void * priv , unsigned int * i )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
struct video_obj * vid_ch = & ch - > video ;
* i = vid_ch - > output_id ;
return 0 ;
}
static int vpif_g_priority ( struct file * file , void * priv , enum v4l2_priority * p )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
* p = v4l2_prio_max ( & ch - > prio ) ;
return 0 ;
}
static int vpif_s_priority ( struct file * file , void * priv , enum v4l2_priority p )
{
struct vpif_fh * fh = priv ;
struct channel_obj * ch = fh - > channel ;
return v4l2_prio_change ( & ch - > prio , & fh - > prio , p ) ;
}
/* vpif display ioctl operations */
static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
. vidioc_querycap = vpif_querycap ,
. vidioc_g_priority = vpif_g_priority ,
. vidioc_s_priority = vpif_s_priority ,
. vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out ,
. vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out ,
. vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out ,
. vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out ,
. vidioc_reqbufs = vpif_reqbufs ,
. vidioc_querybuf = vpif_querybuf ,
. vidioc_qbuf = vpif_qbuf ,
. vidioc_dqbuf = vpif_dqbuf ,
. vidioc_streamon = vpif_streamon ,
. vidioc_streamoff = vpif_streamoff ,
. vidioc_s_std = vpif_s_std ,
. vidioc_g_std = vpif_g_std ,
. vidioc_enum_output = vpif_enum_output ,
. vidioc_s_output = vpif_s_output ,
. vidioc_g_output = vpif_g_output ,
. vidioc_cropcap = vpif_cropcap ,
} ;
static const struct v4l2_file_operations vpif_fops = {
. owner = THIS_MODULE ,
. open = vpif_open ,
. release = vpif_release ,
. ioctl = video_ioctl2 ,
. mmap = vpif_mmap ,
. poll = vpif_poll
} ;
static struct video_device vpif_video_template = {
. name = " vpif " ,
. fops = & vpif_fops ,
. ioctl_ops = & vpif_ioctl_ops ,
. tvnorms = DM646X_V4L2_STD ,
. current_norm = V4L2_STD_625_50 ,
} ;
/*Configure the channels, buffer sizei, request irq */
static int initialize_vpif ( void )
{
int free_channel_objects_index ;
int free_buffer_channel_index ;
int free_buffer_index ;
int err = 0 , i , j ;
/* Default number of buffers should be 3 */
if ( ( ch2_numbuffers > 0 ) & &
( ch2_numbuffers < config_params . min_numbuffers ) )
ch2_numbuffers = config_params . min_numbuffers ;
if ( ( ch3_numbuffers > 0 ) & &
( ch3_numbuffers < config_params . min_numbuffers ) )
ch3_numbuffers = config_params . min_numbuffers ;
/* Set buffer size to min buffers size if invalid buffer size is
* given */
if ( ch2_bufsize < config_params . min_bufsize [ VPIF_CHANNEL2_VIDEO ] )
ch2_bufsize =
config_params . min_bufsize [ VPIF_CHANNEL2_VIDEO ] ;
if ( ch3_bufsize < config_params . min_bufsize [ VPIF_CHANNEL3_VIDEO ] )
ch3_bufsize =
config_params . min_bufsize [ VPIF_CHANNEL3_VIDEO ] ;
config_params . numbuffers [ VPIF_CHANNEL2_VIDEO ] = ch2_numbuffers ;
if ( ch2_numbuffers ) {
config_params . channel_bufsize [ VPIF_CHANNEL2_VIDEO ] =
ch2_bufsize ;
}
config_params . numbuffers [ VPIF_CHANNEL3_VIDEO ] = ch3_numbuffers ;
if ( ch3_numbuffers ) {
config_params . channel_bufsize [ VPIF_CHANNEL3_VIDEO ] =
ch3_bufsize ;
}
/* Allocate memory for six channel objects */
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + ) {
vpif_obj . dev [ i ] =
kmalloc ( sizeof ( struct channel_obj ) , GFP_KERNEL ) ;
/* If memory allocation fails, return error */
if ( ! vpif_obj . dev [ i ] ) {
free_channel_objects_index = i ;
err = - ENOMEM ;
goto vpif_init_free_channel_objects ;
}
}
free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES ;
free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS ;
free_buffer_index = config_params . numbuffers [ i - 1 ] ;
return 0 ;
vpif_init_free_channel_objects :
for ( j = 0 ; j < free_channel_objects_index ; j + + )
kfree ( vpif_obj . dev [ j ] ) ;
return err ;
}
/*
* vpif_probe : This function creates device entries by register itself to the
* V4L2 driver and initializes fields of each channel objects
*/
static __init int vpif_probe ( struct platform_device * pdev )
{
2009-09-16 14:30:53 -03:00
struct vpif_subdev_info * subdevdata ;
struct vpif_display_config * config ;
2009-06-09 05:55:37 -03:00
int i , j = 0 , k , q , m , err = 0 ;
struct i2c_adapter * i2c_adap ;
struct common_obj * common ;
struct channel_obj * ch ;
struct video_device * vfd ;
struct resource * res ;
int subdev_count ;
vpif_dev = & pdev - > dev ;
2009-09-16 14:30:53 -03:00
err = initialize_vpif ( ) ;
2009-06-09 05:55:37 -03:00
2009-09-16 14:30:53 -03:00
if ( err ) {
v4l2_err ( vpif_dev - > driver , " Error initializing vpif \n " ) ;
return err ;
2009-06-09 05:55:37 -03:00
}
err = v4l2_device_register ( vpif_dev , & vpif_obj . v4l2_dev ) ;
if ( err ) {
v4l2_err ( vpif_dev - > driver , " Error registering v4l2 device \n " ) ;
return err ;
}
k = 0 ;
while ( ( res = platform_get_resource ( pdev , IORESOURCE_IRQ , k ) ) ) {
for ( i = res - > start ; i < = res - > end ; i + + ) {
if ( request_irq ( i , vpif_channel_isr , IRQF_DISABLED ,
" DM646x_Display " ,
( void * ) ( & vpif_obj . dev [ k ] - > channel_id ) ) ) {
err = - EBUSY ;
goto vpif_int_err ;
}
}
k + + ;
}
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + ) {
/* Get the pointer to the channel object */
ch = vpif_obj . dev [ i ] ;
/* Allocate memory for video device */
vfd = video_device_alloc ( ) ;
if ( vfd = = NULL ) {
for ( j = 0 ; j < i ; j + + ) {
ch = vpif_obj . dev [ j ] ;
video_device_release ( ch - > video_dev ) ;
}
err = - ENOMEM ;
2009-09-16 14:30:53 -03:00
goto vpif_int_err ;
2009-06-09 05:55:37 -03:00
}
/* Initialize field of video device */
* vfd = vpif_video_template ;
vfd - > v4l2_dev = & vpif_obj . v4l2_dev ;
vfd - > release = video_device_release ;
snprintf ( vfd - > name , sizeof ( vfd - > name ) ,
" DM646x_VPIFDisplay_DRIVER_V%d.%d.%d " ,
( VPIF_DISPLAY_VERSION_CODE > > 16 ) & 0xff ,
( VPIF_DISPLAY_VERSION_CODE > > 8 ) & 0xff ,
( VPIF_DISPLAY_VERSION_CODE ) & 0xff ) ;
/* Set video_dev to the video device */
ch - > video_dev = vfd ;
}
for ( j = 0 ; j < VPIF_DISPLAY_MAX_DEVICES ; j + + ) {
ch = vpif_obj . dev [ j ] ;
/* Initialize field of the channel objects */
atomic_set ( & ch - > usrs , 0 ) ;
for ( k = 0 ; k < VPIF_NUMOBJECTS ; k + + ) {
ch - > common [ k ] . numbuffers = 0 ;
common = & ch - > common [ k ] ;
common - > io_usrs = 0 ;
common - > started = 0 ;
spin_lock_init ( & common - > irqlock ) ;
mutex_init ( & common - > lock ) ;
common - > numbuffers = 0 ;
common - > set_addr = NULL ;
common - > ytop_off = common - > ybtm_off = 0 ;
common - > ctop_off = common - > cbtm_off = 0 ;
common - > cur_frm = common - > next_frm = NULL ;
memset ( & common - > fmt , 0 , sizeof ( common - > fmt ) ) ;
common - > numbuffers = config_params . numbuffers [ k ] ;
}
ch - > initialized = 0 ;
ch - > channel_id = j ;
if ( j < 2 )
ch - > common [ VPIF_VIDEO_INDEX ] . numbuffers =
config_params . numbuffers [ ch - > channel_id ] ;
else
ch - > common [ VPIF_VIDEO_INDEX ] . numbuffers = 0 ;
memset ( & ch - > vpifparams , 0 , sizeof ( ch - > vpifparams ) ) ;
/* Initialize prio member of channel object */
v4l2_prio_init ( & ch - > prio ) ;
ch - > common [ VPIF_VIDEO_INDEX ] . fmt . type =
V4L2_BUF_TYPE_VIDEO_OUTPUT ;
/* register video device */
vpif_dbg ( 1 , debug , " channel=%x,channel->video_dev=%x \n " ,
( int ) ch , ( int ) & ch - > video_dev ) ;
err = video_register_device ( ch - > video_dev ,
VFL_TYPE_GRABBER , ( j ? 3 : 2 ) ) ;
if ( err < 0 )
goto probe_out ;
video_set_drvdata ( ch - > video_dev , ch ) ;
}
i2c_adap = i2c_get_adapter ( 1 ) ;
config = pdev - > dev . platform_data ;
subdev_count = config - > subdev_count ;
subdevdata = config - > subdevinfo ;
vpif_obj . sd = kmalloc ( sizeof ( struct v4l2_subdev * ) * subdev_count ,
GFP_KERNEL ) ;
if ( vpif_obj . sd = = NULL ) {
vpif_err ( " unable to allocate memory for subdevice pointers \n " ) ;
err = - ENOMEM ;
goto probe_out ;
}
for ( i = 0 ; i < subdev_count ; i + + ) {
2009-09-16 14:30:53 -03:00
vpif_obj . sd [ i ] = v4l2_i2c_new_subdev_board ( & vpif_obj . v4l2_dev ,
2009-06-09 05:55:37 -03:00
i2c_adap , subdevdata [ i ] . name ,
2009-09-16 14:30:53 -03:00
& subdevdata [ i ] . board_info ,
NULL ) ;
2009-06-09 05:55:37 -03:00
if ( ! vpif_obj . sd [ i ] ) {
vpif_err ( " Error registering v4l2 subdevice \n " ) ;
goto probe_subdev_out ;
}
if ( vpif_obj . sd [ i ] )
vpif_obj . sd [ i ] - > grp_id = 1 < < i ;
}
return 0 ;
probe_subdev_out :
kfree ( vpif_obj . sd ) ;
probe_out :
for ( k = 0 ; k < j ; k + + ) {
ch = vpif_obj . dev [ k ] ;
video_unregister_device ( ch - > video_dev ) ;
video_device_release ( ch - > video_dev ) ;
ch - > video_dev = NULL ;
}
vpif_int_err :
v4l2_device_unregister ( & vpif_obj . v4l2_dev ) ;
vpif_err ( " VPIF IRQ request failed \n " ) ;
for ( q = k ; k > = 0 ; k - - ) {
for ( m = i ; m > = res - > start ; m - - )
free_irq ( m , ( void * ) ( & vpif_obj . dev [ k ] - > channel_id ) ) ;
res = platform_get_resource ( pdev , IORESOURCE_IRQ , k - 1 ) ;
m = res - > end ;
}
return err ;
}
/*
* vpif_remove : It un - register channels from V4L2 driver
*/
static int vpif_remove ( struct platform_device * device )
{
struct channel_obj * ch ;
int i ;
v4l2_device_unregister ( & vpif_obj . v4l2_dev ) ;
/* un-register device */
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + ) {
/* Get the pointer to the channel object */
ch = vpif_obj . dev [ i ] ;
/* Unregister video device */
video_unregister_device ( ch - > video_dev ) ;
ch - > video_dev = NULL ;
}
return 0 ;
}
static struct platform_driver vpif_driver = {
. driver = {
. name = " vpif_display " ,
. owner = THIS_MODULE ,
} ,
. probe = vpif_probe ,
. remove = vpif_remove ,
} ;
static __init int vpif_init ( void )
{
return platform_driver_register ( & vpif_driver ) ;
}
/*
* vpif_cleanup : This function un - registers device and driver to the kernel ,
* frees requested irq handler and de - allocates memory allocated for channel
* objects .
*/
static void vpif_cleanup ( void )
{
struct platform_device * pdev ;
struct resource * res ;
int irq_num ;
int i = 0 ;
pdev = container_of ( vpif_dev , struct platform_device , dev ) ;
while ( ( res = platform_get_resource ( pdev , IORESOURCE_IRQ , i ) ) ) {
for ( irq_num = res - > start ; irq_num < = res - > end ; irq_num + + )
free_irq ( irq_num ,
( void * ) ( & vpif_obj . dev [ i ] - > channel_id ) ) ;
i + + ;
}
platform_driver_unregister ( & vpif_driver ) ;
kfree ( vpif_obj . sd ) ;
for ( i = 0 ; i < VPIF_DISPLAY_MAX_DEVICES ; i + + )
kfree ( vpif_obj . dev [ i ] ) ;
}
module_init ( vpif_init ) ;
module_exit ( vpif_cleanup ) ;