2008-12-05 16:19:36 +03:00
/*
* drivers / media / video / tvp514x . c
*
* TI TVP5146 / 47 decoder driver
*
* Copyright ( C ) 2008 Texas Instruments Inc
* Author : Vaibhav Hiremath < hvaibhav @ ti . com >
*
* Contributors :
* Sivaraj R < sivaraj @ ti . com >
* Brijesh R Jadav < brijesh . j @ ti . com >
* Hardik Shah < hardik . shah @ ti . com >
* Manjunath Hadli < mrh @ ti . com >
* Karicheri Muralidharan < m - karicheri2 @ ti . com >
*
* This package is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/i2c.h>
# include <linux/delay.h>
# include <linux/videodev2.h>
2009-06-19 14:13:44 +04:00
# include <media/v4l2-device.h>
# include <media/v4l2-common.h>
# include <media/v4l2-chip-ident.h>
2008-12-05 16:19:36 +03:00
# include <media/tvp514x.h>
# include "tvp514x_regs.h"
/* Module Name */
# define TVP514X_MODULE_NAME "tvp514x"
/* Private macros for TVP */
# define I2C_RETRY_COUNT (5)
# define LOCK_RETRY_COUNT (5)
# define LOCK_RETRY_DELAY (200)
/* Debug functions */
static int debug ;
module_param ( debug , bool , 0644 ) ;
MODULE_PARM_DESC ( debug , " Debug level (0-1) " ) ;
2009-06-19 14:13:44 +04:00
MODULE_AUTHOR ( " Texas Instruments " ) ;
MODULE_DESCRIPTION ( " TVP514X linux decoder driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
/*
2008-12-05 16:19:36 +03:00
* enum tvp514x_std - enum for supported standards
*/
enum tvp514x_std {
STD_NTSC_MJ = 0 ,
STD_PAL_BDGHIN ,
STD_INVALID
} ;
2009-06-19 14:13:44 +04:00
/*
2008-12-05 16:19:36 +03:00
* struct tvp514x_std_info - Structure to store standard informations
* @ width : Line width in pixels
* @ height : Number of active lines
* @ video_std : Value to write in REG_VIDEO_STD register
* @ standard : v4l2 standard structure information
*/
struct tvp514x_std_info {
unsigned long width ;
unsigned long height ;
u8 video_std ;
struct v4l2_standard standard ;
} ;
2009-01-12 12:17:43 +03:00
static struct tvp514x_reg tvp514x_reg_list_default [ 0x40 ] ;
2009-06-19 14:13:44 +04:00
/*
2009-01-12 12:17:43 +03:00
* struct tvp514x_decoder - TVP5146 / 47 decoder object
2009-06-19 14:13:44 +04:00
* @ sd : Subdevice Slave handle
2009-01-12 12:17:43 +03:00
* @ tvp514x_regs : copy of hw ' s regs with preset values .
2008-12-05 16:19:36 +03:00
* @ pdata : Board specific
* @ ver : Chip version
2009-06-19 14:13:44 +04:00
* @ streaming : TVP5146 / 47 decoder streaming - enabled or disabled .
2008-12-05 16:19:36 +03:00
* @ pix : Current pixel format
* @ num_fmts : Number of formats
* @ fmt_list : Format list
* @ current_std : Current standard
* @ num_stds : Number of standards
* @ std_list : Standards list
2009-06-19 14:13:44 +04:00
* @ input : Input routing at chip level
* @ output : Output routing at chip level
2008-12-05 16:19:36 +03:00
*/
struct tvp514x_decoder {
2009-06-19 14:13:44 +04:00
struct v4l2_subdev sd ;
2009-01-12 12:17:43 +03:00
struct tvp514x_reg tvp514x_regs [ ARRAY_SIZE ( tvp514x_reg_list_default ) ] ;
2008-12-05 16:19:36 +03:00
const struct tvp514x_platform_data * pdata ;
int ver ;
2009-06-19 14:13:44 +04:00
int streaming ;
2008-12-05 16:19:36 +03:00
struct v4l2_pix_format pix ;
int num_fmts ;
const struct v4l2_fmtdesc * fmt_list ;
enum tvp514x_std current_std ;
int num_stds ;
struct tvp514x_std_info * std_list ;
2009-06-19 14:13:44 +04:00
/*
* Input and Output Routing parameters
*/
u32 input ;
u32 output ;
2008-12-05 16:19:36 +03:00
} ;
/* TVP514x default register values */
2009-01-12 12:17:43 +03:00
static struct tvp514x_reg tvp514x_reg_list_default [ ] = {
2008-12-05 16:19:36 +03:00
{ TOK_WRITE , REG_INPUT_SEL , 0x05 } , /* Composite selected */
{ TOK_WRITE , REG_AFE_GAIN_CTRL , 0x0F } ,
{ TOK_WRITE , REG_VIDEO_STD , 0x00 } , /* Auto mode */
{ TOK_WRITE , REG_OPERATION_MODE , 0x00 } ,
{ TOK_SKIP , REG_AUTOSWITCH_MASK , 0x3F } ,
{ TOK_WRITE , REG_COLOR_KILLER , 0x10 } ,
{ TOK_WRITE , REG_LUMA_CONTROL1 , 0x00 } ,
{ TOK_WRITE , REG_LUMA_CONTROL2 , 0x00 } ,
{ TOK_WRITE , REG_LUMA_CONTROL3 , 0x02 } ,
{ TOK_WRITE , REG_BRIGHTNESS , 0x80 } ,
{ TOK_WRITE , REG_CONTRAST , 0x80 } ,
{ TOK_WRITE , REG_SATURATION , 0x80 } ,
{ TOK_WRITE , REG_HUE , 0x00 } ,
{ TOK_WRITE , REG_CHROMA_CONTROL1 , 0x00 } ,
{ TOK_WRITE , REG_CHROMA_CONTROL2 , 0x0E } ,
{ TOK_SKIP , 0x0F , 0x00 } , /* Reserved */
{ TOK_WRITE , REG_COMP_PR_SATURATION , 0x80 } ,
{ TOK_WRITE , REG_COMP_Y_CONTRAST , 0x80 } ,
{ TOK_WRITE , REG_COMP_PB_SATURATION , 0x80 } ,
{ TOK_SKIP , 0x13 , 0x00 } , /* Reserved */
{ TOK_WRITE , REG_COMP_Y_BRIGHTNESS , 0x80 } ,
{ TOK_SKIP , 0x15 , 0x00 } , /* Reserved */
{ TOK_SKIP , REG_AVID_START_PIXEL_LSB , 0x55 } , /* NTSC timing */
{ TOK_SKIP , REG_AVID_START_PIXEL_MSB , 0x00 } ,
{ TOK_SKIP , REG_AVID_STOP_PIXEL_LSB , 0x25 } ,
{ TOK_SKIP , REG_AVID_STOP_PIXEL_MSB , 0x03 } ,
{ TOK_SKIP , REG_HSYNC_START_PIXEL_LSB , 0x00 } , /* NTSC timing */
{ TOK_SKIP , REG_HSYNC_START_PIXEL_MSB , 0x00 } ,
{ TOK_SKIP , REG_HSYNC_STOP_PIXEL_LSB , 0x40 } ,
{ TOK_SKIP , REG_HSYNC_STOP_PIXEL_MSB , 0x00 } ,
{ TOK_SKIP , REG_VSYNC_START_LINE_LSB , 0x04 } , /* NTSC timing */
{ TOK_SKIP , REG_VSYNC_START_LINE_MSB , 0x00 } ,
{ TOK_SKIP , REG_VSYNC_STOP_LINE_LSB , 0x07 } ,
{ TOK_SKIP , REG_VSYNC_STOP_LINE_MSB , 0x00 } ,
{ TOK_SKIP , REG_VBLK_START_LINE_LSB , 0x01 } , /* NTSC timing */
{ TOK_SKIP , REG_VBLK_START_LINE_MSB , 0x00 } ,
{ TOK_SKIP , REG_VBLK_STOP_LINE_LSB , 0x15 } ,
{ TOK_SKIP , REG_VBLK_STOP_LINE_MSB , 0x00 } ,
{ TOK_SKIP , 0x26 , 0x00 } , /* Reserved */
{ TOK_SKIP , 0x27 , 0x00 } , /* Reserved */
{ TOK_SKIP , REG_FAST_SWTICH_CONTROL , 0xCC } ,
{ TOK_SKIP , 0x29 , 0x00 } , /* Reserved */
{ TOK_SKIP , REG_FAST_SWTICH_SCART_DELAY , 0x00 } ,
{ TOK_SKIP , 0x2B , 0x00 } , /* Reserved */
{ TOK_SKIP , REG_SCART_DELAY , 0x00 } ,
{ TOK_SKIP , REG_CTI_DELAY , 0x00 } ,
{ TOK_SKIP , REG_CTI_CONTROL , 0x00 } ,
{ TOK_SKIP , 0x2F , 0x00 } , /* Reserved */
{ TOK_SKIP , 0x30 , 0x00 } , /* Reserved */
{ TOK_SKIP , 0x31 , 0x00 } , /* Reserved */
{ TOK_WRITE , REG_SYNC_CONTROL , 0x00 } , /* HS, VS active high */
{ TOK_WRITE , REG_OUTPUT_FORMATTER1 , 0x00 } , /* 10-bit BT.656 */
{ TOK_WRITE , REG_OUTPUT_FORMATTER2 , 0x11 } , /* Enable clk & data */
{ TOK_WRITE , REG_OUTPUT_FORMATTER3 , 0xEE } , /* Enable AVID & FLD */
{ TOK_WRITE , REG_OUTPUT_FORMATTER4 , 0xAF } , /* Enable VS & HS */
{ TOK_WRITE , REG_OUTPUT_FORMATTER5 , 0xFF } ,
{ TOK_WRITE , REG_OUTPUT_FORMATTER6 , 0xFF } ,
{ TOK_WRITE , REG_CLEAR_LOST_LOCK , 0x01 } , /* Clear status */
{ TOK_TERM , 0 , 0 } ,
} ;
2009-06-19 14:13:44 +04:00
/*
* List of image formats supported by TVP5146 / 47 decoder
2008-12-05 16:19:36 +03:00
* Currently we are using 8 bit mode only , but can be
* extended to 10 / 20 bit mode .
*/
static const struct v4l2_fmtdesc tvp514x_fmt_list [ ] = {
{
. index = 0 ,
. type = V4L2_BUF_TYPE_VIDEO_CAPTURE ,
. flags = 0 ,
. description = " 8-bit UYVY 4:2:2 Format " ,
. pixelformat = V4L2_PIX_FMT_UYVY ,
} ,
} ;
/*
* Supported standards -
*
* Currently supports two standards only , need to add support for rest of the
* modes , like SECAM , etc . . .
*/
static struct tvp514x_std_info tvp514x_std_list [ ] = {
/* Standard: STD_NTSC_MJ */
[ STD_NTSC_MJ ] = {
. width = NTSC_NUM_ACTIVE_PIXELS ,
. height = NTSC_NUM_ACTIVE_LINES ,
. video_std = VIDEO_STD_NTSC_MJ_BIT ,
. standard = {
. index = 0 ,
. id = V4L2_STD_NTSC ,
. name = " NTSC " ,
. frameperiod = { 1001 , 30000 } ,
. framelines = 525
} ,
/* Standard: STD_PAL_BDGHIN */
} ,
[ STD_PAL_BDGHIN ] = {
. width = PAL_NUM_ACTIVE_PIXELS ,
. height = PAL_NUM_ACTIVE_LINES ,
. video_std = VIDEO_STD_PAL_BDGHIN_BIT ,
. standard = {
. index = 1 ,
. id = V4L2_STD_PAL ,
. name = " PAL " ,
. frameperiod = { 1 , 25 } ,
. framelines = 625
} ,
} ,
/* Standard: need to add for additional standard */
} ;
2009-06-19 14:13:44 +04:00
static inline struct tvp514x_decoder * to_decoder ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct tvp514x_decoder , sd ) ;
}
2008-12-05 16:19:36 +03:00
/*
* Read a value from a register in an TVP5146 / 47 decoder device .
* Returns value read if successful , or non - zero ( - 1 ) otherwise .
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_read_reg ( struct v4l2_subdev * sd , u8 reg )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
int err , retry = 0 ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2008-12-05 16:19:36 +03:00
read_again :
err = i2c_smbus_read_byte_data ( client , reg ) ;
if ( err = = - 1 ) {
if ( retry < = I2C_RETRY_COUNT ) {
2009-06-19 14:13:44 +04:00
v4l2_warn ( sd , " Read: retry ... %d \n " , retry ) ;
2008-12-05 16:19:36 +03:00
retry + + ;
msleep_interruptible ( 10 ) ;
goto read_again ;
}
}
return err ;
}
2009-06-19 14:13:44 +04:00
static void dump_reg ( struct v4l2_subdev * sd , u8 reg )
{
u32 val ;
val = tvp514x_read_reg ( sd , reg ) ;
v4l2_info ( sd , " Reg(0x%.2X): 0x%.2X \n " , reg , val ) ;
}
2008-12-05 16:19:36 +03:00
/*
* Write a value to a register in an TVP5146 / 47 decoder device .
* Returns zero if successful , or non - zero otherwise .
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_write_reg ( struct v4l2_subdev * sd , u8 reg , u8 val )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
int err , retry = 0 ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2008-12-05 16:19:36 +03:00
write_again :
err = i2c_smbus_write_byte_data ( client , reg , val ) ;
if ( err ) {
if ( retry < = I2C_RETRY_COUNT ) {
2009-06-19 14:13:44 +04:00
v4l2_warn ( sd , " Write: retry ... %d \n " , retry ) ;
2008-12-05 16:19:36 +03:00
retry + + ;
msleep_interruptible ( 10 ) ;
goto write_again ;
}
}
return err ;
}
/*
* tvp514x_write_regs : Initializes a list of TVP5146 / 47 registers
* if token is TOK_TERM , then entire write operation terminates
* if token is TOK_DELAY , then a delay of ' val ' msec is introduced
* if token is TOK_SKIP , then the register write is skipped
* if token is TOK_WRITE , then the register write is performed
*
* reglist - list of registers to be written
* Returns zero if successful , or non - zero otherwise .
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_write_regs ( struct v4l2_subdev * sd ,
2008-12-05 16:19:36 +03:00
const struct tvp514x_reg reglist [ ] )
{
int err ;
const struct tvp514x_reg * next = reglist ;
for ( ; next - > token ! = TOK_TERM ; next + + ) {
if ( next - > token = = TOK_DELAY ) {
msleep ( next - > val ) ;
continue ;
}
if ( next - > token = = TOK_SKIP )
continue ;
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , next - > reg , ( u8 ) next - > val ) ;
2008-12-05 16:19:36 +03:00
if ( err ) {
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " Write failed. Err[%d] \n " , err ) ;
2008-12-05 16:19:36 +03:00
return err ;
}
}
return 0 ;
}
/*
* tvp514x_get_current_std :
* Returns the current standard detected by TVP5146 / 47
*/
2009-06-19 14:13:44 +04:00
static enum tvp514x_std tvp514x_get_current_std ( struct v4l2_subdev * sd )
2008-12-05 16:19:36 +03:00
{
u8 std , std_status ;
2009-06-19 14:13:44 +04:00
std = tvp514x_read_reg ( sd , REG_VIDEO_STD ) ;
if ( ( std & VIDEO_STD_MASK ) = = VIDEO_STD_AUTO_SWITCH_BIT )
2008-12-05 16:19:36 +03:00
/* use the standard status register */
2009-06-19 14:13:44 +04:00
std_status = tvp514x_read_reg ( sd , REG_VIDEO_STD_STATUS ) ;
else
2008-12-05 16:19:36 +03:00
std_status = std ; /* use the standard register itself */
switch ( std_status & VIDEO_STD_MASK ) {
case VIDEO_STD_NTSC_MJ_BIT :
return STD_NTSC_MJ ;
case VIDEO_STD_PAL_BDGHIN_BIT :
return STD_PAL_BDGHIN ;
default :
return STD_INVALID ;
}
return STD_INVALID ;
}
/*
* TVP5146 / 47 register dump function
*/
2009-06-19 14:13:44 +04:00
static void tvp514x_reg_dump ( struct v4l2_subdev * sd )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
dump_reg ( sd , REG_INPUT_SEL ) ;
dump_reg ( sd , REG_AFE_GAIN_CTRL ) ;
dump_reg ( sd , REG_VIDEO_STD ) ;
dump_reg ( sd , REG_OPERATION_MODE ) ;
dump_reg ( sd , REG_COLOR_KILLER ) ;
dump_reg ( sd , REG_LUMA_CONTROL1 ) ;
dump_reg ( sd , REG_LUMA_CONTROL2 ) ;
dump_reg ( sd , REG_LUMA_CONTROL3 ) ;
dump_reg ( sd , REG_BRIGHTNESS ) ;
dump_reg ( sd , REG_CONTRAST ) ;
dump_reg ( sd , REG_SATURATION ) ;
dump_reg ( sd , REG_HUE ) ;
dump_reg ( sd , REG_CHROMA_CONTROL1 ) ;
dump_reg ( sd , REG_CHROMA_CONTROL2 ) ;
dump_reg ( sd , REG_COMP_PR_SATURATION ) ;
dump_reg ( sd , REG_COMP_Y_CONTRAST ) ;
dump_reg ( sd , REG_COMP_PB_SATURATION ) ;
dump_reg ( sd , REG_COMP_Y_BRIGHTNESS ) ;
dump_reg ( sd , REG_AVID_START_PIXEL_LSB ) ;
dump_reg ( sd , REG_AVID_START_PIXEL_MSB ) ;
dump_reg ( sd , REG_AVID_STOP_PIXEL_LSB ) ;
dump_reg ( sd , REG_AVID_STOP_PIXEL_MSB ) ;
dump_reg ( sd , REG_HSYNC_START_PIXEL_LSB ) ;
dump_reg ( sd , REG_HSYNC_START_PIXEL_MSB ) ;
dump_reg ( sd , REG_HSYNC_STOP_PIXEL_LSB ) ;
dump_reg ( sd , REG_HSYNC_STOP_PIXEL_MSB ) ;
dump_reg ( sd , REG_VSYNC_START_LINE_LSB ) ;
dump_reg ( sd , REG_VSYNC_START_LINE_MSB ) ;
dump_reg ( sd , REG_VSYNC_STOP_LINE_LSB ) ;
dump_reg ( sd , REG_VSYNC_STOP_LINE_MSB ) ;
dump_reg ( sd , REG_VBLK_START_LINE_LSB ) ;
dump_reg ( sd , REG_VBLK_START_LINE_MSB ) ;
dump_reg ( sd , REG_VBLK_STOP_LINE_LSB ) ;
dump_reg ( sd , REG_VBLK_STOP_LINE_MSB ) ;
dump_reg ( sd , REG_SYNC_CONTROL ) ;
dump_reg ( sd , REG_OUTPUT_FORMATTER1 ) ;
dump_reg ( sd , REG_OUTPUT_FORMATTER2 ) ;
dump_reg ( sd , REG_OUTPUT_FORMATTER3 ) ;
dump_reg ( sd , REG_OUTPUT_FORMATTER4 ) ;
dump_reg ( sd , REG_OUTPUT_FORMATTER5 ) ;
dump_reg ( sd , REG_OUTPUT_FORMATTER6 ) ;
dump_reg ( sd , REG_CLEAR_LOST_LOCK ) ;
2008-12-05 16:19:36 +03:00
}
/*
* Configure the TVP5146 / 47 with the current register settings
* Returns zero if successful , or non - zero otherwise .
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_configure ( struct v4l2_subdev * sd ,
struct tvp514x_decoder * decoder )
2008-12-05 16:19:36 +03:00
{
int err ;
/* common register initialization */
err =
2009-06-19 14:13:44 +04:00
tvp514x_write_regs ( sd , decoder - > tvp514x_regs ) ;
2008-12-05 16:19:36 +03:00
if ( err )
return err ;
if ( debug )
2009-06-19 14:13:44 +04:00
tvp514x_reg_dump ( sd ) ;
2008-12-05 16:19:36 +03:00
return 0 ;
}
/*
* Detect if an tvp514x is present , and if so which revision .
* A device is considered to be detected if the chip ID ( LSB and MSB )
* registers match the expected values .
* Any value of the rom version register is accepted .
* Returns ENODEV error number if no device is detected , or zero
* if a device is detected .
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_detect ( struct v4l2_subdev * sd ,
struct tvp514x_decoder * decoder )
2008-12-05 16:19:36 +03:00
{
u8 chip_id_msb , chip_id_lsb , rom_ver ;
2009-06-19 14:13:44 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
chip_id_msb = tvp514x_read_reg ( sd , REG_CHIP_ID_MSB ) ;
chip_id_lsb = tvp514x_read_reg ( sd , REG_CHIP_ID_LSB ) ;
rom_ver = tvp514x_read_reg ( sd , REG_ROM_VERSION ) ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd ,
2008-12-05 16:19:36 +03:00
" chip id detected msb:0x%x lsb:0x%x rom version:0x%x \n " ,
chip_id_msb , chip_id_lsb , rom_ver ) ;
if ( ( chip_id_msb ! = TVP514X_CHIP_ID_MSB )
| | ( ( chip_id_lsb ! = TVP5146_CHIP_ID_LSB )
& & ( chip_id_lsb ! = TVP5147_CHIP_ID_LSB ) ) ) {
/* We didn't read the values we expected, so this must not be
* an TVP5146 / 47.
*/
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " chip id mismatch msb:0x%x lsb:0x%x \n " ,
chip_id_msb , chip_id_lsb ) ;
2008-12-05 16:19:36 +03:00
return - ENODEV ;
}
decoder - > ver = rom_ver ;
2009-06-19 14:13:44 +04:00
v4l2_info ( sd , " %s (Version - 0x%.2x) found at 0x%x (%s) \n " ,
client - > name , decoder - > ver ,
client - > addr < < 1 , client - > adapter - > name ) ;
2008-12-05 16:19:36 +03:00
return 0 ;
}
/*
* Following are decoder interface functions implemented by
* TVP5146 / 47 decoder driver .
*/
2009-06-19 14:13:44 +04:00
/*
* tvp514x_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ std_id : standard V4L2 std_id ioctl enum
*
* Returns the current standard detected by TVP5146 / 47. If no active input is
* detected , returns - EINVAL
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_querystd ( struct v4l2_subdev * sd , v4l2_std_id * std_id )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
enum tvp514x_std current_std ;
enum tvp514x_input input_sel ;
u8 sync_lock_status , lock_mask ;
if ( std_id = = NULL )
return - EINVAL ;
/* get the current standard */
2009-06-19 14:13:44 +04:00
current_std = tvp514x_get_current_std ( sd ) ;
2008-12-05 16:19:36 +03:00
if ( current_std = = STD_INVALID )
return - EINVAL ;
2009-06-19 14:13:44 +04:00
input_sel = decoder - > input ;
2008-12-05 16:19:36 +03:00
switch ( input_sel ) {
case INPUT_CVBS_VI1A :
case INPUT_CVBS_VI1B :
case INPUT_CVBS_VI1C :
case INPUT_CVBS_VI2A :
case INPUT_CVBS_VI2B :
case INPUT_CVBS_VI2C :
case INPUT_CVBS_VI3A :
case INPUT_CVBS_VI3B :
case INPUT_CVBS_VI3C :
case INPUT_CVBS_VI4A :
lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
STATUS_HORZ_SYNC_LOCK_BIT |
STATUS_VIRT_SYNC_LOCK_BIT ;
break ;
case INPUT_SVIDEO_VI2A_VI1A :
case INPUT_SVIDEO_VI2B_VI1B :
case INPUT_SVIDEO_VI2C_VI1C :
case INPUT_SVIDEO_VI2A_VI3A :
case INPUT_SVIDEO_VI2B_VI3B :
case INPUT_SVIDEO_VI2C_VI3C :
case INPUT_SVIDEO_VI4A_VI1A :
case INPUT_SVIDEO_VI4A_VI1B :
case INPUT_SVIDEO_VI4A_VI1C :
case INPUT_SVIDEO_VI4A_VI3A :
case INPUT_SVIDEO_VI4A_VI3B :
case INPUT_SVIDEO_VI4A_VI3C :
lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
STATUS_VIRT_SYNC_LOCK_BIT ;
break ;
/*Need to add other interfaces*/
default :
return - EINVAL ;
}
/* check whether signal is locked */
2009-06-19 14:13:44 +04:00
sync_lock_status = tvp514x_read_reg ( sd , REG_STATUS1 ) ;
2008-12-05 16:19:36 +03:00
if ( lock_mask ! = ( sync_lock_status & lock_mask ) )
return - EINVAL ; /* No input detected */
decoder - > current_std = current_std ;
* std_id = decoder - > std_list [ current_std ] . standard . id ;
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Current STD: %s " ,
2008-12-05 16:19:36 +03:00
decoder - > std_list [ current_std ] . standard . name ) ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ std_id : standard V4L2 v4l2_std_id ioctl enum
*
* If std_id is supported , sets the requested standard . Otherwise , returns
* - EINVAL
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_s_std ( struct v4l2_subdev * sd , v4l2_std_id std_id )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
int err , i ;
for ( i = 0 ; i < decoder - > num_stds ; i + + )
2009-06-19 14:13:44 +04:00
if ( std_id & decoder - > std_list [ i ] . standard . id )
2008-12-05 16:19:36 +03:00
break ;
if ( ( i = = decoder - > num_stds ) | | ( i = = STD_INVALID ) )
return - EINVAL ;
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , REG_VIDEO_STD ,
2008-12-05 16:19:36 +03:00
decoder - > std_list [ i ] . video_std ) ;
if ( err )
return err ;
decoder - > current_std = i ;
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_VIDEO_STD ] . val =
decoder - > std_list [ i ] . video_std ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Standard set to: %s " ,
2008-12-05 16:19:36 +03:00
decoder - > std_list [ i ] . standard . name ) ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ index : number of the input
*
* If index is valid , selects the requested input . Otherwise , returns - EINVAL if
* the input is not supported or there is no active signal present in the
* selected input .
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_s_routing ( struct v4l2_subdev * sd ,
u32 input , u32 output , u32 config )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
int err ;
enum tvp514x_input input_sel ;
enum tvp514x_output output_sel ;
enum tvp514x_std current_std = STD_INVALID ;
u8 sync_lock_status , lock_mask ;
int try_count = LOCK_RETRY_COUNT ;
2009-06-19 14:13:44 +04:00
if ( ( input > = INPUT_INVALID ) | |
( output > = OUTPUT_INVALID ) )
2008-12-05 16:19:36 +03:00
return - EINVAL ; /* Index out of bound */
2009-06-19 14:13:44 +04:00
input_sel = input ;
output_sel = output ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , REG_INPUT_SEL , input_sel ) ;
2008-12-05 16:19:36 +03:00
if ( err )
return err ;
2009-06-19 14:13:44 +04:00
output_sel | = tvp514x_read_reg ( sd ,
2008-12-05 16:19:36 +03:00
REG_OUTPUT_FORMATTER1 ) & 0x7 ;
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , REG_OUTPUT_FORMATTER1 ,
2008-12-05 16:19:36 +03:00
output_sel ) ;
if ( err )
return err ;
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_INPUT_SEL ] . val = input_sel ;
decoder - > tvp514x_regs [ REG_OUTPUT_FORMATTER1 ] . val = output_sel ;
2008-12-05 16:19:36 +03:00
/* Clear status */
msleep ( LOCK_RETRY_DELAY ) ;
err =
2009-06-19 14:13:44 +04:00
tvp514x_write_reg ( sd , REG_CLEAR_LOST_LOCK , 0x01 ) ;
2008-12-05 16:19:36 +03:00
if ( err )
return err ;
switch ( input_sel ) {
case INPUT_CVBS_VI1A :
case INPUT_CVBS_VI1B :
case INPUT_CVBS_VI1C :
case INPUT_CVBS_VI2A :
case INPUT_CVBS_VI2B :
case INPUT_CVBS_VI2C :
case INPUT_CVBS_VI3A :
case INPUT_CVBS_VI3B :
case INPUT_CVBS_VI3C :
case INPUT_CVBS_VI4A :
lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
STATUS_HORZ_SYNC_LOCK_BIT |
STATUS_VIRT_SYNC_LOCK_BIT ;
break ;
case INPUT_SVIDEO_VI2A_VI1A :
case INPUT_SVIDEO_VI2B_VI1B :
case INPUT_SVIDEO_VI2C_VI1C :
case INPUT_SVIDEO_VI2A_VI3A :
case INPUT_SVIDEO_VI2B_VI3B :
case INPUT_SVIDEO_VI2C_VI3C :
case INPUT_SVIDEO_VI4A_VI1A :
case INPUT_SVIDEO_VI4A_VI1B :
case INPUT_SVIDEO_VI4A_VI1C :
case INPUT_SVIDEO_VI4A_VI3A :
case INPUT_SVIDEO_VI4A_VI3B :
case INPUT_SVIDEO_VI4A_VI3C :
lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
STATUS_VIRT_SYNC_LOCK_BIT ;
break ;
/*Need to add other interfaces*/
default :
return - EINVAL ;
}
while ( try_count - - > 0 ) {
/* Allow decoder to sync up with new input */
msleep ( LOCK_RETRY_DELAY ) ;
/* get the current standard for future reference */
2009-06-19 14:13:44 +04:00
current_std = tvp514x_get_current_std ( sd ) ;
2008-12-05 16:19:36 +03:00
if ( current_std = = STD_INVALID )
continue ;
2009-06-19 14:13:44 +04:00
sync_lock_status = tvp514x_read_reg ( sd ,
2008-12-05 16:19:36 +03:00
REG_STATUS1 ) ;
if ( lock_mask = = ( sync_lock_status & lock_mask ) )
break ; /* Input detected */
}
2009-06-11 18:28:17 +04:00
if ( ( current_std = = STD_INVALID ) | | ( try_count < 0 ) )
2008-12-05 16:19:36 +03:00
return - EINVAL ;
decoder - > current_std = current_std ;
2009-06-19 14:13:44 +04:00
decoder - > input = input ;
decoder - > output = output ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Input set to: %d, std : %d " ,
2008-12-05 16:19:36 +03:00
input_sel , current_std ) ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ qctrl : standard V4L2 v4l2_queryctrl structure
*
* If the requested control is supported , returns the control information .
* Otherwise , returns - EINVAL if the control is not supported .
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_queryctrl ( struct v4l2_subdev * sd , struct v4l2_queryctrl * qctrl )
2008-12-05 16:19:36 +03:00
{
int err = - EINVAL ;
if ( qctrl = = NULL )
return err ;
switch ( qctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
2009-02-22 00:47:24 +03:00
/* Brightness supported is (0-255),
2008-12-05 16:19:36 +03:00
*/
2009-02-22 00:47:24 +03:00
err = v4l2_ctrl_query_fill ( qctrl , 0 , 255 , 1 , 128 ) ;
2008-12-05 16:19:36 +03:00
break ;
case V4L2_CID_CONTRAST :
case V4L2_CID_SATURATION :
/* Saturation and Contrast supported is -
* Contrast : 0 - 255 ( Default - 128 )
* Saturation : 0 - 255 ( Default - 128 )
*/
err = v4l2_ctrl_query_fill ( qctrl , 0 , 255 , 1 , 128 ) ;
break ;
case V4L2_CID_HUE :
/* Hue Supported is -
* Hue - - 180 - + 180 ( Default - 0 , Step - + 180 )
*/
err = v4l2_ctrl_query_fill ( qctrl , - 180 , 180 , 180 , 0 ) ;
break ;
case V4L2_CID_AUTOGAIN :
2009-06-19 14:13:44 +04:00
/*
* Auto Gain supported is -
* 0 - 1 ( Default - 1 )
*/
err = v4l2_ctrl_query_fill ( qctrl , 0 , 1 , 1 , 1 ) ;
2008-12-05 16:19:36 +03:00
break ;
default :
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " invalid control id %d \n " , qctrl - > id ) ;
2008-12-05 16:19:36 +03:00
return err ;
}
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Query Control:%s: Min - %d, Max - %d, Def - %d " ,
qctrl - > name , qctrl - > minimum , qctrl - > maximum ,
2008-12-05 16:19:36 +03:00
qctrl - > default_value ) ;
return err ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ ctrl : pointer to v4l2_control structure
*
* If the requested control is supported , returns the control ' s current
* value from the decoder . Otherwise , returns - EINVAL if the control is not
* supported .
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_g_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
if ( ctrl = = NULL )
return - EINVAL ;
switch ( ctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
2009-01-12 12:17:43 +03:00
ctrl - > value = decoder - > tvp514x_regs [ REG_BRIGHTNESS ] . val ;
2008-12-05 16:19:36 +03:00
break ;
case V4L2_CID_CONTRAST :
2009-01-12 12:17:43 +03:00
ctrl - > value = decoder - > tvp514x_regs [ REG_CONTRAST ] . val ;
2008-12-05 16:19:36 +03:00
break ;
case V4L2_CID_SATURATION :
2009-01-12 12:17:43 +03:00
ctrl - > value = decoder - > tvp514x_regs [ REG_SATURATION ] . val ;
2008-12-05 16:19:36 +03:00
break ;
case V4L2_CID_HUE :
2009-01-12 12:17:43 +03:00
ctrl - > value = decoder - > tvp514x_regs [ REG_HUE ] . val ;
2008-12-05 16:19:36 +03:00
if ( ctrl - > value = = 0x7F )
ctrl - > value = 180 ;
else if ( ctrl - > value = = 0x80 )
ctrl - > value = - 180 ;
else
ctrl - > value = 0 ;
break ;
case V4L2_CID_AUTOGAIN :
2009-01-12 12:17:43 +03:00
ctrl - > value = decoder - > tvp514x_regs [ REG_AFE_GAIN_CTRL ] . val ;
2008-12-05 16:19:36 +03:00
if ( ( ctrl - > value & 0x3 ) = = 3 )
ctrl - > value = 1 ;
else
ctrl - > value = 0 ;
break ;
default :
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " invalid control id %d \n " , ctrl - > id ) ;
2008-12-05 16:19:36 +03:00
return - EINVAL ;
}
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Get Control: ID - %d - %d " ,
2008-12-05 16:19:36 +03:00
ctrl - > id , ctrl - > value ) ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ ctrl : pointer to v4l2_control structure
*
* If the requested control is supported , sets the control ' s current
* value in HW . Otherwise , returns - EINVAL if the control is not supported .
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_s_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
int err = - EINVAL , value ;
if ( ctrl = = NULL )
return err ;
2009-06-19 14:13:44 +04:00
value = ctrl - > value ;
2008-12-05 16:19:36 +03:00
switch ( ctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
if ( ctrl - > value < 0 | | ctrl - > value > 255 ) {
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " invalid brightness setting %d \n " ,
2008-12-05 16:19:36 +03:00
ctrl - > value ) ;
return - ERANGE ;
}
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , REG_BRIGHTNESS ,
2008-12-05 16:19:36 +03:00
value ) ;
if ( err )
return err ;
2009-06-19 14:13:44 +04:00
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_BRIGHTNESS ] . val = value ;
2008-12-05 16:19:36 +03:00
break ;
case V4L2_CID_CONTRAST :
if ( ctrl - > value < 0 | | ctrl - > value > 255 ) {
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " invalid contrast setting %d \n " ,
2008-12-05 16:19:36 +03:00
ctrl - > value ) ;
return - ERANGE ;
}
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , REG_CONTRAST , value ) ;
2008-12-05 16:19:36 +03:00
if ( err )
return err ;
2009-06-19 14:13:44 +04:00
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_CONTRAST ] . val = value ;
2008-12-05 16:19:36 +03:00
break ;
case V4L2_CID_SATURATION :
if ( ctrl - > value < 0 | | ctrl - > value > 255 ) {
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " invalid saturation setting %d \n " ,
2008-12-05 16:19:36 +03:00
ctrl - > value ) ;
return - ERANGE ;
}
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , REG_SATURATION , value ) ;
2008-12-05 16:19:36 +03:00
if ( err )
return err ;
2009-06-19 14:13:44 +04:00
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_SATURATION ] . val = value ;
2008-12-05 16:19:36 +03:00
break ;
case V4L2_CID_HUE :
if ( value = = 180 )
value = 0x7F ;
else if ( value = = - 180 )
value = 0x80 ;
else if ( value = = 0 )
value = 0 ;
else {
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " invalid hue setting %d \n " , ctrl - > value ) ;
2008-12-05 16:19:36 +03:00
return - ERANGE ;
}
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , REG_HUE , value ) ;
2008-12-05 16:19:36 +03:00
if ( err )
return err ;
2009-06-19 14:13:44 +04:00
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_HUE ] . val = value ;
2008-12-05 16:19:36 +03:00
break ;
case V4L2_CID_AUTOGAIN :
if ( value = = 1 )
value = 0x0F ;
else if ( value = = 0 )
value = 0x0C ;
else {
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " invalid auto gain setting %d \n " ,
2008-12-05 16:19:36 +03:00
ctrl - > value ) ;
return - ERANGE ;
}
2009-06-19 14:13:44 +04:00
err = tvp514x_write_reg ( sd , REG_AFE_GAIN_CTRL , value ) ;
2008-12-05 16:19:36 +03:00
if ( err )
return err ;
2009-06-19 14:13:44 +04:00
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_AFE_GAIN_CTRL ] . val = value ;
2008-12-05 16:19:36 +03:00
break ;
default :
2009-06-19 14:13:44 +04:00
v4l2_err ( sd , " invalid control id %d \n " , ctrl - > id ) ;
2008-12-05 16:19:36 +03:00
return err ;
}
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Set Control: ID - %d - %d " ,
2008-12-05 16:19:36 +03:00
ctrl - > id , ctrl - > value ) ;
return err ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ fmt : standard V4L2 VIDIOC_ENUM_FMT ioctl structure
*
* Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_enum_fmt_cap ( struct v4l2_subdev * sd , struct v4l2_fmtdesc * fmt )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
int index ;
if ( fmt = = NULL )
return - EINVAL ;
index = fmt - > index ;
if ( ( index > = decoder - > num_fmts ) | | ( index < 0 ) )
return - EINVAL ; /* Index out of bound */
if ( fmt - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
return - EINVAL ; /* only capture is supported */
memcpy ( fmt , & decoder - > fmt_list [ index ] ,
sizeof ( struct v4l2_fmtdesc ) ) ;
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Current FMT: index - %d (%s) " ,
2008-12-05 16:19:36 +03:00
decoder - > fmt_list [ index ] . index ,
decoder - > fmt_list [ index ] . description ) ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ f : pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
*
* Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type . This
* ioctl is used to negotiate the image capture size and pixel format
* without actually making it take effect .
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_try_fmt_cap ( struct v4l2_subdev * sd , struct v4l2_format * f )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
int ifmt ;
struct v4l2_pix_format * pix ;
enum tvp514x_std current_std ;
if ( f = = NULL )
return - EINVAL ;
if ( f - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
f - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
pix = & f - > fmt . pix ;
/* Calculate height and width based on current standard */
2009-06-19 14:13:44 +04:00
current_std = tvp514x_get_current_std ( sd ) ;
2008-12-05 16:19:36 +03:00
if ( current_std = = STD_INVALID )
return - EINVAL ;
decoder - > current_std = current_std ;
pix - > width = decoder - > std_list [ current_std ] . width ;
pix - > height = decoder - > std_list [ current_std ] . height ;
for ( ifmt = 0 ; ifmt < decoder - > num_fmts ; ifmt + + ) {
if ( pix - > pixelformat = =
decoder - > fmt_list [ ifmt ] . pixelformat )
break ;
}
if ( ifmt = = decoder - > num_fmts )
ifmt = 0 ; /* None of the format matched, select default */
pix - > pixelformat = decoder - > fmt_list [ ifmt ] . pixelformat ;
pix - > field = V4L2_FIELD_INTERLACED ;
pix - > bytesperline = pix - > width * 2 ;
pix - > sizeimage = pix - > bytesperline * pix - > height ;
pix - > colorspace = V4L2_COLORSPACE_SMPTE170M ;
pix - > priv = 0 ;
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Try FMT: pixelformat - %s, bytesperline - %d "
2008-12-05 16:19:36 +03:00
" Width - %d, Height - %d " ,
decoder - > fmt_list [ ifmt ] . description , pix - > bytesperline ,
pix - > width , pix - > height ) ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ f : pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
*
* If the requested format is supported , configures the HW to use that
* format , returns error code if format not supported or HW can ' t be
* correctly configured .
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_s_fmt_cap ( struct v4l2_subdev * sd , struct v4l2_format * f )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
struct v4l2_pix_format * pix ;
int rval ;
if ( f = = NULL )
return - EINVAL ;
if ( f - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
return - EINVAL ; /* only capture is supported */
pix = & f - > fmt . pix ;
2009-06-19 14:13:44 +04:00
rval = tvp514x_try_fmt_cap ( sd , f ) ;
2008-12-05 16:19:36 +03:00
if ( rval )
return rval ;
decoder - > pix = * pix ;
return rval ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_g_fmt_cap - V4L2 decoder interface handler for tvp514x_g_fmt_cap
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ f : pointer to standard V4L2 v4l2_format structure
*
* Returns the decoder ' s current pixel format in the v4l2_format
* parameter .
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_g_fmt_cap ( struct v4l2_subdev * sd , struct v4l2_format * f )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
if ( f = = NULL )
return - EINVAL ;
if ( f - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
return - EINVAL ; /* only capture is supported */
f - > fmt . pix = decoder - > pix ;
2009-06-19 14:13:44 +04:00
v4l2_dbg ( 1 , debug , sd , " Current FMT: bytesperline - %d "
2008-12-05 16:19:36 +03:00
" Width - %d, Height - %d " ,
decoder - > pix . bytesperline ,
decoder - > pix . width , decoder - > pix . height ) ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ a : pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
*
* Returns the decoder ' s video CAPTURE parameters .
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_g_parm ( struct v4l2_subdev * sd , struct v4l2_streamparm * a )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
struct v4l2_captureparm * cparm ;
enum tvp514x_std current_std ;
if ( a = = NULL )
return - EINVAL ;
if ( a - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
return - EINVAL ; /* only capture is supported */
memset ( a , 0 , sizeof ( * a ) ) ;
a - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
/* get the current standard */
2009-06-19 14:13:44 +04:00
current_std = tvp514x_get_current_std ( sd ) ;
2008-12-05 16:19:36 +03:00
if ( current_std = = STD_INVALID )
return - EINVAL ;
decoder - > current_std = current_std ;
cparm = & a - > parm . capture ;
cparm - > capability = V4L2_CAP_TIMEPERFRAME ;
cparm - > timeperframe =
decoder - > std_list [ current_std ] . standard . frameperiod ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl
* @ sd : pointer to standard V4L2 sub - device structure
2008-12-05 16:19:36 +03:00
* @ a : pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
*
* Configures the decoder to use the input parameters , if possible . If
* not possible , returns the appropriate error code .
*/
static int
2009-06-19 14:13:44 +04:00
tvp514x_s_parm ( struct v4l2_subdev * sd , struct v4l2_streamparm * a )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
struct v4l2_fract * timeperframe ;
enum tvp514x_std current_std ;
if ( a = = NULL )
return - EINVAL ;
if ( a - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
return - EINVAL ; /* only capture is supported */
timeperframe = & a - > parm . capture . timeperframe ;
/* get the current standard */
2009-06-19 14:13:44 +04:00
current_std = tvp514x_get_current_std ( sd ) ;
2008-12-05 16:19:36 +03:00
if ( current_std = = STD_INVALID )
return - EINVAL ;
decoder - > current_std = current_std ;
* timeperframe =
decoder - > std_list [ current_std ] . standard . frameperiod ;
return 0 ;
}
2009-06-19 14:13:44 +04:00
/*
* tvp514x_s_stream - V4L2 decoder interface handler for vidioc_int_s_power_num
* @ sd : pointer to standard V4L2 sub - device structure
* @ enable : streaming enable or disable
2008-12-05 16:19:36 +03:00
*
2009-06-19 14:13:44 +04:00
* Sets streaming to enable or disable , if possible .
2008-12-05 16:19:36 +03:00
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_s_stream ( struct v4l2_subdev * sd , int enable )
2008-12-05 16:19:36 +03:00
{
int err = 0 ;
2009-06-19 14:13:44 +04:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
if ( decoder - > streaming = = enable )
return 0 ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
switch ( enable ) {
case 0 :
{
/* Power Down Sequence */
err = tvp514x_write_reg ( sd , REG_OPERATION_MODE , 0x01 ) ;
if ( err ) {
v4l2_err ( sd , " Unable to turn off decoder \n " ) ;
return err ;
}
decoder - > streaming = enable ;
2008-12-05 16:19:36 +03:00
break ;
2009-06-19 14:13:44 +04:00
}
case 1 :
{
struct tvp514x_reg * int_seq = ( struct tvp514x_reg * )
client - > driver - > id_table - > driver_data ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
/* Power Up Sequence */
err = tvp514x_write_regs ( sd , int_seq ) ;
if ( err ) {
v4l2_err ( sd , " Unable to turn on decoder \n " ) ;
return err ;
}
/* Detect if not already detected */
err = tvp514x_detect ( sd , decoder ) ;
if ( err ) {
v4l2_err ( sd , " Unable to detect decoder \n " ) ;
return err ;
2008-12-05 16:19:36 +03:00
}
2009-06-19 14:13:44 +04:00
err = tvp514x_configure ( sd , decoder ) ;
if ( err ) {
v4l2_err ( sd , " Unable to configure decoder \n " ) ;
return err ;
}
decoder - > streaming = enable ;
2008-12-05 16:19:36 +03:00
break ;
2009-06-19 14:13:44 +04:00
}
2008-12-05 16:19:36 +03:00
default :
err = - ENODEV ;
break ;
}
return err ;
}
2009-06-19 14:13:44 +04:00
static const struct v4l2_subdev_core_ops tvp514x_core_ops = {
. queryctrl = tvp514x_queryctrl ,
. g_ctrl = tvp514x_g_ctrl ,
. s_ctrl = tvp514x_s_ctrl ,
. s_std = tvp514x_s_std ,
} ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
static const struct v4l2_subdev_video_ops tvp514x_video_ops = {
. s_routing = tvp514x_s_routing ,
. querystd = tvp514x_querystd ,
. enum_fmt = tvp514x_enum_fmt_cap ,
. g_fmt = tvp514x_g_fmt_cap ,
. try_fmt = tvp514x_try_fmt_cap ,
. s_fmt = tvp514x_s_fmt_cap ,
. g_parm = tvp514x_g_parm ,
. s_parm = tvp514x_s_parm ,
. s_stream = tvp514x_s_stream ,
} ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
static const struct v4l2_subdev_ops tvp514x_ops = {
. core = & tvp514x_core_ops ,
. video = & tvp514x_video_ops ,
2008-12-05 16:19:36 +03:00
} ;
static struct tvp514x_decoder tvp514x_dev = {
2009-06-19 14:13:44 +04:00
. streaming = 0 ,
2008-12-05 16:19:36 +03:00
. fmt_list = tvp514x_fmt_list ,
. num_fmts = ARRAY_SIZE ( tvp514x_fmt_list ) ,
2009-06-19 14:13:44 +04:00
. pix = { /* Default to NTSC 8-bit YUV 422 */
2008-12-05 16:19:36 +03:00
. width = NTSC_NUM_ACTIVE_PIXELS ,
. height = NTSC_NUM_ACTIVE_LINES ,
. pixelformat = V4L2_PIX_FMT_UYVY ,
. field = V4L2_FIELD_INTERLACED ,
. bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2 ,
. sizeimage =
NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES ,
. colorspace = V4L2_COLORSPACE_SMPTE170M ,
} ,
. current_std = STD_NTSC_MJ ,
. std_list = tvp514x_std_list ,
. num_stds = ARRAY_SIZE ( tvp514x_std_list ) ,
2009-06-19 14:13:44 +04:00
2008-12-05 16:19:36 +03:00
} ;
2009-06-19 14:13:44 +04:00
/*
2008-12-05 16:19:36 +03:00
* tvp514x_probe - decoder driver i2c probe handler
* @ client : i2c driver client device structure
2009-06-19 14:13:44 +04:00
* @ id : i2c driver id table
2008-12-05 16:19:36 +03:00
*
* Register decoder as an i2c client device and V4L2
* device .
*/
static int
tvp514x_probe ( struct i2c_client * client , const struct i2c_device_id * id )
{
2009-01-12 12:17:43 +03:00
struct tvp514x_decoder * decoder ;
2009-06-19 14:13:44 +04:00
struct v4l2_subdev * sd ;
2008-12-05 16:19:36 +03:00
/* Check if the adapter supports the needed features */
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
return - EIO ;
2009-06-19 14:13:44 +04:00
if ( ! client - > dev . platform_data ) {
v4l2_err ( client , " No platform data!! \n " ) ;
return - ENODEV ;
}
2009-01-12 12:17:43 +03:00
decoder = kzalloc ( sizeof ( * decoder ) , GFP_KERNEL ) ;
if ( ! decoder )
return - ENOMEM ;
2009-06-19 14:13:44 +04:00
/*
* Initialize the tvp514x_decoder with default configuration
*/
2009-01-12 12:17:43 +03:00
* decoder = tvp514x_dev ;
2009-06-19 14:13:44 +04:00
/* Copy default register configuration */
2009-01-12 12:17:43 +03:00
memcpy ( decoder - > tvp514x_regs , tvp514x_reg_list_default ,
sizeof ( tvp514x_reg_list_default ) ) ;
2009-06-19 14:13:44 +04:00
/*
* Copy board specific information here
*/
decoder - > pdata = client - > dev . platform_data ;
2008-12-05 16:19:36 +03:00
/*
* Fetch platform specific data , and configure the
* tvp514x_reg_list [ ] accordingly . Since this is one
* time configuration , no need to preserve .
*/
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_OUTPUT_FORMATTER2 ] . val | =
2009-06-19 14:13:44 +04:00
( decoder - > pdata - > clk_polarity < < 1 ) ;
2009-01-12 12:17:43 +03:00
decoder - > tvp514x_regs [ REG_SYNC_CONTROL ] . val | =
2009-06-19 14:13:44 +04:00
( ( decoder - > pdata - > hs_polarity < < 2 ) |
( decoder - > pdata - > vs_polarity < < 3 ) ) ;
/* Set default standard to auto */
decoder - > tvp514x_regs [ REG_VIDEO_STD ] . val =
VIDEO_STD_AUTO_SWITCH_BIT ;
2008-12-05 16:19:36 +03:00
/* Register with V4L2 layer as slave device */
2009-06-19 14:13:44 +04:00
sd = & decoder - > sd ;
v4l2_i2c_subdev_init ( sd , client , & tvp514x_ops ) ;
v4l2_info ( sd , " %s decoder driver registered !! \n " , sd - > name ) ;
2008-12-05 16:19:36 +03:00
return 0 ;
2009-01-12 12:17:43 +03:00
2008-12-05 16:19:36 +03:00
}
2009-06-19 14:13:44 +04:00
/*
2008-12-05 16:19:36 +03:00
* tvp514x_remove - decoder driver i2c remove handler
* @ client : i2c driver client device structure
*
* Unregister decoder as an i2c client device and V4L2
* device . Complement of tvp514x_probe ( ) .
*/
2009-06-19 14:13:44 +04:00
static int tvp514x_remove ( struct i2c_client * client )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct tvp514x_decoder * decoder = to_decoder ( sd ) ;
2008-12-05 16:19:36 +03:00
2009-06-19 14:13:44 +04:00
v4l2_device_unregister_subdev ( sd ) ;
2009-01-12 12:17:43 +03:00
kfree ( decoder ) ;
2008-12-05 16:19:36 +03:00
return 0 ;
}
/*
* TVP5146 Init / Power on Sequence
*/
static const struct tvp514x_reg tvp5146_init_reg_seq [ ] = {
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS1 , 0x02 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS2 , 0x00 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS3 , 0x80 } ,
{ TOK_WRITE , REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR , 0x01 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS1 , 0x60 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS2 , 0x00 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS3 , 0xB0 } ,
{ TOK_WRITE , REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR , 0x01 } ,
{ TOK_WRITE , REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR , 0x00 } ,
{ TOK_WRITE , REG_OPERATION_MODE , 0x01 } ,
{ TOK_WRITE , REG_OPERATION_MODE , 0x00 } ,
2009-06-19 14:13:44 +04:00
{ TOK_TERM , 0 , 0 } ,
2008-12-05 16:19:36 +03:00
} ;
2009-06-19 14:13:44 +04:00
2008-12-05 16:19:36 +03:00
/*
* TVP5147 Init / Power on Sequence
*/
static const struct tvp514x_reg tvp5147_init_reg_seq [ ] = {
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS1 , 0x02 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS2 , 0x00 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS3 , 0x80 } ,
{ TOK_WRITE , REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR , 0x01 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS1 , 0x60 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS2 , 0x00 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS3 , 0xB0 } ,
{ TOK_WRITE , REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR , 0x01 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS1 , 0x16 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS2 , 0x00 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS3 , 0xA0 } ,
{ TOK_WRITE , REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR , 0x16 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS1 , 0x60 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS2 , 0x00 } ,
{ TOK_WRITE , REG_VBUS_ADDRESS_ACCESS3 , 0xB0 } ,
{ TOK_WRITE , REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR , 0x00 } ,
{ TOK_WRITE , REG_OPERATION_MODE , 0x01 } ,
{ TOK_WRITE , REG_OPERATION_MODE , 0x00 } ,
2009-06-19 14:13:44 +04:00
{ TOK_TERM , 0 , 0 } ,
2008-12-05 16:19:36 +03:00
} ;
2009-06-19 14:13:44 +04:00
2008-12-05 16:19:36 +03:00
/*
* TVP5146M2 / TVP5147M1 Init / Power on Sequence
*/
static const struct tvp514x_reg tvp514xm_init_reg_seq [ ] = {
{ TOK_WRITE , REG_OPERATION_MODE , 0x01 } ,
{ TOK_WRITE , REG_OPERATION_MODE , 0x00 } ,
2009-06-19 14:13:44 +04:00
{ TOK_TERM , 0 , 0 } ,
2008-12-05 16:19:36 +03:00
} ;
2009-06-19 14:13:44 +04:00
2008-12-05 16:19:36 +03:00
/*
* I2C Device Table -
*
* name - Name of the actual device / chip .
* driver_data - Driver data
*/
static const struct i2c_device_id tvp514x_id [ ] = {
2009-06-19 14:13:44 +04:00
{ " tvp5146 " , ( unsigned long ) tvp5146_init_reg_seq } ,
{ " tvp5146m2 " , ( unsigned long ) tvp514xm_init_reg_seq } ,
{ " tvp5147 " , ( unsigned long ) tvp5147_init_reg_seq } ,
{ " tvp5147m1 " , ( unsigned long ) tvp514xm_init_reg_seq } ,
2008-12-05 16:19:36 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , tvp514x_id ) ;
2009-06-19 14:13:44 +04:00
static struct i2c_driver tvp514x_driver = {
2008-12-05 16:19:36 +03:00
. driver = {
2009-06-19 14:13:44 +04:00
. owner = THIS_MODULE ,
. name = TVP514X_MODULE_NAME ,
} ,
2008-12-05 16:19:36 +03:00
. probe = tvp514x_probe ,
2009-06-19 14:13:44 +04:00
. remove = tvp514x_remove ,
2008-12-05 16:19:36 +03:00
. id_table = tvp514x_id ,
} ;
static int __init tvp514x_init ( void )
{
2009-06-19 14:13:44 +04:00
return i2c_add_driver ( & tvp514x_driver ) ;
2008-12-05 16:19:36 +03:00
}
2009-06-19 14:13:44 +04:00
static void __exit tvp514x_exit ( void )
2008-12-05 16:19:36 +03:00
{
2009-06-19 14:13:44 +04:00
i2c_del_driver ( & tvp514x_driver ) ;
2008-12-05 16:19:36 +03:00
}
module_init ( tvp514x_init ) ;
2009-06-19 14:13:44 +04:00
module_exit ( tvp514x_exit ) ;