这两天在调试TVP5146的驱动程序,之前一直都是LF在负责,现在刚刚接手,很多细节都不清楚。不管三七二十一,先做了一个用于读写TVP5146所有内部寄存器的小工具,通过它可以实时修改其中的任意一个寄存器,而不必编译驱动或内核。
实现了这个小工具后,首先切换了一下输入的通道,工作得很好,又从AVIN模式切换到DVD模式,也能看见画面了,虽然有些闪烁,而这就是接下来需要解决的细节问题。
从网上找到了一份Linux下的参考代码,原厂发布出来的,看上去很靠谱,收藏在这,以备不时之需。
/*
*
*
* Copyright (C) 2006 Texas Instruments Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* tvp5146.c */
// phoenix
// modified 2008.01.04 PM 15:00
// linger
// last modified 2007.12.27 PM 17:00
// last modified 2007.11.30 PM 18:00
// last modified 2007.11.29 PM 18:00
#include < linux / config.h >
#include < linux / init.h >
#include < linux / fs.h >
#include < linux / vmalloc.h >
#include < linux / slab.h >
#include < linux / proc_fs.h >
#include < linux / ctype.h >
#include < linux / delay.h >
#include < linux / i2c.h >
#include < linux / videodev.h >
#include < media / tvp5146.h > // phoenix 2008/05/14 dvs6446 updates to
dvevm_1_20
#include < asm / arch / gio.h > // linger 2007.11.8
#define debug_print(x...) // printk(x)
static struct i2c_client tvp5146_i2c_client;
static struct i2c_driver tvp5146_i2c_driver;
static int tvp5146_i2c_registration = 0 ;
struct device * tvp5146_i2c_dev;
static int i2c_read_reg( struct i2c_client * client, u8 reg, u8 * val);
static int i2c_write_reg( struct i2c_client * client, u8 reg, u8 val);
static int configtvp5146( void * arg);
static int clrtvp5146lostlock( void );
static int enabletvp5146agc( int arg);
static int getctrl( void * arg);
static int gettvp5146status( void * arg);
static int powerdowntvp5146( int powerdownenable);
static int queryctrl( void * arg);
static int resettvp5146( void );
static int setctrl( void * arg);
static int settvp5146amuxmode( int mode);
static int settvp5146brightness( int arg);
static int settvp5146contrast( int arg);
static int settvp5146hue( int arg);
static int settvp5146saturation( int arg);
static int settvp5146std( int arg);
// static int setup656sync(int enable);
static int setup656sync(tvp5146_params * tvp5146params);
/*
* ======== tvp5146_init ========
*/
/* This function is used initialize TVP5146 i2c client */
static int tvp5146_init( void )
{
int err;
struct i2c_driver * driver = & tvp5146_i2c_driver;
err = i2c_add_driver(driver);
if (err) {
printk(KERN_ERR " Failed to register TVP5146 I2C client.\n " );
} else {
tvp5146_i2c_registration = TVP5146_I2C_REGISTERED;
}
return err;
}
/*
* ======== tvp5146_cleanup ========
*/
/* This function is used detach TVP5146 i2c client */
static void tvp5146_cleanup( void )
{
struct i2c_driver * driver = & tvp5146_i2c_driver;
if (tvp5146_i2c_registration) {
i2c_detach_client( & tvp5146_i2c_client);
i2c_del_driver(driver);
tvp5146_i2c_client.adapter = NULL;
tvp5146_i2c_registration = TVP5146_I2C_UNREGISTERED;
}
}
/*
* ======== configtvp5146 ========
*/
static int configtvp5146( void * arg)
{
tvp5146_params * tvp5146params = (tvp5146_params * ) arg;
int ret = 0 ;
ret |= gpio_set_direction( 41 , 0 );
ret |= __gpio_set( 41 , 1 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x03 , 0x69 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x08 , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x09 , 0x80 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0a , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0b , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0c , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0d , 0x47 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0F , 0x02 );
ret |= setup656sync(tvp5146params);
ret |= settvp5146amuxmode(tvp5146params -> amuxmode);
ret |= settvp5146std(tvp5146params -> mode);
return ret;
}
/*
* ======== clrtvp5146lostlock ========
*/
static int clrtvp5146lostlock( void )
{
// TVP5150 Not Support
}
/*
* ======== enabletvp5146agc ========
*/
static int enabletvp5146agc( int arg)
{
int ret = 0 ;
int agc;
u8 value;
// ret = i2c_read_reg(&tvp5146_i2c_client, 0x01, &value);
if (arg == TRUE) {
agc = 0x01 ;
} else {
agc = 0x00 ;
}
// agc = agc | value;
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x01 , agc);
return ret;
}
/*
* ======== gettvpctrl ========
*/
static int getctrl( void * arg)
{
struct v4l2_control * ctrl = arg;
int ret = 0 ;
u8 value;
switch (ctrl -> id) {
case V4L2_CID_BRIGHTNESS:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x09 , & value);
ctrl -> value = value;
break ;
case V4L2_CID_CONTRAST:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x0C , & value);
ctrl -> value = value;
break ;
case V4L2_CID_SATURATION:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x0A , & value);
ctrl -> value = value;
break ;
case V4L2_CID_HUE:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x0B , & value);
ctrl -> value = value;
break ;
case V4L2_CID_AUTOGAIN:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x01 , & value);
if ((value & 0x3 ) == 0x01 ) {
ctrl -> value = TRUE;
} else {
ctrl -> value = FALSE;
}
break ;
default :
ret = - EINVAL;
break ;
}
return ret;
}
/*
* ======== gettvp5146std ========
*/
static int gettvp5146std(tvp5146_mode * mode)
{
int ret = 0 ;
// u8 output1;
u8 std;
u8 lock_status;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x28 , & std);
std &= 0xF ;
if (std == TVP5146_MODE_AUTO){
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x8C , & std);
// std = std>>1;
}
std &= 0xF ;
* mode = std;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x88 , & lock_status);
if ((lock_status & 0xe ) != 0xe ) {
/* not quite locked */
ret = - EAGAIN;
}
return ret;
}
/*
* ======== gettvp5146status ========
*/
static int gettvp5146status( void * arg)
{
int ret = 0 ;
tvp5146_status * status = (tvp5146_status * ) arg;
u8 agc, brightness, contrast, hue, saturation;
u8 status_byte;
u8 std;
ret = i2c_read_reg( & tvp5146_i2c_client, 0x01 , & agc);
if ((agc & 0x3 ) == 0x01 ) {
status -> agc_enable = TRUE;
} else {
status -> agc_enable = FALSE;
}
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x09 , & brightness);
status -> brightness = brightness;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x0C , & contrast);
status -> contrast = contrast;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x0A , & saturation);
status -> saturation = saturation;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x0B , & hue);
status -> hue = hue;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x88 , & status_byte);
status -> field_rate = (status_byte & 0x20 ) ? 50 : 60 ;
status -> lost_lock = (status_byte & 0x10 ) >> 4 ;
status -> csubc_lock = (status_byte & 0x8 ) >> 3 ;
status -> v_lock = (status_byte & 0x4 ) >> 2 ;
status -> h_lock = (status_byte & 0x2 ) >> 1 ;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x8C , & std);
if (std | 0x80 ) { /* auto switch mode */
status -> video_std = TVP5146_MODE_AUTO;
} else {
status -> video_std = std;
}
return ret;
}
/*
* ======== powerdowntvp5146 ========
*/
static int powerdowntvp5146( int powerdownenable)
{
u8 powerdownsettings = 0x01 ;
int ret = 0 ;
u8 value;
/* Put _tvp5146 in power down mode */
if ( ! powerdownenable) {
powerdownsettings = 0x00 ;
}
// ret = i2c_read_reg(&tvp5146_i2c_client, 0x02, &value);
// powerdownsettings |= value;
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x02 , powerdownsettings);
return ret;
}
/*
* ======== resettvp5146========
*/
static int resettvp5146( void )
{
tvp5146_params tvp5146params = { 0 };
dev_dbg(tvp5146_i2c_dev, " \nStarting resettvp5146... " );
int ret = 0 ;
ret |= gpio_set_direction( 41 , 0 );
ret |= __gpio_set( 41 , 1 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x03 , 0x69 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x08 , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x09 , 0x80 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0a , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0b , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0c , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0d , 0x47 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0F , 0x02 );
tvp5146params.enablebt656sync = TRUE;
tvp5146params.data_width = TVP5146_WIDTH_8BIT;
setup656sync( & tvp5146params);
settvp5146amuxmode(TVP5146_AMUX_COMPOSITE);
dev_dbg(tvp5146_i2c_dev, " \nEnd of resettvp5146... " );
}
/*
* ======== queryctrl ========
*/
static int queryctrl( void * arg)
{
struct v4l2_queryctrl * queryctrl = arg;
int ret = 0 ;
int id = queryctrl -> id;
memset(queryctrl, 0 , sizeof ( * queryctrl));
queryctrl -> id = id;
switch (id) {
case V4L2_CID_BRIGHTNESS:
strcpy(queryctrl -> name, " BRIGHTNESS " );
queryctrl -> type = V4L2_CTRL_TYPE_INTEGER;
queryctrl -> minimum = 0 ;
queryctrl -> maximum = 255 ;
queryctrl -> step = 1 ;
queryctrl -> default_value = 128 ;
break ;
case V4L2_CID_CONTRAST:
strcpy(queryctrl -> name, " CONTRAST " );
queryctrl -> type = V4L2_CTRL_TYPE_INTEGER;
queryctrl -> minimum = 0 ;
queryctrl -> maximum = 255 ;
queryctrl -> step = 1 ;
queryctrl -> default_value = 128 ;
break ;
case V4L2_CID_SATURATION:
strcpy(queryctrl -> name, " SATURATION " );
queryctrl -> type = V4L2_CTRL_TYPE_INTEGER;
queryctrl -> minimum = 0 ;
queryctrl -> maximum = 255 ;
queryctrl -> step = 1 ;
queryctrl -> default_value = 128 ;
break ;
case V4L2_CID_HUE:
strcpy(queryctrl -> name, " HUE " );
queryctrl -> type = V4L2_CTRL_TYPE_INTEGER;
queryctrl -> minimum = - 128 ; /* -180 DEGREE */
queryctrl -> maximum = 127 ; /* 180 DEGREE */
queryctrl -> step = 1 ;
queryctrl -> default_value = 0 ; /* 0 DEGREE */
break ;
case V4L2_CID_AUTOGAIN:
strcpy(queryctrl -> name, " Automatic Gain Control " );
queryctrl -> type = V4L2_CTRL_TYPE_BOOLEAN;
queryctrl -> minimum = 0 ;
queryctrl -> maximum = 1 ;
queryctrl -> step = 1 ;
queryctrl -> default_value = 1 ;
break ;
default :
/* if (id < V4L2_CID_LASTP1)
queryctrl->flags = V4L2_CTRL_FLAG_DISABLED;
else
ret = -EINVAL; */
break ;
} /* end switch (id) */
return ret;
}
/*
* ======== setctrl ========
*/
static int setctrl( void * arg)
{
struct v4l2_control * ctrl = arg;
int ret = 0 ;
switch (ctrl -> id) {
case V4L2_CID_BRIGHTNESS:
ret = settvp5146brightness(ctrl -> value);
break ;
case V4L2_CID_CONTRAST:
ret = settvp5146contrast(ctrl -> value);
break ;
case V4L2_CID_SATURATION:
ret = settvp5146saturation(ctrl -> value);
break ;
case V4L2_CID_HUE:
ret = settvp5146hue(ctrl -> value);
break ;
case V4L2_CID_AUTOGAIN:
ret = enabletvp5146agc(ctrl -> value);
break ;
default :
ret = - EINVAL;
break ;
}
return ret;
}
/*
* ======== settvp5146amuxmode ========
*/
static int settvp5146amuxmode( int arg)
{
u8 input_sel;
int opmode = 0 ;
int ret,value;
if (arg == TVP5146_AMUX_COMPOSITE) { /* composite */
input_sel = 0x00 ;
opmode = 0x30 ;
} else if (arg == TVP5146_AMUX_SVIDEO) { /* s-video */
input_sel = 0x01 ;
} else {
return - EINVAL;
}
ret = i2c_write_reg( & tvp5146_i2c_client, 0x02 , opmode);
ret = i2c_write_reg( & tvp5146_i2c_client, 0x00 , input_sel);
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x03 , & value);
value = (value & ~ 0x10 ) | 0x40 ;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x03 , value);
return ret;
}
/*
* ======== settvp5146brightness ========
*/
static int settvp5146brightness( int arg)
{
int ret = 0 ;
u8 brightness = (u8) arg;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x09 , brightness);
return ret;
}
/*
* ======== settvp5146contrast ========
*/
static int settvp5146contrast( int arg)
{
int ret = 0 ;
u8 contrast = (u8) arg;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x0C , contrast);
return ret;
}
/*
* ======== settvp5146hue ========
*/
static int settvp5146hue( int arg)
{
int ret = 0 ;
u8 hue = (u8) arg;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x0C , hue);
return ret;
}
static int settvp5146saturation( int arg)
{
int ret = 0 ;
u8 saturation = (u8) arg;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x0A , saturation);
return ret;
}
static int settvp5146std( int arg)
{
int ret = 0 ;
u8 std = (u8) arg & 0xF ; /* the 4th-bit is for squre pixel sampling */
int fmt = 0 ;
/* setup the sampling rate: 601 or square pixel */
debug_print(KERN_INFO " reading i2c registers.\n " );
/* First tests should be against specific std */
if (std == V4L2_STD_ALL) {
fmt = 0 ; /* Autodetect mode */
} else if (std & V4L2_STD_PAL_M) {
fmt = 0x6 ;
} else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
fmt = 0x8 ;
} else {
/* Then, test against generic ones */
if (std & V4L2_STD_NTSC) {
fmt = 0x2 ;
} else if (std & V4L2_STD_PAL) {
fmt = 0x4 ;
} else if (std & V4L2_STD_SECAM) {
fmt = 0xc ;
}
}
/* setup the video standard */
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x28 , fmt);
/* if autoswitch mode, enable all modes for autoswitch */
if (std == TVP5146_MODE_AUTO) {
u8 mask = 0x3C ; /* enable autoswitch for all standards */
ret = i2c_write_reg( & tvp5146_i2c_client, 0x04 , mask);
}
return ret;
}
/*
* ======== setup656sync ========
*/
// Un
// static int setup656sync(int enable)
static int setup656sync(tvp5146_params * tvp5146params)
{
int output1, output2, output3, output4;
int output5, output6;
int ret = 0 ;
if ((tvp5146params -> enablebt656sync)
&& (tvp5146params -> data_width == TVP5146_WIDTH_8BIT)) {
output1 = 0x47 ;
output4 = 0xFF ;
output6 = 0 ;
} else {
output1 = 0x40 ;
output4 = 0xAF ;
output6 = 0x1E ;
}
output2 = 0x69 ; /* enable clock, enable Y[9:0] */
output3 = 0x0 ;
output5 = 0x4 ;
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0D , output1);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x03 , output2);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0E , output3);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x36 , output4);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0E , output3);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x1B , output5);
// ret |= i2c_write_reg(&tvp5146_i2c_client, 0x1B, output5);
return ret;
}
/*
* ======== tvp5146_ctrl ========
*/
int tvp5146_ctrl(tvp5146_cmd cmd, void * arg)
{
int ret = 0 ;
switch (cmd) {
case TVP5146_CONFIG:
ret = configtvp5146(arg);
break ;
case TVP5146_RESET:
ret = resettvp5146();
break ;
case TVP5146_POWERDOWN:
ret = powerdowntvp5146( * ( int * )arg);
break ;
case TVP5146_SET_AMUXMODE:
ret = settvp5146amuxmode( * ( int * )arg);
break ;
case TVP5146_SET_BRIGHTNESS:
ret = settvp5146brightness( * ( int * )arg);
break ;
case TVP5146_SET_CONTRAST:
ret = settvp5146contrast( * ( int * )arg);
break ;
case TVP5146_SET_HUE:
ret = settvp5146hue( * ( int * )arg);
break ;
case TVP5146_SET_SATURATION:
ret = settvp5146saturation( * ( int * )arg);
break ;
case TVP5146_SET_AGC:
ret = enabletvp5146agc( * ( int * )arg);
break ;
case TVP5146_SET_VIDEOSTD:
ret = settvp5146std( * ( int * )arg);
break ;
case TVP5146_CLR_LOSTLOCK:
ret = clrtvp5146lostlock();
break ;
case TVP5146_GET_STATUS:
ret = gettvp5146status(arg);
break ;
case TVP5146_GET_STD:
ret = gettvp5146std(arg);
break ;
case VIDIOC_QUERYCTRL:
ret = queryctrl(arg);
break ;
case TVP5146_INIT:
ret = tvp5146_i2c_init();
break ;
case VIDIOC_G_CTRL:
ret = getctrl(arg);
break ;
case VIDIOC_S_CTRL:
ret = setctrl(arg);
break ;
default :
ret = - EINVAL;
}
return ret;
}
static int i2c_read_reg( struct i2c_client * client, u8 reg, u8 * val)
{
int err = 0 ;
struct i2c_msg msg[ 1 ];
unsigned char data[ 1 ];
if ( ! client -> adapter) {
err = - ENODEV;
} else {
msg -> addr = client -> addr;
msg -> flags = 0 ;
msg -> len = 1 ;
msg -> buf = data;
data[ 0 ] = reg;
err = i2c_transfer(client -> adapter, msg, 1 );
if (err >= 0 ) {
msg -> flags = I2C_M_RD;
err = i2c_transfer(client -> adapter, msg, 1 );
if (err >= 0 ) {
* val = data[ 0 ];
}
}
}
return err;
}
static int i2c_write_reg( struct i2c_client * client, u8 reg, u8 val)
{
int err = 0 ;
struct i2c_msg msg[ 1 ];
unsigned char data[ 2 ];
if ( ! client -> adapter) {
err = - ENODEV;
} else {
msg -> addr = client -> addr;
msg -> flags = 0 ;
msg -> len = 2 ;
msg -> buf = data;
data[ 0 ] = reg;
data[ 1 ] = val;
err = i2c_transfer(client -> adapter, msg, 1 );
}
debug_print(KERN_INFO " i2c data write \n " );
return err;
}
static int _i2c_attach_client( struct i2c_client * client,
struct i2c_driver * driver,
struct i2c_adapter * adap, int addr)
{
int err = 0 ;
if (client -> adapter) {
err = - EBUSY; /* our client is already attached */
} else {
client -> addr = addr;
client -> flags = I2C_CLIENT_ALLOW_USE;
client -> driver = driver;
client -> adapter = adap;
err = i2c_attach_client(client);
if (err) {
client -> adapter = NULL;
}
}
return err;
}
static int _i2c_detach_client( struct i2c_client * client)
{
int err = 0 ;
if ( ! client -> adapter) {
return - ENODEV; /* our client isn't attached */
} else {
err = i2c_detach_client(client);
client -> adapter = NULL;
}
return err;
}
static int tvp5146_i2c_probe_adapter( struct i2c_adapter * adap)
{
int ret;
ret = _i2c_attach_client( & tvp5146_i2c_client, & tvp5146_i2c_driver,
adap, 0x5D );
// ret |= gpio_set_direction(41,0); // linger 2007.11.29 GPIO pins are exchanged on
V1. 1
// ret |= __gpio_set(41,1);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x03 , 0x69 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x08 , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x09 , 0x80 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0a , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0b , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0c , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0d , 0x47 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0F , 0x02 );
return ret;
// end
}
/*
static int tvp5146_i2c_init(void)
{
int err;
struct i2c_driver *driver = &tvp5146_i2c_driver;
driver->owner = THIS_MODULE;
strlcpy(driver->name, "TVP5150 Video Decoder I2C driver",
sizeof(driver->name));
driver->id = I2C_DRIVERID_EXP0;
driver->flags = I2C_DF_NOTIFY;
driver->attach_adapter = tvp5146_i2c_probe_adapter;
driver->detach_client = _i2c_detach_client;
err = i2c_add_driver(driver);
if (err) {
debug_print(KERN_ERR
"Failed to register TVP5146 I2C client.\n");
}
debug_print(KERN_INFO "tvp5146 driver registered.\n");
return err;
}
static void tvp5146_i2c_cleanup(void)
{
struct i2c_driver *driver = &tvp5146_i2c_driver;
i2c_detach_client(&tvp5146_i2c_client);
i2c_del_driver(driver);
tvp5146_i2c_client.adapter = NULL;
}
*/
/*
* ======== tvp5146_i2c_init ========
*/
/* This function is used initialize TVP5146 i2c client */
static int tvp5146_i2c_init( void )
{
int err = 0 ;
struct i2c_driver * driver = & tvp5146_i2c_driver;
driver -> owner = THIS_MODULE;
strlcpy(driver -> name, " TVP5150 Video Decoder I2C driver " ,
sizeof (driver -> name));
driver -> id = I2C_DRIVERID_EXP0;
driver -> flags = I2C_DF_NOTIFY;
driver -> attach_adapter = tvp5146_i2c_probe_adapter;
driver -> detach_client = _i2c_detach_client;
err = i2c_add_driver(driver);
if (err) {
debug_print(KERN_ERR
" Failed to register TVP5150 I2C client.\n " );
}
debug_print(KERN_INFO " tvp5150 driver registered.\n " );
printk( " TVP5150 support for SEED_DVS6446 by Stephen Zhang\n " );
return err;
return err;
}
/*
* ======== tvp5146_i2c_cleanup ========
*/
/* This function is used detach TVP5146 i2c client */
static void tvp5146_i2c_cleanup( void )
{
struct i2c_driver * driver = & tvp5146_i2c_driver;
if (tvp5146_i2c_registration) {
i2c_detach_client( & tvp5146_i2c_client);
i2c_del_driver(driver);
tvp5146_i2c_client.adapter = NULL;
tvp5146_i2c_registration = TVP5146_I2C_UNREGISTERED;
}
}
// module_init(tvp5146_i2c_init);
module_exit(tvp5146_i2c_cleanup);
EXPORT_SYMBOL(tvp5146_ctrl);
MODULE_LICENSE( " GPL " );
/* ************************************************************************ */
/* End of file */
/* ************************************************************************ */
*
*
* Copyright (C) 2006 Texas Instruments Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* tvp5146.c */
// phoenix
// modified 2008.01.04 PM 15:00
// linger
// last modified 2007.12.27 PM 17:00
// last modified 2007.11.30 PM 18:00
// last modified 2007.11.29 PM 18:00
#include < linux / config.h >
#include < linux / init.h >
#include < linux / fs.h >
#include < linux / vmalloc.h >
#include < linux / slab.h >
#include < linux / proc_fs.h >
#include < linux / ctype.h >
#include < linux / delay.h >
#include < linux / i2c.h >
#include < linux / videodev.h >
#include < media / tvp5146.h > // phoenix 2008/05/14 dvs6446 updates to
dvevm_1_20
#include < asm / arch / gio.h > // linger 2007.11.8
#define debug_print(x...) // printk(x)
static struct i2c_client tvp5146_i2c_client;
static struct i2c_driver tvp5146_i2c_driver;
static int tvp5146_i2c_registration = 0 ;
struct device * tvp5146_i2c_dev;
static int i2c_read_reg( struct i2c_client * client, u8 reg, u8 * val);
static int i2c_write_reg( struct i2c_client * client, u8 reg, u8 val);
static int configtvp5146( void * arg);
static int clrtvp5146lostlock( void );
static int enabletvp5146agc( int arg);
static int getctrl( void * arg);
static int gettvp5146status( void * arg);
static int powerdowntvp5146( int powerdownenable);
static int queryctrl( void * arg);
static int resettvp5146( void );
static int setctrl( void * arg);
static int settvp5146amuxmode( int mode);
static int settvp5146brightness( int arg);
static int settvp5146contrast( int arg);
static int settvp5146hue( int arg);
static int settvp5146saturation( int arg);
static int settvp5146std( int arg);
// static int setup656sync(int enable);
static int setup656sync(tvp5146_params * tvp5146params);
/*
* ======== tvp5146_init ========
*/
/* This function is used initialize TVP5146 i2c client */
static int tvp5146_init( void )
{
int err;
struct i2c_driver * driver = & tvp5146_i2c_driver;
err = i2c_add_driver(driver);
if (err) {
printk(KERN_ERR " Failed to register TVP5146 I2C client.\n " );
} else {
tvp5146_i2c_registration = TVP5146_I2C_REGISTERED;
}
return err;
}
/*
* ======== tvp5146_cleanup ========
*/
/* This function is used detach TVP5146 i2c client */
static void tvp5146_cleanup( void )
{
struct i2c_driver * driver = & tvp5146_i2c_driver;
if (tvp5146_i2c_registration) {
i2c_detach_client( & tvp5146_i2c_client);
i2c_del_driver(driver);
tvp5146_i2c_client.adapter = NULL;
tvp5146_i2c_registration = TVP5146_I2C_UNREGISTERED;
}
}
/*
* ======== configtvp5146 ========
*/
static int configtvp5146( void * arg)
{
tvp5146_params * tvp5146params = (tvp5146_params * ) arg;
int ret = 0 ;
ret |= gpio_set_direction( 41 , 0 );
ret |= __gpio_set( 41 , 1 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x03 , 0x69 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x08 , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x09 , 0x80 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0a , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0b , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0c , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0d , 0x47 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0F , 0x02 );
ret |= setup656sync(tvp5146params);
ret |= settvp5146amuxmode(tvp5146params -> amuxmode);
ret |= settvp5146std(tvp5146params -> mode);
return ret;
}
/*
* ======== clrtvp5146lostlock ========
*/
static int clrtvp5146lostlock( void )
{
// TVP5150 Not Support
}
/*
* ======== enabletvp5146agc ========
*/
static int enabletvp5146agc( int arg)
{
int ret = 0 ;
int agc;
u8 value;
// ret = i2c_read_reg(&tvp5146_i2c_client, 0x01, &value);
if (arg == TRUE) {
agc = 0x01 ;
} else {
agc = 0x00 ;
}
// agc = agc | value;
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x01 , agc);
return ret;
}
/*
* ======== gettvpctrl ========
*/
static int getctrl( void * arg)
{
struct v4l2_control * ctrl = arg;
int ret = 0 ;
u8 value;
switch (ctrl -> id) {
case V4L2_CID_BRIGHTNESS:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x09 , & value);
ctrl -> value = value;
break ;
case V4L2_CID_CONTRAST:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x0C , & value);
ctrl -> value = value;
break ;
case V4L2_CID_SATURATION:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x0A , & value);
ctrl -> value = value;
break ;
case V4L2_CID_HUE:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x0B , & value);
ctrl -> value = value;
break ;
case V4L2_CID_AUTOGAIN:
ret = i2c_read_reg( & tvp5146_i2c_client, 0x01 , & value);
if ((value & 0x3 ) == 0x01 ) {
ctrl -> value = TRUE;
} else {
ctrl -> value = FALSE;
}
break ;
default :
ret = - EINVAL;
break ;
}
return ret;
}
/*
* ======== gettvp5146std ========
*/
static int gettvp5146std(tvp5146_mode * mode)
{
int ret = 0 ;
// u8 output1;
u8 std;
u8 lock_status;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x28 , & std);
std &= 0xF ;
if (std == TVP5146_MODE_AUTO){
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x8C , & std);
// std = std>>1;
}
std &= 0xF ;
* mode = std;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x88 , & lock_status);
if ((lock_status & 0xe ) != 0xe ) {
/* not quite locked */
ret = - EAGAIN;
}
return ret;
}
/*
* ======== gettvp5146status ========
*/
static int gettvp5146status( void * arg)
{
int ret = 0 ;
tvp5146_status * status = (tvp5146_status * ) arg;
u8 agc, brightness, contrast, hue, saturation;
u8 status_byte;
u8 std;
ret = i2c_read_reg( & tvp5146_i2c_client, 0x01 , & agc);
if ((agc & 0x3 ) == 0x01 ) {
status -> agc_enable = TRUE;
} else {
status -> agc_enable = FALSE;
}
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x09 , & brightness);
status -> brightness = brightness;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x0C , & contrast);
status -> contrast = contrast;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x0A , & saturation);
status -> saturation = saturation;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x0B , & hue);
status -> hue = hue;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x88 , & status_byte);
status -> field_rate = (status_byte & 0x20 ) ? 50 : 60 ;
status -> lost_lock = (status_byte & 0x10 ) >> 4 ;
status -> csubc_lock = (status_byte & 0x8 ) >> 3 ;
status -> v_lock = (status_byte & 0x4 ) >> 2 ;
status -> h_lock = (status_byte & 0x2 ) >> 1 ;
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x8C , & std);
if (std | 0x80 ) { /* auto switch mode */
status -> video_std = TVP5146_MODE_AUTO;
} else {
status -> video_std = std;
}
return ret;
}
/*
* ======== powerdowntvp5146 ========
*/
static int powerdowntvp5146( int powerdownenable)
{
u8 powerdownsettings = 0x01 ;
int ret = 0 ;
u8 value;
/* Put _tvp5146 in power down mode */
if ( ! powerdownenable) {
powerdownsettings = 0x00 ;
}
// ret = i2c_read_reg(&tvp5146_i2c_client, 0x02, &value);
// powerdownsettings |= value;
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x02 , powerdownsettings);
return ret;
}
/*
* ======== resettvp5146========
*/
static int resettvp5146( void )
{
tvp5146_params tvp5146params = { 0 };
dev_dbg(tvp5146_i2c_dev, " \nStarting resettvp5146... " );
int ret = 0 ;
ret |= gpio_set_direction( 41 , 0 );
ret |= __gpio_set( 41 , 1 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x03 , 0x69 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x08 , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x09 , 0x80 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0a , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0b , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0c , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0d , 0x47 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0F , 0x02 );
tvp5146params.enablebt656sync = TRUE;
tvp5146params.data_width = TVP5146_WIDTH_8BIT;
setup656sync( & tvp5146params);
settvp5146amuxmode(TVP5146_AMUX_COMPOSITE);
dev_dbg(tvp5146_i2c_dev, " \nEnd of resettvp5146... " );
}
/*
* ======== queryctrl ========
*/
static int queryctrl( void * arg)
{
struct v4l2_queryctrl * queryctrl = arg;
int ret = 0 ;
int id = queryctrl -> id;
memset(queryctrl, 0 , sizeof ( * queryctrl));
queryctrl -> id = id;
switch (id) {
case V4L2_CID_BRIGHTNESS:
strcpy(queryctrl -> name, " BRIGHTNESS " );
queryctrl -> type = V4L2_CTRL_TYPE_INTEGER;
queryctrl -> minimum = 0 ;
queryctrl -> maximum = 255 ;
queryctrl -> step = 1 ;
queryctrl -> default_value = 128 ;
break ;
case V4L2_CID_CONTRAST:
strcpy(queryctrl -> name, " CONTRAST " );
queryctrl -> type = V4L2_CTRL_TYPE_INTEGER;
queryctrl -> minimum = 0 ;
queryctrl -> maximum = 255 ;
queryctrl -> step = 1 ;
queryctrl -> default_value = 128 ;
break ;
case V4L2_CID_SATURATION:
strcpy(queryctrl -> name, " SATURATION " );
queryctrl -> type = V4L2_CTRL_TYPE_INTEGER;
queryctrl -> minimum = 0 ;
queryctrl -> maximum = 255 ;
queryctrl -> step = 1 ;
queryctrl -> default_value = 128 ;
break ;
case V4L2_CID_HUE:
strcpy(queryctrl -> name, " HUE " );
queryctrl -> type = V4L2_CTRL_TYPE_INTEGER;
queryctrl -> minimum = - 128 ; /* -180 DEGREE */
queryctrl -> maximum = 127 ; /* 180 DEGREE */
queryctrl -> step = 1 ;
queryctrl -> default_value = 0 ; /* 0 DEGREE */
break ;
case V4L2_CID_AUTOGAIN:
strcpy(queryctrl -> name, " Automatic Gain Control " );
queryctrl -> type = V4L2_CTRL_TYPE_BOOLEAN;
queryctrl -> minimum = 0 ;
queryctrl -> maximum = 1 ;
queryctrl -> step = 1 ;
queryctrl -> default_value = 1 ;
break ;
default :
/* if (id < V4L2_CID_LASTP1)
queryctrl->flags = V4L2_CTRL_FLAG_DISABLED;
else
ret = -EINVAL; */
break ;
} /* end switch (id) */
return ret;
}
/*
* ======== setctrl ========
*/
static int setctrl( void * arg)
{
struct v4l2_control * ctrl = arg;
int ret = 0 ;
switch (ctrl -> id) {
case V4L2_CID_BRIGHTNESS:
ret = settvp5146brightness(ctrl -> value);
break ;
case V4L2_CID_CONTRAST:
ret = settvp5146contrast(ctrl -> value);
break ;
case V4L2_CID_SATURATION:
ret = settvp5146saturation(ctrl -> value);
break ;
case V4L2_CID_HUE:
ret = settvp5146hue(ctrl -> value);
break ;
case V4L2_CID_AUTOGAIN:
ret = enabletvp5146agc(ctrl -> value);
break ;
default :
ret = - EINVAL;
break ;
}
return ret;
}
/*
* ======== settvp5146amuxmode ========
*/
static int settvp5146amuxmode( int arg)
{
u8 input_sel;
int opmode = 0 ;
int ret,value;
if (arg == TVP5146_AMUX_COMPOSITE) { /* composite */
input_sel = 0x00 ;
opmode = 0x30 ;
} else if (arg == TVP5146_AMUX_SVIDEO) { /* s-video */
input_sel = 0x01 ;
} else {
return - EINVAL;
}
ret = i2c_write_reg( & tvp5146_i2c_client, 0x02 , opmode);
ret = i2c_write_reg( & tvp5146_i2c_client, 0x00 , input_sel);
ret |= i2c_read_reg( & tvp5146_i2c_client, 0x03 , & value);
value = (value & ~ 0x10 ) | 0x40 ;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x03 , value);
return ret;
}
/*
* ======== settvp5146brightness ========
*/
static int settvp5146brightness( int arg)
{
int ret = 0 ;
u8 brightness = (u8) arg;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x09 , brightness);
return ret;
}
/*
* ======== settvp5146contrast ========
*/
static int settvp5146contrast( int arg)
{
int ret = 0 ;
u8 contrast = (u8) arg;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x0C , contrast);
return ret;
}
/*
* ======== settvp5146hue ========
*/
static int settvp5146hue( int arg)
{
int ret = 0 ;
u8 hue = (u8) arg;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x0C , hue);
return ret;
}
static int settvp5146saturation( int arg)
{
int ret = 0 ;
u8 saturation = (u8) arg;
ret = i2c_write_reg( & tvp5146_i2c_client, 0x0A , saturation);
return ret;
}
static int settvp5146std( int arg)
{
int ret = 0 ;
u8 std = (u8) arg & 0xF ; /* the 4th-bit is for squre pixel sampling */
int fmt = 0 ;
/* setup the sampling rate: 601 or square pixel */
debug_print(KERN_INFO " reading i2c registers.\n " );
/* First tests should be against specific std */
if (std == V4L2_STD_ALL) {
fmt = 0 ; /* Autodetect mode */
} else if (std & V4L2_STD_PAL_M) {
fmt = 0x6 ;
} else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
fmt = 0x8 ;
} else {
/* Then, test against generic ones */
if (std & V4L2_STD_NTSC) {
fmt = 0x2 ;
} else if (std & V4L2_STD_PAL) {
fmt = 0x4 ;
} else if (std & V4L2_STD_SECAM) {
fmt = 0xc ;
}
}
/* setup the video standard */
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x28 , fmt);
/* if autoswitch mode, enable all modes for autoswitch */
if (std == TVP5146_MODE_AUTO) {
u8 mask = 0x3C ; /* enable autoswitch for all standards */
ret = i2c_write_reg( & tvp5146_i2c_client, 0x04 , mask);
}
return ret;
}
/*
* ======== setup656sync ========
*/
// Un
// static int setup656sync(int enable)
static int setup656sync(tvp5146_params * tvp5146params)
{
int output1, output2, output3, output4;
int output5, output6;
int ret = 0 ;
if ((tvp5146params -> enablebt656sync)
&& (tvp5146params -> data_width == TVP5146_WIDTH_8BIT)) {
output1 = 0x47 ;
output4 = 0xFF ;
output6 = 0 ;
} else {
output1 = 0x40 ;
output4 = 0xAF ;
output6 = 0x1E ;
}
output2 = 0x69 ; /* enable clock, enable Y[9:0] */
output3 = 0x0 ;
output5 = 0x4 ;
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0D , output1);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x03 , output2);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0E , output3);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x36 , output4);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0E , output3);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x1B , output5);
// ret |= i2c_write_reg(&tvp5146_i2c_client, 0x1B, output5);
return ret;
}
/*
* ======== tvp5146_ctrl ========
*/
int tvp5146_ctrl(tvp5146_cmd cmd, void * arg)
{
int ret = 0 ;
switch (cmd) {
case TVP5146_CONFIG:
ret = configtvp5146(arg);
break ;
case TVP5146_RESET:
ret = resettvp5146();
break ;
case TVP5146_POWERDOWN:
ret = powerdowntvp5146( * ( int * )arg);
break ;
case TVP5146_SET_AMUXMODE:
ret = settvp5146amuxmode( * ( int * )arg);
break ;
case TVP5146_SET_BRIGHTNESS:
ret = settvp5146brightness( * ( int * )arg);
break ;
case TVP5146_SET_CONTRAST:
ret = settvp5146contrast( * ( int * )arg);
break ;
case TVP5146_SET_HUE:
ret = settvp5146hue( * ( int * )arg);
break ;
case TVP5146_SET_SATURATION:
ret = settvp5146saturation( * ( int * )arg);
break ;
case TVP5146_SET_AGC:
ret = enabletvp5146agc( * ( int * )arg);
break ;
case TVP5146_SET_VIDEOSTD:
ret = settvp5146std( * ( int * )arg);
break ;
case TVP5146_CLR_LOSTLOCK:
ret = clrtvp5146lostlock();
break ;
case TVP5146_GET_STATUS:
ret = gettvp5146status(arg);
break ;
case TVP5146_GET_STD:
ret = gettvp5146std(arg);
break ;
case VIDIOC_QUERYCTRL:
ret = queryctrl(arg);
break ;
case TVP5146_INIT:
ret = tvp5146_i2c_init();
break ;
case VIDIOC_G_CTRL:
ret = getctrl(arg);
break ;
case VIDIOC_S_CTRL:
ret = setctrl(arg);
break ;
default :
ret = - EINVAL;
}
return ret;
}
static int i2c_read_reg( struct i2c_client * client, u8 reg, u8 * val)
{
int err = 0 ;
struct i2c_msg msg[ 1 ];
unsigned char data[ 1 ];
if ( ! client -> adapter) {
err = - ENODEV;
} else {
msg -> addr = client -> addr;
msg -> flags = 0 ;
msg -> len = 1 ;
msg -> buf = data;
data[ 0 ] = reg;
err = i2c_transfer(client -> adapter, msg, 1 );
if (err >= 0 ) {
msg -> flags = I2C_M_RD;
err = i2c_transfer(client -> adapter, msg, 1 );
if (err >= 0 ) {
* val = data[ 0 ];
}
}
}
return err;
}
static int i2c_write_reg( struct i2c_client * client, u8 reg, u8 val)
{
int err = 0 ;
struct i2c_msg msg[ 1 ];
unsigned char data[ 2 ];
if ( ! client -> adapter) {
err = - ENODEV;
} else {
msg -> addr = client -> addr;
msg -> flags = 0 ;
msg -> len = 2 ;
msg -> buf = data;
data[ 0 ] = reg;
data[ 1 ] = val;
err = i2c_transfer(client -> adapter, msg, 1 );
}
debug_print(KERN_INFO " i2c data write \n " );
return err;
}
static int _i2c_attach_client( struct i2c_client * client,
struct i2c_driver * driver,
struct i2c_adapter * adap, int addr)
{
int err = 0 ;
if (client -> adapter) {
err = - EBUSY; /* our client is already attached */
} else {
client -> addr = addr;
client -> flags = I2C_CLIENT_ALLOW_USE;
client -> driver = driver;
client -> adapter = adap;
err = i2c_attach_client(client);
if (err) {
client -> adapter = NULL;
}
}
return err;
}
static int _i2c_detach_client( struct i2c_client * client)
{
int err = 0 ;
if ( ! client -> adapter) {
return - ENODEV; /* our client isn't attached */
} else {
err = i2c_detach_client(client);
client -> adapter = NULL;
}
return err;
}
static int tvp5146_i2c_probe_adapter( struct i2c_adapter * adap)
{
int ret;
ret = _i2c_attach_client( & tvp5146_i2c_client, & tvp5146_i2c_driver,
adap, 0x5D );
// ret |= gpio_set_direction(41,0); // linger 2007.11.29 GPIO pins are exchanged on
V1. 1
// ret |= __gpio_set(41,1);
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x03 , 0x69 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x08 , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x09 , 0x80 );
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0a , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0b , 0x00 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0c , 0x80 ); // phoenix 2008.01.04
modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0d , 0x47 ); // phoenix 2008.01.02 modified
ret |= i2c_write_reg( & tvp5146_i2c_client, 0x0F , 0x02 );
return ret;
// end
}
/*
static int tvp5146_i2c_init(void)
{
int err;
struct i2c_driver *driver = &tvp5146_i2c_driver;
driver->owner = THIS_MODULE;
strlcpy(driver->name, "TVP5150 Video Decoder I2C driver",
sizeof(driver->name));
driver->id = I2C_DRIVERID_EXP0;
driver->flags = I2C_DF_NOTIFY;
driver->attach_adapter = tvp5146_i2c_probe_adapter;
driver->detach_client = _i2c_detach_client;
err = i2c_add_driver(driver);
if (err) {
debug_print(KERN_ERR
"Failed to register TVP5146 I2C client.\n");
}
debug_print(KERN_INFO "tvp5146 driver registered.\n");
return err;
}
static void tvp5146_i2c_cleanup(void)
{
struct i2c_driver *driver = &tvp5146_i2c_driver;
i2c_detach_client(&tvp5146_i2c_client);
i2c_del_driver(driver);
tvp5146_i2c_client.adapter = NULL;
}
*/
/*
* ======== tvp5146_i2c_init ========
*/
/* This function is used initialize TVP5146 i2c client */
static int tvp5146_i2c_init( void )
{
int err = 0 ;
struct i2c_driver * driver = & tvp5146_i2c_driver;
driver -> owner = THIS_MODULE;
strlcpy(driver -> name, " TVP5150 Video Decoder I2C driver " ,
sizeof (driver -> name));
driver -> id = I2C_DRIVERID_EXP0;
driver -> flags = I2C_DF_NOTIFY;
driver -> attach_adapter = tvp5146_i2c_probe_adapter;
driver -> detach_client = _i2c_detach_client;
err = i2c_add_driver(driver);
if (err) {
debug_print(KERN_ERR
" Failed to register TVP5150 I2C client.\n " );
}
debug_print(KERN_INFO " tvp5150 driver registered.\n " );
printk( " TVP5150 support for SEED_DVS6446 by Stephen Zhang\n " );
return err;
return err;
}
/*
* ======== tvp5146_i2c_cleanup ========
*/
/* This function is used detach TVP5146 i2c client */
static void tvp5146_i2c_cleanup( void )
{
struct i2c_driver * driver = & tvp5146_i2c_driver;
if (tvp5146_i2c_registration) {
i2c_detach_client( & tvp5146_i2c_client);
i2c_del_driver(driver);
tvp5146_i2c_client.adapter = NULL;
tvp5146_i2c_registration = TVP5146_I2C_UNREGISTERED;
}
}
// module_init(tvp5146_i2c_init);
module_exit(tvp5146_i2c_cleanup);
EXPORT_SYMBOL(tvp5146_ctrl);
MODULE_LICENSE( " GPL " );
/* ************************************************************************ */
/* End of file */
/* ************************************************************************ */