2016-03-02 11:30:16 -03:00
/*
* Copyright ( C ) 2015 VanguardiaSur - www . vanguardiasur . com . ar
*
* Based on original driver by Krzysztof Ha ? asa :
* Copyright ( C ) 2015 Industrial Research Institute for Automation
* and Measurements PIAP
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation .
*
*/
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <media/v4l2-common.h>
# include <media/v4l2-event.h>
2016-06-04 20:47:16 -03:00
# include <media/videobuf2-dma-contig.h>
2016-06-04 20:47:17 -03:00
# include <media/videobuf2-dma-sg.h>
2016-03-02 11:30:16 -03:00
# include <media/videobuf2-vmalloc.h>
# include "tw686x.h"
# include "tw686x-regs.h"
# define TW686X_INPUTS_PER_CH 4
# define TW686X_VIDEO_WIDTH 720
2016-04-21 03:23:58 -03:00
# define TW686X_VIDEO_HEIGHT(id) ((id & V4L2_STD_525_60) ? 480 : 576)
2016-06-28 23:17:34 -03:00
# define TW686X_MAX_FPS(id) ((id & V4L2_STD_525_60) ? 30 : 25)
2016-03-02 11:30:16 -03:00
2016-06-04 20:47:17 -03:00
# define TW686X_MAX_SG_ENTRY_SIZE 4096
# define TW686X_MAX_SG_DESC_COUNT 256 /* PAL 720x576 needs 203 4-KB pages */
# define TW686X_SG_TABLE_SIZE (TW686X_MAX_SG_DESC_COUNT * sizeof(struct tw686x_sg_desc))
2016-03-02 11:30:16 -03:00
static const struct tw686x_format formats [ ] = {
{
. fourcc = V4L2_PIX_FMT_UYVY ,
. mode = 0 ,
. depth = 16 ,
} , {
. fourcc = V4L2_PIX_FMT_RGB565 ,
. mode = 5 ,
. depth = 16 ,
} , {
. fourcc = V4L2_PIX_FMT_YUYV ,
. mode = 6 ,
. depth = 16 ,
}
} ;
2016-06-04 20:47:15 -03:00
static void tw686x_buf_done ( struct tw686x_video_channel * vc ,
unsigned int pb )
{
struct tw686x_dma_desc * desc = & vc - > dma_descs [ pb ] ;
struct tw686x_dev * dev = vc - > dev ;
struct vb2_v4l2_buffer * vb ;
struct vb2_buffer * vb2_buf ;
if ( vc - > curr_bufs [ pb ] ) {
vb = & vc - > curr_bufs [ pb ] - > vb ;
vb - > field = dev - > dma_ops - > field ;
vb - > sequence = vc - > sequence + + ;
vb2_buf = & vb - > vb2_buf ;
if ( dev - > dma_mode = = TW686X_DMA_MODE_MEMCPY )
memcpy ( vb2_plane_vaddr ( vb2_buf , 0 ) , desc - > virt ,
desc - > size ) ;
vb2_buf - > timestamp = ktime_get_ns ( ) ;
vb2_buffer_done ( vb2_buf , VB2_BUF_STATE_DONE ) ;
}
vc - > pb = ! pb ;
}
/*
* We can call this even when alloc_dma failed for the given channel
*/
static void tw686x_memcpy_dma_free ( struct tw686x_video_channel * vc ,
unsigned int pb )
{
struct tw686x_dma_desc * desc = & vc - > dma_descs [ pb ] ;
struct tw686x_dev * dev = vc - > dev ;
struct pci_dev * pci_dev ;
unsigned long flags ;
/* Check device presence. Shouldn't really happen! */
spin_lock_irqsave ( & dev - > lock , flags ) ;
pci_dev = dev - > pci_dev ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
if ( ! pci_dev ) {
WARN ( 1 , " trying to deallocate on missing device \n " ) ;
return ;
}
if ( desc - > virt ) {
pci_free_consistent ( dev - > pci_dev , desc - > size ,
desc - > virt , desc - > phys ) ;
desc - > virt = NULL ;
}
}
static int tw686x_memcpy_dma_alloc ( struct tw686x_video_channel * vc ,
unsigned int pb )
{
struct tw686x_dev * dev = vc - > dev ;
u32 reg = pb ? VDMA_B_ADDR [ vc - > ch ] : VDMA_P_ADDR [ vc - > ch ] ;
unsigned int len ;
void * virt ;
WARN ( vc - > dma_descs [ pb ] . virt ,
" Allocating buffer but previous still here \n " ) ;
len = ( vc - > width * vc - > height * vc - > format - > depth ) > > 3 ;
virt = pci_alloc_consistent ( dev - > pci_dev , len ,
& vc - > dma_descs [ pb ] . phys ) ;
if ( ! virt ) {
v4l2_err ( & dev - > v4l2_dev ,
" dma%d: unable to allocate %s-buffer \n " ,
vc - > ch , pb ? " B " : " P " ) ;
return - ENOMEM ;
}
vc - > dma_descs [ pb ] . size = len ;
vc - > dma_descs [ pb ] . virt = virt ;
reg_write ( dev , reg , vc - > dma_descs [ pb ] . phys ) ;
return 0 ;
}
static void tw686x_memcpy_buf_refill ( struct tw686x_video_channel * vc ,
unsigned int pb )
{
struct tw686x_v4l2_buf * buf ;
while ( ! list_empty ( & vc - > vidq_queued ) ) {
buf = list_first_entry ( & vc - > vidq_queued ,
struct tw686x_v4l2_buf , list ) ;
list_del ( & buf - > list ) ;
vc - > curr_bufs [ pb ] = buf ;
return ;
}
vc - > curr_bufs [ pb ] = NULL ;
}
2016-07-01 11:37:27 -03:00
static const struct tw686x_dma_ops memcpy_dma_ops = {
2016-06-04 20:47:15 -03:00
. alloc = tw686x_memcpy_dma_alloc ,
. free = tw686x_memcpy_dma_free ,
. buf_refill = tw686x_memcpy_buf_refill ,
. mem_ops = & vb2_vmalloc_memops ,
. hw_dma_mode = TW686X_FRAME_MODE ,
. field = V4L2_FIELD_INTERLACED ,
} ;
2016-06-04 20:47:16 -03:00
static void tw686x_contig_buf_refill ( struct tw686x_video_channel * vc ,
unsigned int pb )
{
struct tw686x_v4l2_buf * buf ;
while ( ! list_empty ( & vc - > vidq_queued ) ) {
u32 reg = pb ? VDMA_B_ADDR [ vc - > ch ] : VDMA_P_ADDR [ vc - > ch ] ;
dma_addr_t phys ;
buf = list_first_entry ( & vc - > vidq_queued ,
struct tw686x_v4l2_buf , list ) ;
list_del ( & buf - > list ) ;
phys = vb2_dma_contig_plane_dma_addr ( & buf - > vb . vb2_buf , 0 ) ;
reg_write ( vc - > dev , reg , phys ) ;
buf - > vb . vb2_buf . state = VB2_BUF_STATE_ACTIVE ;
vc - > curr_bufs [ pb ] = buf ;
return ;
}
vc - > curr_bufs [ pb ] = NULL ;
}
2016-07-01 11:37:27 -03:00
static const struct tw686x_dma_ops contig_dma_ops = {
2016-06-04 20:47:16 -03:00
. buf_refill = tw686x_contig_buf_refill ,
. mem_ops = & vb2_dma_contig_memops ,
. hw_dma_mode = TW686X_FRAME_MODE ,
. field = V4L2_FIELD_INTERLACED ,
} ;
2016-06-04 20:47:17 -03:00
static int tw686x_sg_desc_fill ( struct tw686x_sg_desc * descs ,
struct tw686x_v4l2_buf * buf ,
unsigned int buf_len )
{
struct sg_table * vbuf = vb2_dma_sg_plane_desc ( & buf - > vb . vb2_buf , 0 ) ;
unsigned int len , entry_len ;
struct scatterlist * sg ;
int i , count ;
/* Clear the scatter-gather table */
memset ( descs , 0 , TW686X_SG_TABLE_SIZE ) ;
count = 0 ;
for_each_sg ( vbuf - > sgl , sg , vbuf - > nents , i ) {
dma_addr_t phys = sg_dma_address ( sg ) ;
len = sg_dma_len ( sg ) ;
while ( len & & buf_len ) {
if ( count = = TW686X_MAX_SG_DESC_COUNT )
return - ENOMEM ;
entry_len = min_t ( unsigned int , len ,
TW686X_MAX_SG_ENTRY_SIZE ) ;
entry_len = min_t ( unsigned int , entry_len , buf_len ) ;
descs [ count ] . phys = cpu_to_le32 ( phys ) ;
descs [ count + + ] . flags_length =
cpu_to_le32 ( BIT ( 30 ) | entry_len ) ;
phys + = entry_len ;
len - = entry_len ;
buf_len - = entry_len ;
}
if ( ! buf_len )
return 0 ;
}
return - ENOMEM ;
}
static void tw686x_sg_buf_refill ( struct tw686x_video_channel * vc ,
unsigned int pb )
{
struct tw686x_dev * dev = vc - > dev ;
struct tw686x_v4l2_buf * buf ;
while ( ! list_empty ( & vc - > vidq_queued ) ) {
unsigned int buf_len ;
buf = list_first_entry ( & vc - > vidq_queued ,
struct tw686x_v4l2_buf , list ) ;
list_del ( & buf - > list ) ;
buf_len = ( vc - > width * vc - > height * vc - > format - > depth ) > > 3 ;
if ( tw686x_sg_desc_fill ( vc - > sg_descs [ pb ] , buf , buf_len ) ) {
v4l2_err ( & dev - > v4l2_dev ,
" dma%d: unable to fill %s-buffer \n " ,
vc - > ch , pb ? " B " : " P " ) ;
vb2_buffer_done ( & buf - > vb . vb2_buf , VB2_BUF_STATE_ERROR ) ;
continue ;
}
buf - > vb . vb2_buf . state = VB2_BUF_STATE_ACTIVE ;
vc - > curr_bufs [ pb ] = buf ;
return ;
}
vc - > curr_bufs [ pb ] = NULL ;
}
static void tw686x_sg_dma_free ( struct tw686x_video_channel * vc ,
unsigned int pb )
{
struct tw686x_dma_desc * desc = & vc - > dma_descs [ pb ] ;
struct tw686x_dev * dev = vc - > dev ;
if ( desc - > size ) {
pci_free_consistent ( dev - > pci_dev , desc - > size ,
desc - > virt , desc - > phys ) ;
desc - > virt = NULL ;
}
vc - > sg_descs [ pb ] = NULL ;
}
static int tw686x_sg_dma_alloc ( struct tw686x_video_channel * vc ,
unsigned int pb )
{
struct tw686x_dma_desc * desc = & vc - > dma_descs [ pb ] ;
struct tw686x_dev * dev = vc - > dev ;
u32 reg = pb ? DMA_PAGE_TABLE1_ADDR [ vc - > ch ] :
DMA_PAGE_TABLE0_ADDR [ vc - > ch ] ;
void * virt ;
if ( desc - > size ) {
virt = pci_alloc_consistent ( dev - > pci_dev , desc - > size ,
& desc - > phys ) ;
if ( ! virt ) {
v4l2_err ( & dev - > v4l2_dev ,
" dma%d: unable to allocate %s-buffer \n " ,
vc - > ch , pb ? " B " : " P " ) ;
return - ENOMEM ;
}
desc - > virt = virt ;
reg_write ( dev , reg , desc - > phys ) ;
} else {
virt = dev - > video_channels [ 0 ] . dma_descs [ pb ] . virt +
vc - > ch * TW686X_SG_TABLE_SIZE ;
}
vc - > sg_descs [ pb ] = virt ;
return 0 ;
}
static int tw686x_sg_setup ( struct tw686x_dev * dev )
{
unsigned int sg_table_size , pb , ch , channels ;
if ( is_second_gen ( dev ) ) {
/*
* TW6865 / TW6869 : each channel needs a pair of
* P - B descriptor tables .
*/
channels = max_channels ( dev ) ;
sg_table_size = TW686X_SG_TABLE_SIZE ;
} else {
/*
* TW6864 / TW6868 : we need to allocate a pair of
* P - B descriptor tables , common for all channels .
* Each table will be bigger than 4 KB .
*/
channels = 1 ;
sg_table_size = max_channels ( dev ) * TW686X_SG_TABLE_SIZE ;
}
for ( ch = 0 ; ch < channels ; ch + + ) {
struct tw686x_video_channel * vc = & dev - > video_channels [ ch ] ;
for ( pb = 0 ; pb < 2 ; pb + + )
vc - > dma_descs [ pb ] . size = sg_table_size ;
}
return 0 ;
}
2016-07-01 11:37:27 -03:00
static const struct tw686x_dma_ops sg_dma_ops = {
2016-06-04 20:47:17 -03:00
. setup = tw686x_sg_setup ,
. alloc = tw686x_sg_dma_alloc ,
. free = tw686x_sg_dma_free ,
. buf_refill = tw686x_sg_buf_refill ,
. mem_ops = & vb2_dma_sg_memops ,
. hw_dma_mode = TW686X_SG_MODE ,
. field = V4L2_FIELD_SEQ_TB ,
} ;
2016-06-28 23:17:34 -03:00
static const unsigned int fps_map [ 15 ] = {
/*
* bit 31 enables selecting the field control register
* bits 0 - 29 are a bitmask with fields that will be output .
* For NTSC ( and PAL - M , PAL - 60 ) , all 30 bits are used .
* For other PAL standards , only the first 25 bits are used .
*/
0x00000000 , /* output all fields */
0x80000006 , /* 2 fps (60Hz), 2 fps (50Hz) */
0x80018006 , /* 4 fps (60Hz), 4 fps (50Hz) */
0x80618006 , /* 6 fps (60Hz), 6 fps (50Hz) */
0x81818186 , /* 8 fps (60Hz), 8 fps (50Hz) */
0x86186186 , /* 10 fps (60Hz), 8 fps (50Hz) */
0x86619866 , /* 12 fps (60Hz), 10 fps (50Hz) */
0x86666666 , /* 14 fps (60Hz), 12 fps (50Hz) */
0x9999999e , /* 16 fps (60Hz), 14 fps (50Hz) */
0x99e6799e , /* 18 fps (60Hz), 16 fps (50Hz) */
0x9e79e79e , /* 20 fps (60Hz), 16 fps (50Hz) */
0x9e7e7e7e , /* 22 fps (60Hz), 18 fps (50Hz) */
0x9fe7f9fe , /* 24 fps (60Hz), 20 fps (50Hz) */
0x9ffe7ffe , /* 26 fps (60Hz), 22 fps (50Hz) */
0x9ffffffe , /* 28 fps (60Hz), 24 fps (50Hz) */
} ;
static unsigned int tw686x_real_fps ( unsigned int index , unsigned int max_fps )
2016-03-02 11:30:16 -03:00
{
2016-06-28 23:17:34 -03:00
unsigned long mask ;
2016-03-02 11:30:16 -03:00
2016-06-28 23:17:34 -03:00
if ( ! index | | index > = ARRAY_SIZE ( fps_map ) )
return max_fps ;
2016-04-23 06:21:09 -03:00
2016-06-28 23:17:34 -03:00
mask = GENMASK ( max_fps - 1 , 0 ) ;
return hweight_long ( fps_map [ index ] & mask ) ;
}
2016-03-02 11:30:16 -03:00
2016-06-28 23:17:34 -03:00
static unsigned int tw686x_fps_idx ( unsigned int fps , unsigned int max_fps )
{
unsigned int idx , real_fps ;
int delta ;
/* First guess */
idx = ( 12 + 15 * fps ) / max_fps ;
/* Minimal possible framerate is 2 frames per second */
if ( ! idx )
return 1 ;
/* Check if the difference is bigger than abs(1) and adjust */
real_fps = tw686x_real_fps ( idx , max_fps ) ;
delta = real_fps - fps ;
if ( delta < - 1 )
idx + + ;
else if ( delta > 1 )
idx - - ;
/* Max framerate */
if ( idx > = 15 )
return 0 ;
return idx ;
2016-03-02 11:30:16 -03:00
}
static void tw686x_set_framerate ( struct tw686x_video_channel * vc ,
unsigned int fps )
{
2016-06-28 23:17:34 -03:00
unsigned int i ;
2016-03-02 11:30:16 -03:00
if ( vc - > fps = = fps )
return ;
2016-06-28 23:17:34 -03:00
i = tw686x_fps_idx ( fps , TW686X_MAX_FPS ( vc - > video_standard ) ) ;
reg_write ( vc - > dev , VIDEO_FIELD_CTRL [ vc - > ch ] , fps_map [ i ] ) ;
vc - > fps = tw686x_real_fps ( i , TW686X_MAX_FPS ( vc - > video_standard ) ) ;
2016-03-02 11:30:16 -03:00
}
static const struct tw686x_format * format_by_fourcc ( unsigned int fourcc )
{
unsigned int cnt ;
for ( cnt = 0 ; cnt < ARRAY_SIZE ( formats ) ; cnt + + )
if ( formats [ cnt ] . fourcc = = fourcc )
return & formats [ cnt ] ;
return NULL ;
}
static int tw686x_queue_setup ( struct vb2_queue * vq ,
unsigned int * nbuffers , unsigned int * nplanes ,
2016-04-15 09:15:05 -03:00
unsigned int sizes [ ] , struct device * alloc_devs [ ] )
2016-03-02 11:30:16 -03:00
{
struct tw686x_video_channel * vc = vb2_get_drv_priv ( vq ) ;
unsigned int szimage =
( vc - > width * vc - > height * vc - > format - > depth ) > > 3 ;
/*
* Let ' s request at least three buffers : two for the
* DMA engine and one for userspace .
*/
if ( vq - > num_buffers + * nbuffers < 3 )
* nbuffers = 3 - vq - > num_buffers ;
if ( * nplanes ) {
if ( * nplanes ! = 1 | | sizes [ 0 ] < szimage )
return - EINVAL ;
return 0 ;
}
sizes [ 0 ] = szimage ;
* nplanes = 1 ;
return 0 ;
}
static void tw686x_buf_queue ( struct vb2_buffer * vb )
{
struct tw686x_video_channel * vc = vb2_get_drv_priv ( vb - > vb2_queue ) ;
struct tw686x_dev * dev = vc - > dev ;
struct pci_dev * pci_dev ;
unsigned long flags ;
struct vb2_v4l2_buffer * vbuf = to_vb2_v4l2_buffer ( vb ) ;
struct tw686x_v4l2_buf * buf =
container_of ( vbuf , struct tw686x_v4l2_buf , vb ) ;
/* Check device presence */
spin_lock_irqsave ( & dev - > lock , flags ) ;
pci_dev = dev - > pci_dev ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
if ( ! pci_dev ) {
vb2_buffer_done ( & buf - > vb . vb2_buf , VB2_BUF_STATE_ERROR ) ;
return ;
}
spin_lock_irqsave ( & vc - > qlock , flags ) ;
list_add_tail ( & buf - > list , & vc - > vidq_queued ) ;
spin_unlock_irqrestore ( & vc - > qlock , flags ) ;
}
static void tw686x_clear_queue ( struct tw686x_video_channel * vc ,
enum vb2_buffer_state state )
{
unsigned int pb ;
while ( ! list_empty ( & vc - > vidq_queued ) ) {
struct tw686x_v4l2_buf * buf ;
buf = list_first_entry ( & vc - > vidq_queued ,
struct tw686x_v4l2_buf , list ) ;
list_del ( & buf - > list ) ;
vb2_buffer_done ( & buf - > vb . vb2_buf , state ) ;
}
for ( pb = 0 ; pb < 2 ; pb + + ) {
if ( vc - > curr_bufs [ pb ] )
vb2_buffer_done ( & vc - > curr_bufs [ pb ] - > vb . vb2_buf , state ) ;
vc - > curr_bufs [ pb ] = NULL ;
}
}
static int tw686x_start_streaming ( struct vb2_queue * vq , unsigned int count )
{
struct tw686x_video_channel * vc = vb2_get_drv_priv ( vq ) ;
struct tw686x_dev * dev = vc - > dev ;
struct pci_dev * pci_dev ;
unsigned long flags ;
int pb , err ;
/* Check device presence */
spin_lock_irqsave ( & dev - > lock , flags ) ;
pci_dev = dev - > pci_dev ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
if ( ! pci_dev ) {
err = - ENODEV ;
goto err_clear_queue ;
}
spin_lock_irqsave ( & vc - > qlock , flags ) ;
/* Sanity check */
2016-06-04 20:47:15 -03:00
if ( dev - > dma_mode = = TW686X_DMA_MODE_MEMCPY & &
( ! vc - > dma_descs [ 0 ] . virt | | ! vc - > dma_descs [ 1 ] . virt ) ) {
2016-03-02 11:30:16 -03:00
spin_unlock_irqrestore ( & vc - > qlock , flags ) ;
v4l2_err ( & dev - > v4l2_dev ,
" video%d: refusing to start without DMA buffers \n " ,
vc - > num ) ;
err = - ENOMEM ;
goto err_clear_queue ;
}
for ( pb = 0 ; pb < 2 ; pb + + )
2016-06-04 20:47:15 -03:00
dev - > dma_ops - > buf_refill ( vc , pb ) ;
2016-03-02 11:30:16 -03:00
spin_unlock_irqrestore ( & vc - > qlock , flags ) ;
vc - > sequence = 0 ;
vc - > pb = 0 ;
spin_lock_irqsave ( & dev - > lock , flags ) ;
tw686x_enable_channel ( dev , vc - > ch ) ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
mod_timer ( & dev - > dma_delay_timer , jiffies + msecs_to_jiffies ( 100 ) ) ;
return 0 ;
err_clear_queue :
spin_lock_irqsave ( & vc - > qlock , flags ) ;
tw686x_clear_queue ( vc , VB2_BUF_STATE_QUEUED ) ;
spin_unlock_irqrestore ( & vc - > qlock , flags ) ;
return err ;
}
static void tw686x_stop_streaming ( struct vb2_queue * vq )
{
struct tw686x_video_channel * vc = vb2_get_drv_priv ( vq ) ;
struct tw686x_dev * dev = vc - > dev ;
struct pci_dev * pci_dev ;
unsigned long flags ;
/* Check device presence */
spin_lock_irqsave ( & dev - > lock , flags ) ;
pci_dev = dev - > pci_dev ;
spin_unlock_irqrestore ( & dev - > lock , flags ) ;
if ( pci_dev )
tw686x_disable_channel ( dev , vc - > ch ) ;
spin_lock_irqsave ( & vc - > qlock , flags ) ;
tw686x_clear_queue ( vc , VB2_BUF_STATE_ERROR ) ;
spin_unlock_irqrestore ( & vc - > qlock , flags ) ;
}
static int tw686x_buf_prepare ( struct vb2_buffer * vb )
{
struct tw686x_video_channel * vc = vb2_get_drv_priv ( vb - > vb2_queue ) ;
unsigned int size =
( vc - > width * vc - > height * vc - > format - > depth ) > > 3 ;
if ( vb2_plane_size ( vb , 0 ) < size )
return - EINVAL ;
vb2_set_plane_payload ( vb , 0 , size ) ;
return 0 ;
}
static struct vb2_ops tw686x_video_qops = {
. queue_setup = tw686x_queue_setup ,
. buf_queue = tw686x_buf_queue ,
. buf_prepare = tw686x_buf_prepare ,
. start_streaming = tw686x_start_streaming ,
. stop_streaming = tw686x_stop_streaming ,
. wait_prepare = vb2_ops_wait_prepare ,
. wait_finish = vb2_ops_wait_finish ,
} ;
static int tw686x_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct tw686x_video_channel * vc ;
struct tw686x_dev * dev ;
unsigned int ch ;
vc = container_of ( ctrl - > handler , struct tw686x_video_channel ,
ctrl_handler ) ;
dev = vc - > dev ;
ch = vc - > ch ;
switch ( ctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
reg_write ( dev , BRIGHT [ ch ] , ctrl - > val & 0xff ) ;
return 0 ;
case V4L2_CID_CONTRAST :
reg_write ( dev , CONTRAST [ ch ] , ctrl - > val ) ;
return 0 ;
case V4L2_CID_SATURATION :
reg_write ( dev , SAT_U [ ch ] , ctrl - > val ) ;
reg_write ( dev , SAT_V [ ch ] , ctrl - > val ) ;
return 0 ;
case V4L2_CID_HUE :
reg_write ( dev , HUE [ ch ] , ctrl - > val & 0xff ) ;
return 0 ;
}
return - EINVAL ;
}
static const struct v4l2_ctrl_ops ctrl_ops = {
. s_ctrl = tw686x_s_ctrl ,
} ;
static int tw686x_g_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
2016-06-04 20:47:15 -03:00
struct tw686x_dev * dev = vc - > dev ;
2016-03-02 11:30:16 -03:00
f - > fmt . pix . width = vc - > width ;
f - > fmt . pix . height = vc - > height ;
2016-06-04 20:47:15 -03:00
f - > fmt . pix . field = dev - > dma_ops - > field ;
2016-03-02 11:30:16 -03:00
f - > fmt . pix . pixelformat = vc - > format - > fourcc ;
f - > fmt . pix . colorspace = V4L2_COLORSPACE_SMPTE170M ;
f - > fmt . pix . bytesperline = ( f - > fmt . pix . width * vc - > format - > depth ) / 8 ;
f - > fmt . pix . sizeimage = f - > fmt . pix . height * f - > fmt . pix . bytesperline ;
return 0 ;
}
static int tw686x_try_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
2016-06-04 20:47:15 -03:00
struct tw686x_dev * dev = vc - > dev ;
2016-03-02 11:30:16 -03:00
unsigned int video_height = TW686X_VIDEO_HEIGHT ( vc - > video_standard ) ;
const struct tw686x_format * format ;
format = format_by_fourcc ( f - > fmt . pix . pixelformat ) ;
if ( ! format ) {
format = & formats [ 0 ] ;
f - > fmt . pix . pixelformat = format - > fourcc ;
}
if ( f - > fmt . pix . width < = TW686X_VIDEO_WIDTH / 2 )
f - > fmt . pix . width = TW686X_VIDEO_WIDTH / 2 ;
else
f - > fmt . pix . width = TW686X_VIDEO_WIDTH ;
if ( f - > fmt . pix . height < = video_height / 2 )
f - > fmt . pix . height = video_height / 2 ;
else
f - > fmt . pix . height = video_height ;
f - > fmt . pix . bytesperline = ( f - > fmt . pix . width * format - > depth ) / 8 ;
f - > fmt . pix . sizeimage = f - > fmt . pix . height * f - > fmt . pix . bytesperline ;
f - > fmt . pix . colorspace = V4L2_COLORSPACE_SMPTE170M ;
2016-06-04 20:47:15 -03:00
f - > fmt . pix . field = dev - > dma_ops - > field ;
2016-03-02 11:30:16 -03:00
return 0 ;
}
static int tw686x_s_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
2016-06-04 20:47:15 -03:00
struct tw686x_dev * dev = vc - > dev ;
2016-03-02 11:30:16 -03:00
u32 val , width , line_width , height ;
unsigned long bitsperframe ;
int err , pb ;
if ( vb2_is_busy ( & vc - > vidq ) )
return - EBUSY ;
bitsperframe = vc - > width * vc - > height * vc - > format - > depth ;
err = tw686x_try_fmt_vid_cap ( file , priv , f ) ;
if ( err )
return err ;
vc - > format = format_by_fourcc ( f - > fmt . pix . pixelformat ) ;
vc - > width = f - > fmt . pix . width ;
vc - > height = f - > fmt . pix . height ;
/* We need new DMA buffers if the framesize has changed */
2016-06-04 20:47:15 -03:00
if ( dev - > dma_ops - > alloc & &
bitsperframe ! = vc - > width * vc - > height * vc - > format - > depth ) {
2016-03-02 11:30:16 -03:00
for ( pb = 0 ; pb < 2 ; pb + + )
2016-06-04 20:47:15 -03:00
dev - > dma_ops - > free ( vc , pb ) ;
2016-03-02 11:30:16 -03:00
for ( pb = 0 ; pb < 2 ; pb + + ) {
2016-06-04 20:47:15 -03:00
err = dev - > dma_ops - > alloc ( vc , pb ) ;
2016-03-02 11:30:16 -03:00
if ( err ) {
if ( pb > 0 )
2016-06-04 20:47:15 -03:00
dev - > dma_ops - > free ( vc , 0 ) ;
2016-03-02 11:30:16 -03:00
return err ;
}
}
}
val = reg_read ( vc - > dev , VDMA_CHANNEL_CONFIG [ vc - > ch ] ) ;
if ( vc - > width < = TW686X_VIDEO_WIDTH / 2 )
val | = BIT ( 23 ) ;
else
val & = ~ BIT ( 23 ) ;
if ( vc - > height < = TW686X_VIDEO_HEIGHT ( vc - > video_standard ) / 2 )
val | = BIT ( 24 ) ;
else
val & = ~ BIT ( 24 ) ;
2016-06-04 20:47:17 -03:00
val & = ~ 0x7ffff ;
/* Program the DMA scatter-gather */
if ( dev - > dma_mode = = TW686X_DMA_MODE_SG ) {
u32 start_idx , end_idx ;
start_idx = is_second_gen ( dev ) ?
0 : vc - > ch * TW686X_MAX_SG_DESC_COUNT ;
end_idx = start_idx + TW686X_MAX_SG_DESC_COUNT - 1 ;
val | = ( end_idx < < 10 ) | start_idx ;
}
2016-03-02 11:30:16 -03:00
val & = ~ ( 0x7 < < 20 ) ;
val | = vc - > format - > mode < < 20 ;
reg_write ( vc - > dev , VDMA_CHANNEL_CONFIG [ vc - > ch ] , val ) ;
/* Program the DMA frame size */
width = ( vc - > width * 2 ) & 0x7ff ;
height = vc - > height / 2 ;
line_width = ( vc - > width * 2 ) & 0x7ff ;
val = ( height < < 22 ) | ( line_width < < 11 ) | width ;
reg_write ( vc - > dev , VDMA_WHP [ vc - > ch ] , val ) ;
return 0 ;
}
static int tw686x_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
struct tw686x_dev * dev = vc - > dev ;
strlcpy ( cap - > driver , " tw686x " , sizeof ( cap - > driver ) ) ;
strlcpy ( cap - > card , dev - > name , sizeof ( cap - > card ) ) ;
snprintf ( cap - > bus_info , sizeof ( cap - > bus_info ) ,
" PCI:%s " , pci_name ( dev - > pci_dev ) ) ;
cap - > device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE ;
cap - > capabilities = cap - > device_caps | V4L2_CAP_DEVICE_CAPS ;
return 0 ;
}
static int tw686x_s_std ( struct file * file , void * priv , v4l2_std_id id )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
struct v4l2_format f ;
u32 val , ret ;
if ( vc - > video_standard = = id )
return 0 ;
if ( vb2_is_busy ( & vc - > vidq ) )
return - EBUSY ;
if ( id & V4L2_STD_NTSC )
val = 0 ;
else if ( id & V4L2_STD_PAL )
val = 1 ;
else if ( id & V4L2_STD_SECAM )
val = 2 ;
else if ( id & V4L2_STD_NTSC_443 )
val = 3 ;
else if ( id & V4L2_STD_PAL_M )
val = 4 ;
else if ( id & V4L2_STD_PAL_Nc )
val = 5 ;
else if ( id & V4L2_STD_PAL_60 )
val = 6 ;
else
return - EINVAL ;
vc - > video_standard = id ;
reg_write ( vc - > dev , SDT [ vc - > ch ] , val ) ;
val = reg_read ( vc - > dev , VIDEO_CONTROL1 ) ;
2016-04-21 03:23:58 -03:00
if ( id & V4L2_STD_525_60 )
2016-03-02 11:30:16 -03:00
val & = ~ ( 1 < < ( SYS_MODE_DMA_SHIFT + vc - > ch ) ) ;
2016-04-21 03:23:58 -03:00
else
val | = ( 1 < < ( SYS_MODE_DMA_SHIFT + vc - > ch ) ) ;
2016-03-02 11:30:16 -03:00
reg_write ( vc - > dev , VIDEO_CONTROL1 , val ) ;
/*
* Adjust format after V4L2_STD_525_60 / V4L2_STD_625_50 change ,
* calling g_fmt and s_fmt will sanitize the height
* according to the standard .
*/
ret = tw686x_g_fmt_vid_cap ( file , priv , & f ) ;
if ( ! ret )
tw686x_s_fmt_vid_cap ( file , priv , & f ) ;
return 0 ;
}
static int tw686x_querystd ( struct file * file , void * priv , v4l2_std_id * std )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
struct tw686x_dev * dev = vc - > dev ;
unsigned int old_std , detected_std = 0 ;
unsigned long end ;
if ( vb2_is_streaming ( & vc - > vidq ) )
return - EBUSY ;
/* Enable and start standard detection */
old_std = reg_read ( dev , SDT [ vc - > ch ] ) ;
reg_write ( dev , SDT [ vc - > ch ] , 0x7 ) ;
reg_write ( dev , SDT_EN [ vc - > ch ] , 0xff ) ;
end = jiffies + msecs_to_jiffies ( 500 ) ;
while ( time_is_after_jiffies ( end ) ) {
detected_std = reg_read ( dev , SDT [ vc - > ch ] ) ;
if ( ! ( detected_std & BIT ( 7 ) ) )
break ;
msleep ( 100 ) ;
}
reg_write ( dev , SDT [ vc - > ch ] , old_std ) ;
/* Exit if still busy */
if ( detected_std & BIT ( 7 ) )
return 0 ;
detected_std = ( detected_std > > 4 ) & 0x7 ;
switch ( detected_std ) {
case TW686X_STD_NTSC_M :
* std & = V4L2_STD_NTSC ;
break ;
case TW686X_STD_NTSC_443 :
* std & = V4L2_STD_NTSC_443 ;
break ;
case TW686X_STD_PAL_M :
* std & = V4L2_STD_PAL_M ;
break ;
case TW686X_STD_PAL_60 :
* std & = V4L2_STD_PAL_60 ;
break ;
case TW686X_STD_PAL :
* std & = V4L2_STD_PAL ;
break ;
case TW686X_STD_PAL_CN :
* std & = V4L2_STD_PAL_Nc ;
break ;
case TW686X_STD_SECAM :
* std & = V4L2_STD_SECAM ;
break ;
default :
* std = 0 ;
}
return 0 ;
}
static int tw686x_g_std ( struct file * file , void * priv , v4l2_std_id * id )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
* id = vc - > video_standard ;
return 0 ;
}
static int tw686x_enum_fmt_vid_cap ( struct file * file , void * priv ,
struct v4l2_fmtdesc * f )
{
if ( f - > index > = ARRAY_SIZE ( formats ) )
return - EINVAL ;
f - > pixelformat = formats [ f - > index ] . fourcc ;
return 0 ;
}
static int tw686x_s_input ( struct file * file , void * priv , unsigned int i )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
u32 val ;
if ( i > = TW686X_INPUTS_PER_CH )
return - EINVAL ;
if ( i = = vc - > input )
return 0 ;
/*
* Not sure we are able to support on the fly input change
*/
if ( vb2_is_busy ( & vc - > vidq ) )
return - EBUSY ;
vc - > input = i ;
val = reg_read ( vc - > dev , VDMA_CHANNEL_CONFIG [ vc - > ch ] ) ;
val & = ~ ( 0x3 < < 30 ) ;
val | = i < < 30 ;
reg_write ( vc - > dev , VDMA_CHANNEL_CONFIG [ vc - > ch ] , val ) ;
return 0 ;
}
static int tw686x_g_input ( struct file * file , void * priv , unsigned int * i )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
* i = vc - > input ;
return 0 ;
}
static int tw686x_enum_input ( struct file * file , void * priv ,
struct v4l2_input * i )
{
struct tw686x_video_channel * vc = video_drvdata ( file ) ;
unsigned int vidstat ;
if ( i - > index > = TW686X_INPUTS_PER_CH )
return - EINVAL ;
snprintf ( i - > name , sizeof ( i - > name ) , " Composite%d " , i - > index ) ;
i - > type = V4L2_INPUT_TYPE_CAMERA ;
i - > std = vc - > device - > tvnorms ;
i - > capabilities = V4L2_IN_CAP_STD ;
vidstat = reg_read ( vc - > dev , VIDSTAT [ vc - > ch ] ) ;
i - > status = 0 ;
if ( vidstat & TW686X_VIDSTAT_VDLOSS )
i - > status | = V4L2_IN_ST_NO_SIGNAL ;
if ( ! ( vidstat & TW686X_VIDSTAT_HLOCK ) )
i - > status | = V4L2_IN_ST_NO_H_LOCK ;
return 0 ;
}
2016-03-21 12:09:59 -03:00
static const struct v4l2_file_operations tw686x_video_fops = {
2016-03-02 11:30:16 -03:00
. owner = THIS_MODULE ,
. open = v4l2_fh_open ,
. unlocked_ioctl = video_ioctl2 ,
. release = vb2_fop_release ,
. poll = vb2_fop_poll ,
. read = vb2_fop_read ,
. mmap = vb2_fop_mmap ,
} ;
2016-03-21 12:09:59 -03:00
static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = {
2016-03-02 11:30:16 -03:00
. vidioc_querycap = tw686x_querycap ,
. vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap ,
. vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap ,
. vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap ,
. vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap ,
. vidioc_querystd = tw686x_querystd ,
. vidioc_g_std = tw686x_g_std ,
. vidioc_s_std = tw686x_s_std ,
. vidioc_enum_input = tw686x_enum_input ,
. vidioc_g_input = tw686x_g_input ,
. vidioc_s_input = tw686x_s_input ,
. vidioc_reqbufs = vb2_ioctl_reqbufs ,
. vidioc_querybuf = vb2_ioctl_querybuf ,
. vidioc_qbuf = vb2_ioctl_qbuf ,
. vidioc_dqbuf = vb2_ioctl_dqbuf ,
. vidioc_create_bufs = vb2_ioctl_create_bufs ,
. vidioc_streamon = vb2_ioctl_streamon ,
. vidioc_streamoff = vb2_ioctl_streamoff ,
. vidioc_prepare_buf = vb2_ioctl_prepare_buf ,
. vidioc_log_status = v4l2_ctrl_log_status ,
. vidioc_subscribe_event = v4l2_ctrl_subscribe_event ,
. vidioc_unsubscribe_event = v4l2_event_unsubscribe ,
} ;
void tw686x_video_irq ( struct tw686x_dev * dev , unsigned long requests ,
unsigned int pb_status , unsigned int fifo_status ,
unsigned int * reset_ch )
{
struct tw686x_video_channel * vc ;
unsigned long flags ;
unsigned int ch , pb ;
for_each_set_bit ( ch , & requests , max_channels ( dev ) ) {
vc = & dev - > video_channels [ ch ] ;
/*
* This can either be a blue frame ( with signal - lost bit set )
* or a good frame ( with signal - lost bit clear ) . If we have just
* got signal , then this channel needs resetting .
*/
if ( vc - > no_signal & & ! ( fifo_status & BIT ( ch ) ) ) {
v4l2_printk ( KERN_DEBUG , & dev - > v4l2_dev ,
" video%d: signal recovered \n " , vc - > num ) ;
vc - > no_signal = false ;
* reset_ch | = BIT ( ch ) ;
vc - > pb = 0 ;
continue ;
}
vc - > no_signal = ! ! ( fifo_status & BIT ( ch ) ) ;
/* Check FIFO errors only if there's signal */
if ( ! vc - > no_signal ) {
u32 fifo_ov , fifo_bad ;
fifo_ov = ( fifo_status > > 24 ) & BIT ( ch ) ;
fifo_bad = ( fifo_status > > 16 ) & BIT ( ch ) ;
if ( fifo_ov | | fifo_bad ) {
/* Mark this channel for reset */
v4l2_printk ( KERN_DEBUG , & dev - > v4l2_dev ,
" video%d: FIFO error \n " , vc - > num ) ;
* reset_ch | = BIT ( ch ) ;
vc - > pb = 0 ;
continue ;
}
}
pb = ! ! ( pb_status & BIT ( ch ) ) ;
if ( vc - > pb ! = pb ) {
/* Mark this channel for reset */
v4l2_printk ( KERN_DEBUG , & dev - > v4l2_dev ,
" video%d: unexpected p-b buffer! \n " ,
vc - > num ) ;
* reset_ch | = BIT ( ch ) ;
vc - > pb = 0 ;
continue ;
}
spin_lock_irqsave ( & vc - > qlock , flags ) ;
2016-06-04 20:47:15 -03:00
tw686x_buf_done ( vc , pb ) ;
dev - > dma_ops - > buf_refill ( vc , pb ) ;
2016-03-02 11:30:16 -03:00
spin_unlock_irqrestore ( & vc - > qlock , flags ) ;
}
}
void tw686x_video_free ( struct tw686x_dev * dev )
{
unsigned int ch , pb ;
for ( ch = 0 ; ch < max_channels ( dev ) ; ch + + ) {
struct tw686x_video_channel * vc = & dev - > video_channels [ ch ] ;
if ( vc - > device )
video_unregister_device ( vc - > device ) ;
2016-06-04 20:47:15 -03:00
if ( dev - > dma_ops - > free )
for ( pb = 0 ; pb < 2 ; pb + + )
dev - > dma_ops - > free ( vc , pb ) ;
2016-03-02 11:30:16 -03:00
}
}
int tw686x_video_init ( struct tw686x_dev * dev )
{
unsigned int ch , val , pb ;
int err ;
2016-06-04 20:47:15 -03:00
if ( dev - > dma_mode = = TW686X_DMA_MODE_MEMCPY )
dev - > dma_ops = & memcpy_dma_ops ;
2016-06-04 20:47:16 -03:00
else if ( dev - > dma_mode = = TW686X_DMA_MODE_CONTIG )
dev - > dma_ops = & contig_dma_ops ;
2016-06-04 20:47:17 -03:00
else if ( dev - > dma_mode = = TW686X_DMA_MODE_SG )
dev - > dma_ops = & sg_dma_ops ;
2016-06-04 20:47:15 -03:00
else
return - EINVAL ;
2016-03-02 11:30:16 -03:00
err = v4l2_device_register ( & dev - > pci_dev - > dev , & dev - > v4l2_dev ) ;
if ( err )
return err ;
2016-06-04 20:47:15 -03:00
if ( dev - > dma_ops - > setup ) {
err = dev - > dma_ops - > setup ( dev ) ;
if ( err )
return err ;
}
2016-03-02 11:30:16 -03:00
for ( ch = 0 ; ch < max_channels ( dev ) ; ch + + ) {
struct tw686x_video_channel * vc = & dev - > video_channels [ ch ] ;
struct video_device * vdev ;
mutex_init ( & vc - > vb_mutex ) ;
spin_lock_init ( & vc - > qlock ) ;
INIT_LIST_HEAD ( & vc - > vidq_queued ) ;
vc - > dev = dev ;
vc - > ch = ch ;
/* default settings */
vc - > format = & formats [ 0 ] ;
vc - > video_standard = V4L2_STD_NTSC ;
vc - > width = TW686X_VIDEO_WIDTH ;
vc - > height = TW686X_VIDEO_HEIGHT ( vc - > video_standard ) ;
vc - > input = 0 ;
reg_write ( vc - > dev , SDT [ ch ] , 0 ) ;
tw686x_set_framerate ( vc , 30 ) ;
reg_write ( dev , VDELAY_LO [ ch ] , 0x14 ) ;
reg_write ( dev , HACTIVE_LO [ ch ] , 0xd0 ) ;
reg_write ( dev , VIDEO_SIZE [ ch ] , 0 ) ;
2016-06-04 20:47:15 -03:00
if ( dev - > dma_ops - > alloc ) {
for ( pb = 0 ; pb < 2 ; pb + + ) {
err = dev - > dma_ops - > alloc ( vc , pb ) ;
if ( err )
goto error ;
}
2016-03-02 11:30:16 -03:00
}
vc - > vidq . io_modes = VB2_READ | VB2_MMAP | VB2_DMABUF ;
vc - > vidq . type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
vc - > vidq . drv_priv = vc ;
vc - > vidq . buf_struct_size = sizeof ( struct tw686x_v4l2_buf ) ;
vc - > vidq . ops = & tw686x_video_qops ;
2016-06-04 20:47:15 -03:00
vc - > vidq . mem_ops = dev - > dma_ops - > mem_ops ;
2016-03-02 11:30:16 -03:00
vc - > vidq . timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC ;
vc - > vidq . min_buffers_needed = 2 ;
vc - > vidq . lock = & vc - > vb_mutex ;
2016-04-01 19:38:21 -03:00
vc - > vidq . gfp_flags = GFP_DMA32 ;
2016-07-01 06:50:17 -03:00
vc - > vidq . dev = & dev - > pci_dev - > dev ;
2016-03-02 11:30:16 -03:00
err = vb2_queue_init ( & vc - > vidq ) ;
if ( err ) {
v4l2_err ( & dev - > v4l2_dev ,
" dma%d: cannot init vb2 queue \n " , ch ) ;
goto error ;
}
err = v4l2_ctrl_handler_init ( & vc - > ctrl_handler , 4 ) ;
if ( err ) {
v4l2_err ( & dev - > v4l2_dev ,
" dma%d: cannot init ctrl handler \n " , ch ) ;
goto error ;
}
v4l2_ctrl_new_std ( & vc - > ctrl_handler , & ctrl_ops ,
V4L2_CID_BRIGHTNESS , - 128 , 127 , 1 , 0 ) ;
v4l2_ctrl_new_std ( & vc - > ctrl_handler , & ctrl_ops ,
V4L2_CID_CONTRAST , 0 , 255 , 1 , 100 ) ;
v4l2_ctrl_new_std ( & vc - > ctrl_handler , & ctrl_ops ,
V4L2_CID_SATURATION , 0 , 255 , 1 , 128 ) ;
v4l2_ctrl_new_std ( & vc - > ctrl_handler , & ctrl_ops ,
V4L2_CID_HUE , - 128 , 127 , 1 , 0 ) ;
err = vc - > ctrl_handler . error ;
if ( err )
goto error ;
err = v4l2_ctrl_handler_setup ( & vc - > ctrl_handler ) ;
if ( err )
goto error ;
vdev = video_device_alloc ( ) ;
if ( ! vdev ) {
v4l2_err ( & dev - > v4l2_dev ,
" dma%d: unable to allocate device \n " , ch ) ;
err = - ENOMEM ;
goto error ;
}
snprintf ( vdev - > name , sizeof ( vdev - > name ) , " %s video " , dev - > name ) ;
vdev - > fops = & tw686x_video_fops ;
vdev - > ioctl_ops = & tw686x_video_ioctl_ops ;
vdev - > release = video_device_release ;
vdev - > v4l2_dev = & dev - > v4l2_dev ;
vdev - > queue = & vc - > vidq ;
vdev - > tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50 ;
vdev - > minor = - 1 ;
vdev - > lock = & vc - > vb_mutex ;
vdev - > ctrl_handler = & vc - > ctrl_handler ;
vc - > device = vdev ;
video_set_drvdata ( vdev , vc ) ;
err = video_register_device ( vdev , VFL_TYPE_GRABBER , - 1 ) ;
if ( err < 0 )
goto error ;
vc - > num = vdev - > num ;
}
val = TW686X_DEF_PHASE_REF ;
for ( ch = 0 ; ch < max_channels ( dev ) ; ch + + )
2016-06-04 20:47:15 -03:00
val | = dev - > dma_ops - > hw_dma_mode < < ( 16 + ch * 2 ) ;
2016-03-02 11:30:16 -03:00
reg_write ( dev , PHASE_REF , val ) ;
reg_write ( dev , MISC2 [ 0 ] , 0xe7 ) ;
reg_write ( dev , VCTRL1 [ 0 ] , 0xcc ) ;
reg_write ( dev , LOOP [ 0 ] , 0xa5 ) ;
if ( max_channels ( dev ) > 4 ) {
reg_write ( dev , VCTRL1 [ 1 ] , 0xcc ) ;
reg_write ( dev , LOOP [ 1 ] , 0xa5 ) ;
reg_write ( dev , MISC2 [ 1 ] , 0xe7 ) ;
}
return 0 ;
error :
tw686x_video_free ( dev ) ;
return err ;
}