2005-11-13 16:07:56 -08:00
/* cx25840 - Conexant CX25840 audio/video decoder driver
*
* Copyright ( C ) 2004 Ulf Eklund
*
* Based on the saa7115 driver and on the first verison of Chris Kennedy ' s
* cx25840 driver .
*
* Changes by Tyler Trafford < tatrafford @ comcast . net >
* - cleanup / rewrite for V4L2 API ( 2005 )
*
* VBI support by Hans Verkuil < hverkuil @ xs4all . nl > .
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/videodev2.h>
# include <linux/i2c.h>
# include <media/v4l2-common.h>
2006-03-25 10:26:09 -03:00
# include <media/cx25840.h>
2005-11-13 16:07:56 -08:00
2006-03-25 10:26:09 -03:00
# include "cx25840-core.h"
2005-11-13 16:07:56 -08:00
MODULE_DESCRIPTION ( " Conexant CX25840 audio/video decoder driver " ) ;
2005-11-13 16:08:05 -08:00
MODULE_AUTHOR ( " Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford " ) ;
2005-11-13 16:07:56 -08:00
MODULE_LICENSE ( " GPL " ) ;
static unsigned short normal_i2c [ ] = { 0x88 > > 1 , I2C_CLIENT_END } ;
2006-01-15 06:56:15 -02:00
static int cx25840_debug ;
2005-11-13 16:07:56 -08:00
2006-01-11 22:41:36 -02:00
module_param_named ( debug , cx25840_debug , int , 0644 ) ;
2005-11-13 16:07:56 -08:00
2006-01-09 15:32:40 -02:00
MODULE_PARM_DESC ( debug , " Debugging messages [0=Off (default) 1=On] " ) ;
2005-11-13 16:07:56 -08:00
I2C_CLIENT_INSMOD ;
/* ----------------------------------------------------------------------- */
int cx25840_write ( struct i2c_client * client , u16 addr , u8 value )
{
u8 buffer [ 3 ] ;
buffer [ 0 ] = addr > > 8 ;
buffer [ 1 ] = addr & 0xff ;
buffer [ 2 ] = value ;
return i2c_master_send ( client , buffer , 3 ) ;
}
int cx25840_write4 ( struct i2c_client * client , u16 addr , u32 value )
{
u8 buffer [ 6 ] ;
buffer [ 0 ] = addr > > 8 ;
buffer [ 1 ] = addr & 0xff ;
buffer [ 2 ] = value > > 24 ;
buffer [ 3 ] = ( value > > 16 ) & 0xff ;
buffer [ 4 ] = ( value > > 8 ) & 0xff ;
buffer [ 5 ] = value & 0xff ;
return i2c_master_send ( client , buffer , 6 ) ;
}
u8 cx25840_read ( struct i2c_client * client , u16 addr )
{
u8 buffer [ 2 ] ;
buffer [ 0 ] = addr > > 8 ;
buffer [ 1 ] = addr & 0xff ;
if ( i2c_master_send ( client , buffer , 2 ) < 2 )
return 0 ;
if ( i2c_master_recv ( client , buffer , 1 ) < 1 )
return 0 ;
return buffer [ 0 ] ;
}
u32 cx25840_read4 ( struct i2c_client * client , u16 addr )
{
u8 buffer [ 4 ] ;
buffer [ 0 ] = addr > > 8 ;
buffer [ 1 ] = addr & 0xff ;
if ( i2c_master_send ( client , buffer , 2 ) < 2 )
return 0 ;
if ( i2c_master_recv ( client , buffer , 4 ) < 4 )
return 0 ;
return ( buffer [ 0 ] < < 24 ) | ( buffer [ 1 ] < < 16 ) |
( buffer [ 2 ] < < 8 ) | buffer [ 3 ] ;
}
2006-04-22 10:22:46 -03:00
int cx25840_and_or ( struct i2c_client * client , u16 addr , unsigned and_mask ,
2005-11-13 16:07:56 -08:00
u8 or_value )
{
return cx25840_write ( client , addr ,
( cx25840_read ( client , addr ) & and_mask ) |
or_value ) ;
}
/* ----------------------------------------------------------------------- */
2006-01-09 15:25:42 -02:00
static int set_input ( struct i2c_client * client , enum cx25840_video_input vid_input ,
enum cx25840_audio_input aud_input ) ;
2006-04-22 10:22:46 -03:00
static void log_audio_status ( struct i2c_client * client ) ;
static void log_video_status ( struct i2c_client * client ) ;
2005-11-13 16:07:56 -08:00
/* ----------------------------------------------------------------------- */
2006-01-09 15:32:41 -02:00
static void init_dll1 ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
/* This is the Hauppauge sequence used to
* initialize the Delay Lock Loop 1 ( ADC DLL ) . */
cx25840_write ( client , 0x159 , 0x23 ) ;
cx25840_write ( client , 0x15a , 0x87 ) ;
cx25840_write ( client , 0x15b , 0x06 ) ;
cx25840_write ( client , 0x159 , 0xe1 ) ;
cx25840_write ( client , 0x15a , 0x86 ) ;
cx25840_write ( client , 0x159 , 0xe0 ) ;
cx25840_write ( client , 0x159 , 0xe1 ) ;
cx25840_write ( client , 0x15b , 0x10 ) ;
}
2006-01-09 15:32:41 -02:00
static void init_dll2 ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
/* This is the Hauppauge sequence used to
* initialize the Delay Lock Loop 2 ( ADC DLL ) . */
cx25840_write ( client , 0x15d , 0xe3 ) ;
cx25840_write ( client , 0x15e , 0x86 ) ;
cx25840_write ( client , 0x15f , 0x06 ) ;
cx25840_write ( client , 0x15d , 0xe1 ) ;
cx25840_write ( client , 0x15d , 0xe0 ) ;
cx25840_write ( client , 0x15d , 0xe1 ) ;
}
2006-04-22 10:22:46 -03:00
static void cx25836_initialize ( struct i2c_client * client )
{
/* reset configuration is described on page 3-77 of the CX25836 datasheet */
/* 2. */
cx25840_and_or ( client , 0x000 , ~ 0x01 , 0x01 ) ;
cx25840_and_or ( client , 0x000 , ~ 0x01 , 0x00 ) ;
/* 3a. */
cx25840_and_or ( client , 0x15a , ~ 0x70 , 0x00 ) ;
/* 3b. */
cx25840_and_or ( client , 0x15b , ~ 0x1e , 0x06 ) ;
/* 3c. */
cx25840_and_or ( client , 0x159 , ~ 0x02 , 0x02 ) ;
/* 3d. */
/* There should be a 10-us delay here, but since the
i2c bus already has a 10 - us delay we don ' t need to do
anything */
/* 3e. */
cx25840_and_or ( client , 0x159 , ~ 0x02 , 0x00 ) ;
/* 3f. */
cx25840_and_or ( client , 0x159 , ~ 0xc0 , 0xc0 ) ;
/* 3g. */
cx25840_and_or ( client , 0x159 , ~ 0x01 , 0x00 ) ;
cx25840_and_or ( client , 0x159 , ~ 0x01 , 0x01 ) ;
/* 3h. */
cx25840_and_or ( client , 0x15b , ~ 0x1e , 0x10 ) ;
}
2005-11-13 16:07:56 -08:00
static void cx25840_initialize ( struct i2c_client * client , int loadfw )
{
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
/* datasheet startup in numbered steps, refer to page 3-77 */
/* 2. */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x00 ) ;
/* The default of this register should be 4, but I get 0 instead.
* Set this register to 4 manually . */
cx25840_write ( client , 0x000 , 0x04 ) ;
/* 3. */
init_dll1 ( client ) ;
init_dll2 ( client ) ;
cx25840_write ( client , 0x136 , 0x0a ) ;
/* 4. */
cx25840_write ( client , 0x13c , 0x01 ) ;
cx25840_write ( client , 0x13c , 0x00 ) ;
/* 5. */
if ( loadfw )
cx25840_loadfw ( client ) ;
/* 6. */
cx25840_write ( client , 0x115 , 0x8c ) ;
cx25840_write ( client , 0x116 , 0x07 ) ;
cx25840_write ( client , 0x118 , 0x02 ) ;
/* 7. */
cx25840_write ( client , 0x4a5 , 0x80 ) ;
cx25840_write ( client , 0x4a5 , 0x00 ) ;
cx25840_write ( client , 0x402 , 0x00 ) ;
/* 8. */
2006-03-16 20:23:47 -03:00
cx25840_and_or ( client , 0x401 , ~ 0x18 , 0 ) ;
cx25840_and_or ( client , 0x4a2 , ~ 0x10 , 0x10 ) ;
/* steps 8c and 8d are done in change_input() */
2005-11-13 16:07:56 -08:00
/* 10. */
cx25840_write ( client , 0x8d3 , 0x1f ) ;
cx25840_write ( client , 0x8e3 , 0x03 ) ;
cx25840_vbi_setup ( client ) ;
/* trial and error says these are needed to get audio */
cx25840_write ( client , 0x914 , 0xa0 ) ;
cx25840_write ( client , 0x918 , 0xa0 ) ;
cx25840_write ( client , 0x919 , 0x01 ) ;
/* stereo prefered */
cx25840_write ( client , 0x809 , 0x04 ) ;
/* AC97 shift */
cx25840_write ( client , 0x8cf , 0x0f ) ;
2006-01-09 15:25:42 -02:00
/* (re)set input */
set_input ( client , state - > vid_input , state - > aud_input ) ;
2005-11-13 16:07:56 -08:00
/* start microcontroller */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x10 ) ;
}
/* ----------------------------------------------------------------------- */
static void input_change ( struct i2c_client * client )
{
2005-12-01 00:51:42 -08:00
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
2005-11-13 16:07:56 -08:00
v4l2_std_id std = cx25840_get_v4lstd ( client ) ;
2006-03-16 20:23:47 -03:00
/* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
if ( std & V4L2_STD_SECAM ) {
cx25840_write ( client , 0x402 , 0 ) ;
}
else {
cx25840_write ( client , 0x402 , 0x04 ) ;
cx25840_write ( client , 0x49f , ( std & V4L2_STD_NTSC ) ? 0x14 : 0x11 ) ;
}
cx25840_and_or ( client , 0x401 , ~ 0x60 , 0 ) ;
cx25840_and_or ( client , 0x401 , ~ 0x60 , 0x60 ) ;
2005-12-01 00:51:42 -08:00
/* Note: perhaps V4L2_STD_PAL_M should be handled as V4L2_STD_NTSC
instead of V4L2_STD_PAL . Someone needs to test this . */
2005-11-13 16:07:56 -08:00
if ( std & V4L2_STD_PAL ) {
/* Follow tuner change procedure for PAL */
cx25840_write ( client , 0x808 , 0xff ) ;
cx25840_write ( client , 0x80b , 0x10 ) ;
} else if ( std & V4L2_STD_SECAM ) {
/* Select autodetect for SECAM */
cx25840_write ( client , 0x808 , 0xff ) ;
cx25840_write ( client , 0x80b , 0x10 ) ;
} else if ( std & V4L2_STD_NTSC ) {
2006-02-07 06:48:40 -02:00
/* Certain Hauppauge PVR150 models have a hardware bug
that causes audio to drop out . For these models the
audio standard must be set explicitly .
To be precise : it affects cards with tuner models
85 , 99 and 112 ( model numbers from tveeprom ) . */
int hw_fix = state - > pvr150_workaround ;
if ( std = = V4L2_STD_NTSC_M_JP ) {
2005-12-01 00:51:42 -08:00
/* Japan uses EIAJ audio standard */
2006-02-07 06:48:40 -02:00
cx25840_write ( client , 0x808 , hw_fix ? 0x2f : 0xf7 ) ;
} else if ( std = = V4L2_STD_NTSC_M_KR ) {
/* South Korea uses A2 audio standard */
cx25840_write ( client , 0x808 , hw_fix ? 0x3f : 0xf8 ) ;
2005-12-01 00:51:42 -08:00
} else {
/* Others use the BTSC audio standard */
2006-02-07 06:48:40 -02:00
cx25840_write ( client , 0x808 , hw_fix ? 0x1f : 0xf6 ) ;
2005-12-01 00:51:42 -08:00
}
2005-11-13 16:07:56 -08:00
cx25840_write ( client , 0x80b , 0x00 ) ;
}
if ( cx25840_read ( client , 0x803 ) & 0x10 ) {
/* restart audio decoder microcontroller */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x00 ) ;
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x10 ) ;
}
}
2006-01-09 15:25:42 -02:00
static int set_input ( struct i2c_client * client , enum cx25840_video_input vid_input ,
enum cx25840_audio_input aud_input )
2005-11-13 16:07:56 -08:00
{
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
2006-01-09 15:25:42 -02:00
u8 is_composite = ( vid_input > = CX25840_COMPOSITE1 & &
vid_input < = CX25840_COMPOSITE8 ) ;
u8 reg ;
2005-11-13 16:07:56 -08:00
2006-01-11 22:41:36 -02:00
v4l_dbg ( 1 , cx25840_debug , client , " decoder set video input %d, audio input %d \n " ,
2006-01-09 15:25:42 -02:00
vid_input , aud_input ) ;
2005-11-13 16:07:56 -08:00
2006-01-09 15:25:42 -02:00
if ( is_composite ) {
reg = 0xf0 + ( vid_input - CX25840_COMPOSITE1 ) ;
} else {
int luma = vid_input & 0xf0 ;
int chroma = vid_input & 0xf00 ;
2005-11-13 16:07:56 -08:00
2006-01-09 15:25:42 -02:00
if ( ( vid_input & ~ 0xff0 ) | |
luma < CX25840_SVIDEO_LUMA1 | | luma > CX25840_SVIDEO_LUMA4 | |
chroma < CX25840_SVIDEO_CHROMA4 | | chroma > CX25840_SVIDEO_CHROMA8 ) {
2006-01-09 15:32:40 -02:00
v4l_err ( client , " 0x%04x is not a valid video input! \n " , vid_input ) ;
2006-01-09 15:25:42 -02:00
return - EINVAL ;
2005-11-13 16:07:56 -08:00
}
2006-01-09 15:25:42 -02:00
reg = 0xf0 + ( ( luma - CX25840_SVIDEO_LUMA1 ) > > 4 ) ;
if ( chroma > = CX25840_SVIDEO_CHROMA7 ) {
reg & = 0x3f ;
reg | = ( chroma - CX25840_SVIDEO_CHROMA7 ) > > 2 ;
2005-11-13 16:07:56 -08:00
} else {
2006-01-09 15:25:42 -02:00
reg & = 0xcf ;
reg | = ( chroma - CX25840_SVIDEO_CHROMA4 ) > > 4 ;
2005-11-13 16:07:56 -08:00
}
2006-01-09 15:25:42 -02:00
}
2005-11-13 16:07:56 -08:00
2006-01-09 15:25:42 -02:00
switch ( aud_input ) {
case CX25840_AUDIO_SERIAL :
/* do nothing, use serial audio input */
2005-11-13 16:07:56 -08:00
break ;
2006-01-09 15:25:42 -02:00
case CX25840_AUDIO4 : reg & = ~ 0x30 ; break ;
case CX25840_AUDIO5 : reg & = ~ 0x30 ; reg | = 0x10 ; break ;
case CX25840_AUDIO6 : reg & = ~ 0x30 ; reg | = 0x20 ; break ;
case CX25840_AUDIO7 : reg & = ~ 0xc0 ; break ;
case CX25840_AUDIO8 : reg & = ~ 0xc0 ; reg | = 0x40 ; break ;
2005-11-13 16:07:56 -08:00
default :
2006-01-09 15:32:40 -02:00
v4l_err ( client , " 0x%04x is not a valid audio input! \n " , aud_input ) ;
2005-11-13 16:07:56 -08:00
return - EINVAL ;
}
2006-01-09 15:25:42 -02:00
cx25840_write ( client , 0x103 , reg ) ;
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx25840_and_or ( client , 0x401 , ~ 0x6 , is_composite ? 0 : 0x02 ) ;
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
cx25840_and_or ( client , 0x102 , ~ 0x2 , ( reg & 0x80 ) = = 0 ? 2 : 0 ) ;
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
if ( ( reg & 0xc0 ) ! = 0xc0 & & ( reg & 0x30 ) ! = 0x30 )
cx25840_and_or ( client , 0x102 , ~ 0x4 , 4 ) ;
else
cx25840_and_or ( client , 0x102 , ~ 0x4 , 0 ) ;
state - > vid_input = vid_input ;
state - > aud_input = aud_input ;
2006-04-22 10:22:46 -03:00
if ( ! state - > is_cx25836 ) {
cx25840_audio_set_path ( client ) ;
input_change ( client ) ;
}
2005-11-13 16:07:56 -08:00
return 0 ;
}
/* ----------------------------------------------------------------------- */
static int set_v4lstd ( struct i2c_client * client , v4l2_std_id std )
{
2005-12-19 08:54:11 -02:00
u8 fmt = 0 ; /* zero is autodetect */
/* First tests should be against specific std */
2006-02-07 06:48:40 -02:00
if ( std = = V4L2_STD_NTSC_M_JP ) {
2005-12-19 08:54:11 -02:00
fmt = 0x2 ;
2006-02-07 06:48:40 -02:00
} else if ( std = = V4L2_STD_NTSC_443 ) {
2005-12-19 08:54:11 -02:00
fmt = 0x3 ;
2006-02-07 06:48:40 -02:00
} else if ( std = = V4L2_STD_PAL_M ) {
2005-12-19 08:54:11 -02:00
fmt = 0x5 ;
2006-02-07 06:48:40 -02:00
} else if ( std = = V4L2_STD_PAL_N ) {
2005-12-19 08:54:11 -02:00
fmt = 0x6 ;
2006-02-07 06:48:40 -02:00
} else if ( std = = V4L2_STD_PAL_Nc ) {
2005-12-19 08:54:11 -02:00
fmt = 0x7 ;
2006-02-07 06:48:40 -02:00
} else if ( std = = V4L2_STD_PAL_60 ) {
2005-12-19 08:54:11 -02:00
fmt = 0x8 ;
} else {
/* Then, test against generic ones */
if ( std & V4L2_STD_NTSC ) {
fmt = 0x1 ;
} else if ( std & V4L2_STD_PAL ) {
fmt = 0x4 ;
} else if ( std & V4L2_STD_SECAM ) {
fmt = 0xc ;
}
2005-11-13 16:07:56 -08:00
}
2006-03-16 20:23:47 -03:00
/* Follow step 9 of section 3.16 in the cx25840 datasheet.
Without this PAL may display a vertical ghosting effect .
This happens for example with the Yuan MPC622 . */
if ( fmt > = 4 & & fmt < 8 ) {
/* Set format to NTSC-M */
cx25840_and_or ( client , 0x400 , ~ 0xf , 1 ) ;
/* Turn off LCOMB */
cx25840_and_or ( client , 0x47b , ~ 6 , 0 ) ;
}
2005-11-13 16:07:56 -08:00
cx25840_and_or ( client , 0x400 , ~ 0xf , fmt ) ;
cx25840_vbi_setup ( client ) ;
return 0 ;
}
v4l2_std_id cx25840_get_v4lstd ( struct i2c_client * client )
{
2006-04-22 10:22:46 -03:00
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
2005-11-13 16:07:56 -08:00
/* check VID_FMT_SEL first */
u8 fmt = cx25840_read ( client , 0x400 ) & 0xf ;
if ( ! fmt ) {
/* check AFD_FMT_STAT if set to autodetect */
fmt = cx25840_read ( client , 0x40d ) & 0xf ;
}
switch ( fmt ) {
2006-03-16 20:23:47 -03:00
case 0x1 :
{
/* if the audio std is A2-M, then this is the South Korean
NTSC standard */
2006-04-22 10:22:46 -03:00
if ( ! state - > is_cx25836 & & cx25840_read ( client , 0x805 ) = = 2 )
2006-03-16 20:23:47 -03:00
return V4L2_STD_NTSC_M_KR ;
return V4L2_STD_NTSC_M ;
}
2005-11-13 16:07:56 -08:00
case 0x2 : return V4L2_STD_NTSC_M_JP ;
case 0x3 : return V4L2_STD_NTSC_443 ;
case 0x4 : return V4L2_STD_PAL ;
case 0x5 : return V4L2_STD_PAL_M ;
case 0x6 : return V4L2_STD_PAL_N ;
case 0x7 : return V4L2_STD_PAL_Nc ;
case 0x8 : return V4L2_STD_PAL_60 ;
case 0xc : return V4L2_STD_SECAM ;
default : return V4L2_STD_UNKNOWN ;
}
}
/* ----------------------------------------------------------------------- */
static int set_v4lctrl ( struct i2c_client * client , struct v4l2_control * ctrl )
{
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
switch ( ctrl - > id ) {
2006-01-09 15:25:42 -02:00
case CX25840_CID_ENABLE_PVR150_WORKAROUND :
state - > pvr150_workaround = ctrl - > value ;
set_input ( client , state - > vid_input , state - > aud_input ) ;
2005-11-13 16:07:56 -08:00
break ;
case V4L2_CID_BRIGHTNESS :
if ( ctrl - > value < 0 | | ctrl - > value > 255 ) {
2006-01-09 15:32:40 -02:00
v4l_err ( client , " invalid brightness setting %d \n " ,
2005-11-13 16:07:56 -08:00
ctrl - > value ) ;
return - ERANGE ;
}
cx25840_write ( client , 0x414 , ctrl - > value - 128 ) ;
break ;
case V4L2_CID_CONTRAST :
if ( ctrl - > value < 0 | | ctrl - > value > 127 ) {
2006-01-09 15:32:40 -02:00
v4l_err ( client , " invalid contrast setting %d \n " ,
2005-11-13 16:07:56 -08:00
ctrl - > value ) ;
return - ERANGE ;
}
cx25840_write ( client , 0x415 , ctrl - > value < < 1 ) ;
break ;
case V4L2_CID_SATURATION :
if ( ctrl - > value < 0 | | ctrl - > value > 127 ) {
2006-01-09 15:32:40 -02:00
v4l_err ( client , " invalid saturation setting %d \n " ,
2005-11-13 16:07:56 -08:00
ctrl - > value ) ;
return - ERANGE ;
}
cx25840_write ( client , 0x420 , ctrl - > value < < 1 ) ;
cx25840_write ( client , 0x421 , ctrl - > value < < 1 ) ;
break ;
case V4L2_CID_HUE :
if ( ctrl - > value < - 127 | | ctrl - > value > 127 ) {
2006-01-09 15:32:40 -02:00
v4l_err ( client , " invalid hue setting %d \n " , ctrl - > value ) ;
2005-11-13 16:07:56 -08:00
return - ERANGE ;
}
cx25840_write ( client , 0x422 , ctrl - > value ) ;
break ;
case V4L2_CID_AUDIO_VOLUME :
case V4L2_CID_AUDIO_BASS :
case V4L2_CID_AUDIO_TREBLE :
case V4L2_CID_AUDIO_BALANCE :
case V4L2_CID_AUDIO_MUTE :
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
return - EINVAL ;
2005-11-13 16:07:56 -08:00
return cx25840_audio ( client , VIDIOC_S_CTRL , ctrl ) ;
2006-01-09 15:25:44 -02:00
default :
return - EINVAL ;
2005-11-13 16:07:56 -08:00
}
return 0 ;
}
static int get_v4lctrl ( struct i2c_client * client , struct v4l2_control * ctrl )
{
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
switch ( ctrl - > id ) {
2006-01-09 15:25:42 -02:00
case CX25840_CID_ENABLE_PVR150_WORKAROUND :
ctrl - > value = state - > pvr150_workaround ;
2005-11-13 16:07:56 -08:00
break ;
case V4L2_CID_BRIGHTNESS :
2006-01-09 15:32:44 -02:00
ctrl - > value = ( s8 ) cx25840_read ( client , 0x414 ) + 128 ;
2005-11-13 16:07:56 -08:00
break ;
case V4L2_CID_CONTRAST :
ctrl - > value = cx25840_read ( client , 0x415 ) > > 1 ;
break ;
case V4L2_CID_SATURATION :
ctrl - > value = cx25840_read ( client , 0x420 ) > > 1 ;
break ;
case V4L2_CID_HUE :
2006-01-09 15:32:43 -02:00
ctrl - > value = ( s8 ) cx25840_read ( client , 0x422 ) ;
2005-11-13 16:07:56 -08:00
break ;
case V4L2_CID_AUDIO_VOLUME :
case V4L2_CID_AUDIO_BASS :
case V4L2_CID_AUDIO_TREBLE :
case V4L2_CID_AUDIO_BALANCE :
case V4L2_CID_AUDIO_MUTE :
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
return - EINVAL ;
2005-11-13 16:07:56 -08:00
return cx25840_audio ( client , VIDIOC_G_CTRL , ctrl ) ;
default :
return - EINVAL ;
}
return 0 ;
}
/* ----------------------------------------------------------------------- */
static int get_v4lfmt ( struct i2c_client * client , struct v4l2_format * fmt )
{
switch ( fmt - > type ) {
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE :
return cx25840_vbi ( client , VIDIOC_G_FMT , fmt ) ;
default :
return - EINVAL ;
}
return 0 ;
}
static int set_v4lfmt ( struct i2c_client * client , struct v4l2_format * fmt )
{
struct v4l2_pix_format * pix ;
int HSC , VSC , Vsrc , Hsrc , filter , Vlines ;
int is_pal = ! ( cx25840_get_v4lstd ( client ) & V4L2_STD_NTSC ) ;
switch ( fmt - > type ) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE :
pix = & ( fmt - > fmt . pix ) ;
Vsrc = ( cx25840_read ( client , 0x476 ) & 0x3f ) < < 4 ;
Vsrc | = ( cx25840_read ( client , 0x475 ) & 0xf0 ) > > 4 ;
Hsrc = ( cx25840_read ( client , 0x472 ) & 0x3f ) < < 4 ;
Hsrc | = ( cx25840_read ( client , 0x471 ) & 0xf0 ) > > 4 ;
Vlines = pix - > height + ( is_pal ? 4 : 7 ) ;
if ( ( pix - > width * 16 < Hsrc ) | | ( Hsrc < pix - > width ) | |
( Vlines * 8 < Vsrc ) | | ( Vsrc < Vlines ) ) {
2006-01-09 15:32:40 -02:00
v4l_err ( client , " %dx%d is not a valid size! \n " ,
2005-11-13 16:07:56 -08:00
pix - > width , pix - > height ) ;
return - ERANGE ;
}
HSC = ( Hsrc * ( 1 < < 20 ) ) / pix - > width - ( 1 < < 20 ) ;
VSC = ( 1 < < 16 ) - ( Vsrc * ( 1 < < 9 ) / Vlines - ( 1 < < 9 ) ) ;
VSC & = 0x1fff ;
if ( pix - > width > = 385 )
filter = 0 ;
else if ( pix - > width > 192 )
filter = 1 ;
else if ( pix - > width > 96 )
filter = 2 ;
else
filter = 3 ;
2006-01-11 22:41:36 -02:00
v4l_dbg ( 1 , cx25840_debug , client , " decoder set size %dx%d -> scale %ux%u \n " ,
2005-11-13 16:07:56 -08:00
pix - > width , pix - > height , HSC , VSC ) ;
/* HSCALE=HSC */
cx25840_write ( client , 0x418 , HSC & 0xff ) ;
cx25840_write ( client , 0x419 , ( HSC > > 8 ) & 0xff ) ;
cx25840_write ( client , 0x41a , HSC > > 16 ) ;
/* VSCALE=VSC */
cx25840_write ( client , 0x41c , VSC & 0xff ) ;
cx25840_write ( client , 0x41d , VSC > > 8 ) ;
/* VS_INTRLACE=1 VFILT=filter */
cx25840_write ( client , 0x41e , 0x8 | filter ) ;
break ;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE :
return cx25840_vbi ( client , VIDIOC_S_FMT , fmt ) ;
case V4L2_BUF_TYPE_VBI_CAPTURE :
return cx25840_vbi ( client , VIDIOC_S_FMT , fmt ) ;
default :
return - EINVAL ;
}
return 0 ;
}
/* ----------------------------------------------------------------------- */
2006-04-22 10:22:46 -03:00
static struct v4l2_queryctrl cx25836_qctrl [ ] = {
2006-01-09 15:32:41 -02:00
{
. id = V4L2_CID_BRIGHTNESS ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Brightness " ,
. minimum = 0 ,
. maximum = 255 ,
. step = 1 ,
. default_value = 128 ,
. flags = 0 ,
} , {
. id = V4L2_CID_CONTRAST ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Contrast " ,
. minimum = 0 ,
2006-02-27 00:08:20 -03:00
. maximum = 127 ,
2006-01-09 15:32:41 -02:00
. step = 1 ,
. default_value = 64 ,
. flags = 0 ,
} , {
. id = V4L2_CID_SATURATION ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Saturation " ,
. minimum = 0 ,
2006-02-27 00:08:20 -03:00
. maximum = 127 ,
2006-01-09 15:32:41 -02:00
. step = 1 ,
. default_value = 64 ,
. flags = 0 ,
} , {
. id = V4L2_CID_HUE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Hue " ,
. minimum = - 128 ,
. maximum = 127 ,
. step = 1 ,
. default_value = 0 ,
. flags = 0 ,
2006-04-22 10:22:46 -03:00
} ,
} ;
static struct v4l2_queryctrl cx25840_qctrl [ ] = {
{
2006-01-09 15:32:41 -02:00
. id = V4L2_CID_AUDIO_VOLUME ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Volume " ,
. minimum = 0 ,
. maximum = 65535 ,
. step = 65535 / 100 ,
. default_value = 58880 ,
. flags = 0 ,
} , {
. id = V4L2_CID_AUDIO_BALANCE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Balance " ,
. minimum = 0 ,
. maximum = 65535 ,
. step = 65535 / 100 ,
. default_value = 32768 ,
. flags = 0 ,
} , {
. id = V4L2_CID_AUDIO_MUTE ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. name = " Mute " ,
. minimum = 0 ,
. maximum = 1 ,
. step = 1 ,
. default_value = 1 ,
. flags = 0 ,
} , {
. id = V4L2_CID_AUDIO_BASS ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Bass " ,
. minimum = 0 ,
. maximum = 65535 ,
. step = 65535 / 100 ,
. default_value = 32768 ,
} , {
. id = V4L2_CID_AUDIO_TREBLE ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Treble " ,
. minimum = 0 ,
. maximum = 65535 ,
. step = 65535 / 100 ,
. default_value = 32768 ,
} ,
} ;
/* ----------------------------------------------------------------------- */
2005-11-13 16:07:56 -08:00
static int cx25840_command ( struct i2c_client * client , unsigned int cmd ,
void * arg )
{
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
struct v4l2_tuner * vt = arg ;
2006-03-25 10:26:09 -03:00
struct v4l2_routing * route = arg ;
2005-11-13 16:07:56 -08:00
switch ( cmd ) {
# ifdef CONFIG_VIDEO_ADV_DEBUG
/* ioctls to allow direct access to the
* cx25840 registers for testing */
case VIDIOC_INT_G_REGISTER :
{
struct v4l2_register * reg = arg ;
if ( reg - > i2c_id ! = I2C_DRIVERID_CX25840 )
return - EINVAL ;
reg - > val = cx25840_read ( client , reg - > reg & 0x0fff ) ;
break ;
}
case VIDIOC_INT_S_REGISTER :
{
struct v4l2_register * reg = arg ;
if ( reg - > i2c_id ! = I2C_DRIVERID_CX25840 )
return - EINVAL ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
cx25840_write ( client , reg - > reg & 0x0fff , reg - > val & 0xff ) ;
break ;
}
# endif
case VIDIOC_INT_DECODE_VBI_LINE :
return cx25840_vbi ( client , cmd , arg ) ;
case VIDIOC_INT_AUDIO_CLOCK_FREQ :
2006-01-09 15:25:44 -02:00
return cx25840_audio ( client , cmd , arg ) ;
2005-11-13 16:07:56 -08:00
case VIDIOC_STREAMON :
2006-01-11 22:41:36 -02:00
v4l_dbg ( 1 , cx25840_debug , client , " enable output \n " ) ;
2006-04-22 10:22:46 -03:00
cx25840_write ( client , 0x115 , state - > is_cx25836 ? 0x0c : 0x8c ) ;
cx25840_write ( client , 0x116 , state - > is_cx25836 ? 0x04 : 0x07 ) ;
2005-11-13 16:07:56 -08:00
break ;
case VIDIOC_STREAMOFF :
2006-01-11 22:41:36 -02:00
v4l_dbg ( 1 , cx25840_debug , client , " disable output \n " ) ;
2005-11-13 16:07:56 -08:00
cx25840_write ( client , 0x115 , 0x00 ) ;
cx25840_write ( client , 0x116 , 0x00 ) ;
break ;
case VIDIOC_LOG_STATUS :
2006-04-22 10:22:46 -03:00
log_video_status ( client ) ;
if ( ! state - > is_cx25836 )
log_audio_status ( client ) ;
2005-11-13 16:07:56 -08:00
break ;
case VIDIOC_G_CTRL :
2006-01-09 15:25:44 -02:00
return get_v4lctrl ( client , ( struct v4l2_control * ) arg ) ;
2005-11-13 16:07:56 -08:00
case VIDIOC_S_CTRL :
2006-01-09 15:25:44 -02:00
return set_v4lctrl ( client , ( struct v4l2_control * ) arg ) ;
2005-11-13 16:07:56 -08:00
2006-01-09 15:32:41 -02:00
case VIDIOC_QUERYCTRL :
{
struct v4l2_queryctrl * qc = arg ;
int i ;
2006-04-22 10:22:46 -03:00
for ( i = 0 ; i < ARRAY_SIZE ( cx25836_qctrl ) ; i + + )
if ( qc - > id & & qc - > id = = cx25836_qctrl [ i ] . id ) {
memcpy ( qc , & cx25836_qctrl [ i ] , sizeof ( * qc ) ) ;
return 0 ;
}
if ( state - > is_cx25836 )
return - EINVAL ;
2006-01-09 15:32:41 -02:00
for ( i = 0 ; i < ARRAY_SIZE ( cx25840_qctrl ) ; i + + )
if ( qc - > id & & qc - > id = = cx25840_qctrl [ i ] . id ) {
memcpy ( qc , & cx25840_qctrl [ i ] , sizeof ( * qc ) ) ;
return 0 ;
}
return - EINVAL ;
}
2005-11-13 16:07:56 -08:00
case VIDIOC_G_STD :
* ( v4l2_std_id * ) arg = cx25840_get_v4lstd ( client ) ;
break ;
case VIDIOC_S_STD :
2006-01-09 15:25:44 -02:00
state - > radio = 0 ;
return set_v4lstd ( client , * ( v4l2_std_id * ) arg ) ;
case AUDC_SET_RADIO :
state - > radio = 1 ;
2005-11-13 16:07:56 -08:00
break ;
2006-03-25 10:26:09 -03:00
case VIDIOC_INT_G_VIDEO_ROUTING :
route - > input = state - > vid_input ;
route - > output = 0 ;
2005-11-13 16:07:56 -08:00
break ;
2006-03-25 10:26:09 -03:00
case VIDIOC_INT_S_VIDEO_ROUTING :
return set_input ( client , route - > input , state - > aud_input ) ;
2005-11-13 16:07:56 -08:00
2006-03-25 10:26:09 -03:00
case VIDIOC_INT_G_AUDIO_ROUTING :
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
return - EINVAL ;
2006-03-25 10:26:09 -03:00
route - > input = state - > aud_input ;
route - > output = 0 ;
break ;
2006-01-09 15:25:42 -02:00
2006-03-25 10:26:09 -03:00
case VIDIOC_INT_S_AUDIO_ROUTING :
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
return - EINVAL ;
2006-03-25 10:26:09 -03:00
return set_input ( client , state - > vid_input , route - > input ) ;
2006-01-09 15:25:42 -02:00
2005-11-13 16:07:56 -08:00
case VIDIOC_S_FREQUENCY :
2006-04-22 10:22:46 -03:00
if ( ! state - > is_cx25836 ) {
input_change ( client ) ;
}
2005-11-13 16:07:56 -08:00
break ;
case VIDIOC_G_TUNER :
{
2006-04-22 10:22:46 -03:00
u8 vpres = cx25840_read ( client , 0x40e ) & 0x20 ;
u8 mode ;
2005-11-13 16:07:56 -08:00
int val = 0 ;
2006-01-09 15:25:44 -02:00
if ( state - > radio )
break ;
2006-04-22 10:22:46 -03:00
vt - > signal = vpres ? 0xffff : 0x0 ;
if ( state - > is_cx25836 )
break ;
2005-11-13 16:07:56 -08:00
vt - > capability | =
V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP ;
2006-04-22 10:22:46 -03:00
mode = cx25840_read ( client , 0x804 ) ;
2005-11-13 16:07:56 -08:00
/* get rxsubchans and audmode */
if ( ( mode & 0xf ) = = 1 )
val | = V4L2_TUNER_SUB_STEREO ;
else
val | = V4L2_TUNER_SUB_MONO ;
if ( mode = = 2 | | mode = = 4 )
2006-01-23 17:11:09 -02:00
val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2 ;
2005-11-13 16:07:56 -08:00
if ( mode & 0x10 )
val | = V4L2_TUNER_SUB_SAP ;
vt - > rxsubchans = val ;
2006-01-23 17:11:09 -02:00
vt - > audmode = state - > audmode ;
2005-11-13 16:07:56 -08:00
break ;
}
case VIDIOC_S_TUNER :
2006-04-22 10:22:46 -03:00
if ( state - > radio | | state - > is_cx25836 )
2006-01-23 17:11:09 -02:00
break ;
2005-11-13 16:07:56 -08:00
switch ( vt - > audmode ) {
case V4L2_TUNER_MODE_MONO :
2006-01-23 17:11:09 -02:00
/* mono -> mono
stereo - > mono
bilingual - > lang1 */
2005-11-13 16:07:56 -08:00
cx25840_and_or ( client , 0x809 , ~ 0xf , 0x00 ) ;
break ;
2006-03-18 17:15:00 -03:00
case V4L2_TUNER_MODE_STEREO :
2006-01-23 17:11:09 -02:00
case V4L2_TUNER_MODE_LANG1 :
/* mono -> mono
stereo - > stereo
bilingual - > lang1 */
2005-11-13 16:07:56 -08:00
cx25840_and_or ( client , 0x809 , ~ 0xf , 0x04 ) ;
break ;
2006-03-18 17:15:00 -03:00
case V4L2_TUNER_MODE_LANG1_LANG2 :
2006-01-23 17:11:09 -02:00
/* mono -> mono
stereo - > stereo
bilingual - > lang1 / lang2 */
cx25840_and_or ( client , 0x809 , ~ 0xf , 0x07 ) ;
break ;
2005-11-13 16:07:56 -08:00
case V4L2_TUNER_MODE_LANG2 :
2006-01-23 17:11:09 -02:00
/* mono -> mono
2006-03-18 17:15:00 -03:00
stereo - > stereo
2006-01-23 17:11:09 -02:00
bilingual - > lang2 */
2005-11-13 16:07:56 -08:00
cx25840_and_or ( client , 0x809 , ~ 0xf , 0x01 ) ;
break ;
2006-01-23 17:11:09 -02:00
default :
return - EINVAL ;
2005-11-13 16:07:56 -08:00
}
2006-01-23 17:11:09 -02:00
state - > audmode = vt - > audmode ;
2005-11-13 16:07:56 -08:00
break ;
case VIDIOC_G_FMT :
2006-01-09 15:25:44 -02:00
return get_v4lfmt ( client , ( struct v4l2_format * ) arg ) ;
2005-11-13 16:07:56 -08:00
case VIDIOC_S_FMT :
2006-01-09 15:25:44 -02:00
return set_v4lfmt ( client , ( struct v4l2_format * ) arg ) ;
2005-11-13 16:07:56 -08:00
case VIDIOC_INT_RESET :
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
cx25836_initialize ( client ) ;
else
cx25840_initialize ( client , 0 ) ;
2005-11-13 16:07:56 -08:00
break ;
case VIDIOC_INT_G_CHIP_IDENT :
2006-04-22 10:22:46 -03:00
* ( enum v4l2_chip_ident * ) arg = state - > id ;
2005-11-13 16:07:56 -08:00
break ;
default :
return - EINVAL ;
}
2006-01-09 15:25:44 -02:00
return 0 ;
2005-11-13 16:07:56 -08:00
}
/* ----------------------------------------------------------------------- */
2005-12-01 00:51:35 -08:00
static struct i2c_driver i2c_driver_cx25840 ;
2005-11-13 16:07:56 -08:00
static int cx25840_detect_client ( struct i2c_adapter * adapter , int address ,
int kind )
{
struct i2c_client * client ;
struct cx25840_state * state ;
2006-04-22 10:22:46 -03:00
enum v4l2_chip_ident id ;
2005-11-13 16:07:56 -08:00
u16 device_id ;
/* Check if the adapter supports the needed features
* Not until kernel version 2.6 .11 did the bit - algo
* correctly report that it would do an I2C - level xfer */
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_I2C ) )
return 0 ;
2006-04-22 10:22:46 -03:00
state = kzalloc ( sizeof ( struct cx25840_state ) , GFP_KERNEL ) ;
if ( state = = 0 )
2005-11-13 16:07:56 -08:00
return - ENOMEM ;
2006-04-22 10:22:46 -03:00
client = & state - > c ;
2005-11-13 16:07:56 -08:00
client - > addr = address ;
client - > adapter = adapter ;
client - > driver = & i2c_driver_cx25840 ;
snprintf ( client - > name , sizeof ( client - > name ) - 1 , " cx25840 " ) ;
2006-01-11 22:41:36 -02:00
v4l_dbg ( 1 , cx25840_debug , client , " detecting cx25840 client on address 0x%x \n " , address < < 1 ) ;
2005-11-13 16:07:56 -08:00
device_id = cx25840_read ( client , 0x101 ) < < 8 ;
device_id | = cx25840_read ( client , 0x100 ) ;
/* The high byte of the device ID should be
2006-04-22 10:22:46 -03:00
* 0x83 for the cx2583x and 0x84 for the cx2584x */
if ( ( device_id & 0xff00 ) = = 0x8300 ) {
id = V4L2_IDENT_CX25836 + ( ( device_id > > 4 ) & 0xf ) - 6 ;
state - > is_cx25836 = 1 ;
}
else if ( ( device_id & 0xff00 ) = = 0x8400 ) {
id = V4L2_IDENT_CX25840 + ( ( device_id > > 4 ) & 0xf ) ;
state - > is_cx25836 = 0 ;
}
else {
2006-01-11 22:41:36 -02:00
v4l_dbg ( 1 , cx25840_debug , client , " cx25840 not found \n " ) ;
2006-04-22 10:22:46 -03:00
kfree ( state ) ;
2005-11-13 16:07:56 -08:00
return 0 ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " cx25%3x-2%x found @ 0x%x (%s) \n " ,
2005-11-13 16:07:56 -08:00
( device_id & 0xfff0 ) > > 4 ,
( device_id & 0x0f ) < 3 ? ( device_id & 0x0f ) + 1 : 3 ,
address < < 1 , adapter - > name ) ;
i2c_set_clientdata ( client , state ) ;
2006-01-09 15:25:42 -02:00
state - > vid_input = CX25840_COMPOSITE7 ;
state - > aud_input = CX25840_AUDIO8 ;
2006-01-09 15:25:41 -02:00
state - > audclk_freq = 48000 ;
2006-01-09 15:25:42 -02:00
state - > pvr150_workaround = 0 ;
2006-01-23 17:11:09 -02:00
state - > audmode = V4L2_TUNER_MODE_LANG1 ;
2006-04-22 10:22:46 -03:00
state - > id = id ;
2005-11-13 16:07:56 -08:00
2006-04-22 10:22:46 -03:00
if ( state - > is_cx25836 )
cx25836_initialize ( client ) ;
else
cx25840_initialize ( client , 1 ) ;
2005-11-13 16:07:56 -08:00
i2c_attach_client ( client ) ;
return 0 ;
}
static int cx25840_attach_adapter ( struct i2c_adapter * adapter )
{
if ( adapter - > class & I2C_CLASS_TV_ANALOG )
return i2c_probe ( adapter , & addr_data , & cx25840_detect_client ) ;
return 0 ;
}
static int cx25840_detach_client ( struct i2c_client * client )
{
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
int err ;
err = i2c_detach_client ( client ) ;
if ( err ) {
return err ;
}
kfree ( state ) ;
return 0 ;
}
/* ----------------------------------------------------------------------- */
2005-12-01 00:51:35 -08:00
static struct i2c_driver i2c_driver_cx25840 = {
2005-11-26 20:43:39 +01:00
. driver = {
. name = " cx25840 " ,
} ,
2005-11-13 16:07:56 -08:00
. id = I2C_DRIVERID_CX25840 ,
. attach_adapter = cx25840_attach_adapter ,
. detach_client = cx25840_detach_client ,
. command = cx25840_command ,
} ;
static int __init m__init ( void )
{
return i2c_add_driver ( & i2c_driver_cx25840 ) ;
}
static void __exit m__exit ( void )
{
i2c_del_driver ( & i2c_driver_cx25840 ) ;
}
module_init ( m__init ) ;
module_exit ( m__exit ) ;
/* ----------------------------------------------------------------------- */
2006-04-22 10:22:46 -03:00
static void log_video_status ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
static const char * const fmt_strs [ ] = {
" 0x0 " ,
" NTSC-M " , " NTSC-J " , " NTSC-4.43 " ,
" PAL-BDGHI " , " PAL-M " , " PAL-N " , " PAL-Nc " , " PAL-60 " ,
" 0x9 " , " 0xA " , " 0xB " ,
" SECAM " ,
" 0xD " , " 0xE " , " 0xF "
} ;
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
u8 vidfmt_sel = cx25840_read ( client , 0x400 ) & 0xf ;
u8 gen_stat1 = cx25840_read ( client , 0x40d ) ;
2006-04-22 10:22:46 -03:00
u8 gen_stat2 = cx25840_read ( client , 0x40e ) ;
int vid_input = state - > vid_input ;
v4l_info ( client , " Video signal: %spresent \n " ,
( gen_stat2 & 0x20 ) ? " " : " not " ) ;
v4l_info ( client , " Detected format: %s \n " ,
fmt_strs [ gen_stat1 & 0xf ] ) ;
v4l_info ( client , " Specified standard: %s \n " ,
vidfmt_sel ? fmt_strs [ vidfmt_sel ] : " automatic detection " ) ;
if ( vid_input > = CX25840_COMPOSITE1 & &
vid_input < = CX25840_COMPOSITE8 ) {
v4l_info ( client , " Specified video input: Composite %d \n " ,
vid_input - CX25840_COMPOSITE1 + 1 ) ;
} else {
v4l_info ( client , " Specified video input: S-Video (Luma In%d, Chroma In%d) \n " ,
( vid_input & 0xf0 ) > > 4 , ( vid_input & 0xf00 ) > > 8 ) ;
}
v4l_info ( client , " Specified audioclock freq: %d Hz \n " , state - > audclk_freq ) ;
}
/* ----------------------------------------------------------------------- */
static void log_audio_status ( struct i2c_client * client )
{
struct cx25840_state * state = i2c_get_clientdata ( client ) ;
2005-11-13 16:07:56 -08:00
u8 download_ctl = cx25840_read ( client , 0x803 ) ;
u8 mod_det_stat0 = cx25840_read ( client , 0x804 ) ;
u8 mod_det_stat1 = cx25840_read ( client , 0x805 ) ;
u8 audio_config = cx25840_read ( client , 0x808 ) ;
u8 pref_mode = cx25840_read ( client , 0x809 ) ;
u8 afc0 = cx25840_read ( client , 0x80b ) ;
u8 mute_ctl = cx25840_read ( client , 0x8d3 ) ;
2006-01-09 15:25:42 -02:00
int aud_input = state - > aud_input ;
2005-11-13 16:07:56 -08:00
char * p ;
switch ( mod_det_stat0 ) {
case 0x00 : p = " mono " ; break ;
case 0x01 : p = " stereo " ; break ;
case 0x02 : p = " dual " ; break ;
case 0x04 : p = " tri " ; break ;
case 0x10 : p = " mono with SAP " ; break ;
case 0x11 : p = " stereo with SAP " ; break ;
case 0x12 : p = " dual with SAP " ; break ;
case 0x14 : p = " tri with SAP " ; break ;
case 0xfe : p = " forced mode " ; break ;
default : p = " not defined " ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Detected audio mode: %s \n " , p ) ;
2005-11-13 16:07:56 -08:00
switch ( mod_det_stat1 ) {
case 0x00 : p = " not defined " ; break ;
case 0x01 : p = " EIAJ " ; break ;
case 0x02 : p = " A2-M " ; break ;
case 0x03 : p = " A2-BG " ; break ;
case 0x04 : p = " A2-DK1 " ; break ;
case 0x05 : p = " A2-DK2 " ; break ;
case 0x06 : p = " A2-DK3 " ; break ;
case 0x07 : p = " A1 (6.0 MHz FM Mono) " ; break ;
case 0x08 : p = " AM-L " ; break ;
case 0x09 : p = " NICAM-BG " ; break ;
case 0x0a : p = " NICAM-DK " ; break ;
case 0x0b : p = " NICAM-I " ; break ;
case 0x0c : p = " NICAM-L " ; break ;
case 0x0d : p = " BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono) " ; break ;
case 0x0e : p = " IF FM Radio " ; break ;
case 0x0f : p = " BTSC " ; break ;
case 0x10 : p = " high-deviation FM " ; break ;
case 0x11 : p = " very high-deviation FM " ; break ;
case 0xfd : p = " unknown audio standard " ; break ;
case 0xfe : p = " forced audio standard " ; break ;
case 0xff : p = " no detected audio standard " ; break ;
default : p = " not defined " ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Detected audio standard: %s \n " , p ) ;
v4l_info ( client , " Audio muted: %s \n " ,
2005-11-13 16:07:56 -08:00
( mute_ctl & 0x2 ) ? " yes " : " no " ) ;
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Audio microcontroller: %s \n " ,
2005-11-13 16:07:56 -08:00
( download_ctl & 0x10 ) ? " running " : " stopped " ) ;
switch ( audio_config > > 4 ) {
case 0x00 : p = " undefined " ; break ;
case 0x01 : p = " BTSC " ; break ;
case 0x02 : p = " EIAJ " ; break ;
case 0x03 : p = " A2-M " ; break ;
case 0x04 : p = " A2-BG " ; break ;
case 0x05 : p = " A2-DK1 " ; break ;
case 0x06 : p = " A2-DK2 " ; break ;
case 0x07 : p = " A2-DK3 " ; break ;
case 0x08 : p = " A1 (6.0 MHz FM Mono) " ; break ;
case 0x09 : p = " AM-L " ; break ;
case 0x0a : p = " NICAM-BG " ; break ;
case 0x0b : p = " NICAM-DK " ; break ;
case 0x0c : p = " NICAM-I " ; break ;
case 0x0d : p = " NICAM-L " ; break ;
case 0x0e : p = " FM radio " ; break ;
case 0x0f : p = " automatic detection " ; break ;
default : p = " undefined " ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Configured audio standard: %s \n " , p ) ;
2005-11-13 16:07:56 -08:00
if ( ( audio_config > > 4 ) < 0xF ) {
switch ( audio_config & 0xF ) {
case 0x00 : p = " MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2) " ; break ;
case 0x01 : p = " MONO2 (LANGUAGE B) " ; break ;
case 0x02 : p = " MONO3 (STEREO forced MONO) " ; break ;
case 0x03 : p = " MONO4 (NICAM ANALOG-Language C/Analog Fallback) " ; break ;
case 0x04 : p = " STEREO " ; break ;
case 0x05 : p = " DUAL1 (AB) " ; break ;
case 0x06 : p = " DUAL2 (AC) (FM) " ; break ;
case 0x07 : p = " DUAL3 (BC) (FM) " ; break ;
case 0x08 : p = " DUAL4 (AC) (AM) " ; break ;
case 0x09 : p = " DUAL5 (BC) (AM) " ; break ;
case 0x0a : p = " SAP " ; break ;
default : p = " undefined " ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Configured audio mode: %s \n " , p ) ;
2005-11-13 16:07:56 -08:00
} else {
switch ( audio_config & 0xF ) {
case 0x00 : p = " BG " ; break ;
case 0x01 : p = " DK1 " ; break ;
case 0x02 : p = " DK2 " ; break ;
case 0x03 : p = " DK3 " ; break ;
case 0x04 : p = " I " ; break ;
case 0x05 : p = " L " ; break ;
case 0x06 : p = " BTSC " ; break ;
case 0x07 : p = " EIAJ " ; break ;
case 0x08 : p = " A2-M " ; break ;
case 0x09 : p = " FM Radio " ; break ;
case 0x0f : p = " automatic standard and mode detection " ; break ;
default : p = " undefined " ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Configured audio system: %s \n " , p ) ;
2005-11-13 16:07:56 -08:00
}
2006-01-09 15:25:42 -02:00
if ( aud_input ) {
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Specified audio input: Tuner (In%d) \n " , aud_input ) ;
2006-01-09 15:25:42 -02:00
} else {
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Specified audio input: External \n " ) ;
2005-11-13 16:07:56 -08:00
}
switch ( pref_mode & 0xf ) {
case 0 : p = " mono/language A " ; break ;
case 1 : p = " language B " ; break ;
case 2 : p = " language C " ; break ;
case 3 : p = " analog fallback " ; break ;
case 4 : p = " stereo " ; break ;
case 5 : p = " language AC " ; break ;
case 6 : p = " language BC " ; break ;
case 7 : p = " language AB " ; break ;
default : p = " undefined " ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Preferred audio mode: %s \n " , p ) ;
2005-11-13 16:07:56 -08:00
if ( ( audio_config & 0xf ) = = 0xf ) {
switch ( ( afc0 > > 3 ) & 0x3 ) {
case 0 : p = " system DK " ; break ;
case 1 : p = " system L " ; break ;
case 2 : p = " autodetect " ; break ;
default : p = " undefined " ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Selected 65 MHz format: %s \n " , p ) ;
2005-11-13 16:07:56 -08:00
switch ( afc0 & 0x7 ) {
case 0 : p = " chroma " ; break ;
case 1 : p = " BTSC " ; break ;
case 2 : p = " EIAJ " ; break ;
case 3 : p = " A2-M " ; break ;
case 4 : p = " autodetect " ; break ;
default : p = " undefined " ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " Selected 45 MHz format: %s \n " , p ) ;
2005-11-13 16:07:56 -08:00
}
}