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 > .
*
2006-05-24 10:16:45 -03:00
* NTSC sliced VBI support by Christopher Neufeld < television @ cneufeld . ca >
* with additional fixes by Hans Verkuil < hverkuil @ xs4all . nl > .
*
2008-09-03 17:12:12 -03:00
* CX23885 support by Steven Toth < stoth @ linuxtv . org > .
2008-01-10 01:22:39 -03:00
*
2005-11-13 16:07:56 -08:00
* 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>
2007-09-01 02:02:51 -03:00
# include <linux/delay.h>
2005-11-13 16:07:56 -08:00
# include <media/v4l2-common.h>
2007-04-27 12:31:08 -03:00
# include <media/v4l2-chip-ident.h>
2009-03-29 08:55:46 -03:00
# include <media/v4l2-i2c-drv.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 " ) ;
2008-07-21 16:33:48 -03: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
/* ----------------------------------------------------------------------- */
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 ;
2007-12-02 07:03:45 -03:00
buffer [ 2 ] = value & 0xff ;
buffer [ 3 ] = ( value > > 8 ) & 0xff ;
buffer [ 4 ] = ( value > > 16 ) & 0xff ;
buffer [ 5 ] = value > > 24 ;
2005-11-13 16:07:56 -08:00
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 ;
2006-08-08 09:10:12 -03:00
return ( buffer [ 3 ] < < 24 ) | ( buffer [ 2 ] < < 16 ) |
( buffer [ 1 ] < < 8 ) | buffer [ 0 ] ;
2005-11-13 16:07:56 -08:00
}
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 ) ;
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 ) ;
2007-08-28 17:56:47 -03:00
udelay ( 10 ) ;
2005-11-13 16:07:56 -08:00
cx25840_write ( client , 0x159 , 0xe1 ) ;
2007-08-28 17:56:47 -03:00
udelay ( 10 ) ;
2005-11-13 16:07:56 -08:00
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 ) ;
2007-08-28 17:56:47 -03:00
udelay ( 10 ) ;
2005-11-13 16:07:56 -08:00
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. */
2007-08-28 17:56:47 -03:00
udelay ( 10 ) ;
2006-04-22 10:22:46 -03:00
/* 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 ) ;
}
2007-08-26 10:53:16 -03:00
static void cx25840_work_handler ( struct work_struct * work )
{
struct cx25840_state * state = container_of ( work , struct cx25840_state , fw_work ) ;
cx25840_loadfw ( state - > c ) ;
wake_up ( & state - > fw_wait ) ;
}
2007-08-04 05:00:07 -03:00
static void cx25840_initialize ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
2007-08-26 10:53:16 -03:00
DEFINE_WAIT ( wait ) ;
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2007-08-26 10:53:16 -03:00
struct workqueue_struct * q ;
2005-11-13 16:07:56 -08:00
/* 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. */
2007-08-26 10:53:16 -03:00
/* Do the firmware load in a work handler to prevent.
Otherwise the kernel is blocked waiting for the
bit - banging i2c interface to finish uploading the
firmware . */
INIT_WORK ( & state - > fw_work , cx25840_work_handler ) ;
init_waitqueue_head ( & state - > fw_wait ) ;
q = create_singlethread_workqueue ( " cx25840_fw " ) ;
prepare_to_wait ( & state - > fw_wait , & wait , TASK_UNINTERRUPTIBLE ) ;
queue_work ( q , & state - > fw_work ) ;
schedule ( ) ;
finish_wait ( & state - > fw_wait , & wait ) ;
destroy_workqueue ( q ) ;
2005-11-13 16:07:56 -08:00
/* 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 ) ;
2008-07-17 11:08:40 -03:00
cx25840_std_setup ( client ) ;
2005-11-13 16:07:56 -08:00
/* 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 ) ;
}
2008-01-10 01:22:39 -03:00
static void cx23885_initialize ( struct i2c_client * client )
{
DEFINE_WAIT ( wait ) ;
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2008-01-10 01:22:39 -03:00
struct workqueue_struct * q ;
2009-09-27 00:00:48 -03:00
/*
* Come out of digital power down
* The CX23888 , at least , needs this , otherwise registers aside from
* 0x0 - 0x2 can ' t be read or written .
*/
cx25840_write ( client , 0x000 , 0 ) ;
2008-01-10 01:22:39 -03:00
/* Internal Reset */
cx25840_and_or ( client , 0x102 , ~ 0x01 , 0x01 ) ;
cx25840_and_or ( client , 0x102 , ~ 0x01 , 0x00 ) ;
/* Stop microcontroller */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x00 ) ;
/* DIF in reset? */
cx25840_write ( client , 0x398 , 0 ) ;
2009-09-27 00:00:48 -03:00
/*
* Trust the default xtal , no division
* ' 885 : 28.636363 . . . MHz
* ' 887 : 25.000000 MHz
* ' 888 : 50.000000 MHz
*/
2008-01-10 01:22:39 -03:00
cx25840_write ( client , 0x2 , 0x76 ) ;
2009-09-27 00:00:48 -03:00
/* Power up all the PLL's and DLL */
2008-01-10 01:22:39 -03:00
cx25840_write ( client , 0x1 , 0x40 ) ;
2009-09-27 00:00:48 -03:00
/* Sys PLL */
switch ( state - > id ) {
case V4L2_IDENT_CX23888_AV :
/*
* 50.0 MHz * ( 0xb + 0xe8ba26 / 0x2000000 ) / 4 = 5 * 28.636363 MHz
* 572.73 MHz before post divide
*/
cx25840_write4 ( client , 0x11c , 0x00e8ba26 ) ;
cx25840_write4 ( client , 0x118 , 0x0000040b ) ;
break ;
case V4L2_IDENT_CX23887_AV :
/*
* 25.0 MHz * ( 0x16 + 0x1d1744c / 0x2000000 ) / 4 = 5 * 28.636363 MHz
* 572.73 MHz before post divide
*/
cx25840_write4 ( client , 0x11c , 0x01d1744c ) ;
cx25840_write4 ( client , 0x118 , 0x00000416 ) ;
break ;
case V4L2_IDENT_CX23885_AV :
default :
/*
* 28.636363 MHz * ( 0x14 + 0x0 / 0x2000000 ) / 4 = 5 * 28.636363 MHz
* 572.73 MHz before post divide
*/
cx25840_write4 ( client , 0x11c , 0x00000000 ) ;
cx25840_write4 ( client , 0x118 , 0x00000414 ) ;
break ;
}
2008-01-10 01:22:39 -03:00
/* Disable DIF bypass */
cx25840_write4 ( client , 0x33c , 0x00000001 ) ;
/* DIF Src phase inc */
cx25840_write4 ( client , 0x340 , 0x0df7df83 ) ;
2009-09-27 00:00:48 -03:00
/*
* Vid PLL
* Setup for a BT .656 pixel clock of 13.5 Mpixels / second
*
* 28.636363 MHz * ( 0xf + 0x02be2c9 / 0x2000000 ) / 4 = 8 * 13.5 MHz
* 432.0 MHz before post divide
*/
cx25840_write4 ( client , 0x10c , 0x002be2c9 ) ;
cx25840_write4 ( client , 0x108 , 0x0000040f ) ;
2008-01-10 01:22:39 -03:00
/* Luma */
cx25840_write4 ( client , 0x414 , 0x00107d12 ) ;
/* Chroma */
cx25840_write4 ( client , 0x420 , 0x3d008282 ) ;
2009-09-27 00:00:48 -03:00
/*
* Aux PLL
* Initial setup for audio sample clock :
* 48 ksps , 16 bits / sample , x160 multiplier = 122.88 MHz
* Intial I2S output / master clock ( ? ) :
* 48 ksps , 16 bits / sample , x16 multiplier = 12.288 MHz
*/
switch ( state - > id ) {
case V4L2_IDENT_CX23888_AV :
/*
* 50.0 MHz * ( 0x7 + 0x0bedfa4 / 0x2000000 ) / 3 = 122.88 MHz
* 368.64 MHz before post divide
* 122.88 MHz / 0xa = 12.288 MHz
*/
cx25840_write4 ( client , 0x114 , 0x00bedfa4 ) ;
cx25840_write4 ( client , 0x110 , 0x000a0307 ) ;
break ;
case V4L2_IDENT_CX23887_AV :
/*
* 25.0 MHz * ( 0xe + 0x17dbf48 / 0x2000000 ) / 3 = 122.88 MHz
* 368.64 MHz before post divide
* 122.88 MHz / 0xa = 12.288 MHz
*/
cx25840_write4 ( client , 0x114 , 0x017dbf48 ) ;
cx25840_write4 ( client , 0x110 , 0x000a030e ) ;
break ;
case V4L2_IDENT_CX23885_AV :
default :
/*
* 28.636363 MHz * ( 0xc + 0x1bf0c9e / 0x2000000 ) / 3 = 122.88 MHz
* 368.64 MHz before post divide
* 122.88 MHz / 0xa = 12.288 MHz
*/
cx25840_write4 ( client , 0x114 , 0x01bf0c9e ) ;
cx25840_write4 ( client , 0x110 , 0x000a030c ) ;
break ;
} ;
2008-01-10 01:22:39 -03:00
/* ADC2 input select */
cx25840_write ( client , 0x102 , 0x10 ) ;
/* VIN1 & VIN5 */
cx25840_write ( client , 0x103 , 0x11 ) ;
/* Enable format auto detect */
cx25840_write ( client , 0x400 , 0 ) ;
/* Fast subchroma lock */
/* White crush, Chroma AGC & Chroma Killer enabled */
cx25840_write ( client , 0x401 , 0xe8 ) ;
/* Select AFE clock pad output source */
cx25840_write ( client , 0x144 , 0x05 ) ;
2009-07-23 12:18:54 -03:00
/* Drive GPIO2 direction and values for HVR1700
* where an onboard mux selects the output of demodulator
* vs the 417. Failure to set this results in no DTV .
* It ' s safe to set this across all Hauppauge boards
* currently , regardless of the board type .
*/
cx25840_write ( client , 0x160 , 0x1d ) ;
cx25840_write ( client , 0x164 , 0x00 ) ;
2008-01-10 01:22:39 -03:00
/* Do the firmware load in a work handler to prevent.
Otherwise the kernel is blocked waiting for the
bit - banging i2c interface to finish uploading the
firmware . */
INIT_WORK ( & state - > fw_work , cx25840_work_handler ) ;
init_waitqueue_head ( & state - > fw_wait ) ;
q = create_singlethread_workqueue ( " cx25840_fw " ) ;
prepare_to_wait ( & state - > fw_wait , & wait , TASK_UNINTERRUPTIBLE ) ;
queue_work ( q , & state - > fw_work ) ;
schedule ( ) ;
finish_wait ( & state - > fw_wait , & wait ) ;
destroy_workqueue ( q ) ;
2008-07-17 11:08:40 -03:00
cx25840_std_setup ( client ) ;
2008-01-10 01:22:39 -03:00
/* (re)set input */
set_input ( client , state - > vid_input , state - > aud_input ) ;
/* start microcontroller */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x10 ) ;
}
2005-11-13 16:07:56 -08:00
/* ----------------------------------------------------------------------- */
2009-03-03 06:07:42 -03:00
static void cx231xx_initialize ( struct i2c_client * client )
{
DEFINE_WAIT ( wait ) ;
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
struct workqueue_struct * q ;
/* Internal Reset */
cx25840_and_or ( client , 0x102 , ~ 0x01 , 0x01 ) ;
cx25840_and_or ( client , 0x102 , ~ 0x01 , 0x00 ) ;
/* Stop microcontroller */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x00 ) ;
/* DIF in reset? */
cx25840_write ( client , 0x398 , 0 ) ;
/* Trust the default xtal, no division */
/* This changes for the cx23888 products */
cx25840_write ( client , 0x2 , 0x76 ) ;
/* Bring down the regulator for AUX clk */
cx25840_write ( client , 0x1 , 0x40 ) ;
/* Disable DIF bypass */
cx25840_write4 ( client , 0x33c , 0x00000001 ) ;
/* DIF Src phase inc */
cx25840_write4 ( client , 0x340 , 0x0df7df83 ) ;
/* Luma */
cx25840_write4 ( client , 0x414 , 0x00107d12 ) ;
/* Chroma */
cx25840_write4 ( client , 0x420 , 0x3d008282 ) ;
/* ADC2 input select */
cx25840_write ( client , 0x102 , 0x10 ) ;
/* VIN1 & VIN5 */
cx25840_write ( client , 0x103 , 0x11 ) ;
/* Enable format auto detect */
cx25840_write ( client , 0x400 , 0 ) ;
/* Fast subchroma lock */
/* White crush, Chroma AGC & Chroma Killer enabled */
cx25840_write ( client , 0x401 , 0xe8 ) ;
/* Do the firmware load in a work handler to prevent.
Otherwise the kernel is blocked waiting for the
bit - banging i2c interface to finish uploading the
firmware . */
INIT_WORK ( & state - > fw_work , cx25840_work_handler ) ;
init_waitqueue_head ( & state - > fw_wait ) ;
q = create_singlethread_workqueue ( " cx25840_fw " ) ;
prepare_to_wait ( & state - > fw_wait , & wait , TASK_UNINTERRUPTIBLE ) ;
queue_work ( q , & state - > fw_work ) ;
schedule ( ) ;
finish_wait ( & state - > fw_wait , & wait ) ;
destroy_workqueue ( q ) ;
cx25840_std_setup ( client ) ;
/* (re)set input */
set_input ( client , state - > vid_input , state - > aud_input ) ;
/* start microcontroller */
cx25840_and_or ( client , 0x803 , ~ 0x10 , 0x10 ) ;
}
/* ----------------------------------------------------------------------- */
2008-07-17 11:08:40 -03:00
void cx25840_std_setup ( struct i2c_client * client )
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2008-07-17 11:08:40 -03:00
v4l2_std_id std = state - > std ;
int hblank , hactive , burst , vblank , vactive , sc ;
int vblank656 , src_decimation ;
int luma_lpf , uv_lpf , comb ;
u32 pll_int , pll_frac , pll_post ;
/* datasheet startup, step 8d */
if ( std & ~ V4L2_STD_NTSC )
cx25840_write ( client , 0x49f , 0x11 ) ;
else
cx25840_write ( client , 0x49f , 0x14 ) ;
if ( std & V4L2_STD_625_50 ) {
hblank = 132 ;
hactive = 720 ;
burst = 93 ;
vblank = 36 ;
vactive = 580 ;
vblank656 = 40 ;
src_decimation = 0x21f ;
luma_lpf = 2 ;
if ( std & V4L2_STD_SECAM ) {
uv_lpf = 0 ;
comb = 0 ;
sc = 0x0a425f ;
} else if ( std = = V4L2_STD_PAL_Nc ) {
uv_lpf = 1 ;
comb = 0x20 ;
sc = 556453 ;
} else {
uv_lpf = 1 ;
comb = 0x20 ;
sc = 688739 ;
}
} else {
hactive = 720 ;
hblank = 122 ;
vactive = 487 ;
luma_lpf = 1 ;
uv_lpf = 1 ;
src_decimation = 0x21f ;
if ( std = = V4L2_STD_PAL_60 ) {
vblank = 26 ;
vblank656 = 26 ;
burst = 0x5b ;
luma_lpf = 2 ;
comb = 0x20 ;
sc = 688739 ;
} else if ( std = = V4L2_STD_PAL_M ) {
vblank = 20 ;
vblank656 = 24 ;
burst = 0x61 ;
comb = 0x20 ;
sc = 555452 ;
} else {
vblank = 26 ;
vblank656 = 26 ;
burst = 0x5b ;
comb = 0x66 ;
sc = 556063 ;
}
}
/* DEBUG: Displays configured PLL frequency */
2009-09-26 23:47:21 -03:00
if ( ! is_cx231xx ( state ) ) {
2009-03-03 14:36:55 -03:00
pll_int = cx25840_read ( client , 0x108 ) ;
pll_frac = cx25840_read4 ( client , 0x10c ) & 0x1ffffff ;
pll_post = cx25840_read ( client , 0x109 ) ;
2008-07-17 11:08:40 -03:00
v4l_dbg ( 1 , cx25840_debug , client ,
2009-03-03 14:36:55 -03:00
" PLL regs = int: %u, frac: %u, post: %u \n " ,
pll_int , pll_frac , pll_post ) ;
if ( pll_post ) {
int fin , fsc ;
int pll = ( 28636363L * ( ( ( ( u64 ) pll_int ) < < 25L ) + pll_frac ) ) > > 25L ;
pll / = pll_post ;
v4l_dbg ( 1 , cx25840_debug , client , " PLL = %d.%06d MHz \n " ,
pll / 1000000 , pll % 1000000 ) ;
v4l_dbg ( 1 , cx25840_debug , client , " PLL/8 = %d.%06d MHz \n " ,
pll / 8000000 , ( pll / 8 ) % 1000000 ) ;
fin = ( ( u64 ) src_decimation * pll ) > > 12 ;
v4l_dbg ( 1 , cx25840_debug , client ,
" ADC Sampling freq = %d.%06d MHz \n " ,
fin / 1000000 , fin % 1000000 ) ;
fsc = ( ( ( u64 ) sc ) * pll ) > > 24L ;
v4l_dbg ( 1 , cx25840_debug , client ,
" Chroma sub-carrier freq = %d.%06d MHz \n " ,
fsc / 1000000 , fsc % 1000000 ) ;
v4l_dbg ( 1 , cx25840_debug , client , " hblank %i, hactive %i, "
" vblank %i, vactive %i, vblank656 %i, src_dec %i, "
" burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, "
" sc 0x%06x \n " ,
hblank , hactive , vblank , vactive , vblank656 ,
src_decimation , burst , luma_lpf , uv_lpf , comb , sc ) ;
}
2008-07-17 11:08:40 -03:00
}
/* Sets horizontal blanking delay and active lines */
cx25840_write ( client , 0x470 , hblank ) ;
cx25840_write ( client , 0x471 ,
0xff & ( ( ( hblank > > 8 ) & 0x3 ) | ( hactive < < 4 ) ) ) ;
cx25840_write ( client , 0x472 , hactive > > 4 ) ;
/* Sets burst gate delay */
cx25840_write ( client , 0x473 , burst ) ;
/* Sets vertical blanking delay and active duration */
cx25840_write ( client , 0x474 , vblank ) ;
cx25840_write ( client , 0x475 ,
0xff & ( ( ( vblank > > 8 ) & 0x3 ) | ( vactive < < 4 ) ) ) ;
cx25840_write ( client , 0x476 , vactive > > 4 ) ;
cx25840_write ( client , 0x477 , vblank656 ) ;
/* Sets src decimation rate */
cx25840_write ( client , 0x478 , 0xff & src_decimation ) ;
cx25840_write ( client , 0x479 , 0xff & ( src_decimation > > 8 ) ) ;
/* Sets Luma and UV Low pass filters */
cx25840_write ( client , 0x47a , luma_lpf < < 6 | ( ( uv_lpf < < 4 ) & 0x30 ) ) ;
/* Enables comb filters */
cx25840_write ( client , 0x47b , comb ) ;
/* Sets SC Step*/
cx25840_write ( client , 0x47c , sc ) ;
cx25840_write ( client , 0x47d , 0xff & sc > > 8 ) ;
cx25840_write ( client , 0x47e , 0xff & sc > > 16 ) ;
/* Sets VBI parameters */
if ( std & V4L2_STD_625_50 ) {
cx25840_write ( client , 0x47f , 0x01 ) ;
state - > vbi_line_offset = 5 ;
} else {
cx25840_write ( client , 0x47f , 0x00 ) ;
state - > vbi_line_offset = 8 ;
}
}
/* ----------------------------------------------------------------------- */
2005-11-13 16:07:56 -08:00
static void input_change ( struct i2c_client * client )
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2008-04-22 14:45:51 -03:00
v4l2_std_id std = state - > std ;
2005-11-13 16:07:56 -08:00
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 ) ;
2007-08-07 07:16:07 -03:00
cx25840_and_or ( client , 0x810 , ~ 0x01 , 1 ) ;
2006-03-16 20:23:47 -03:00
2007-08-05 14:24:17 -03:00
if ( state - > radio ) {
cx25840_write ( client , 0x808 , 0xf9 ) ;
cx25840_write ( client , 0x80b , 0x00 ) ;
}
else if ( std & V4L2_STD_525_60 ) {
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 ) ;
2006-06-04 12:15:55 -03:00
} else if ( std & V4L2_STD_PAL ) {
2009-09-25 18:16:21 -03:00
/* Autodetect audio standard and audio system */
2006-06-04 12:15:55 -03:00
cx25840_write ( client , 0x808 , 0xff ) ;
2009-09-25 18:16:21 -03:00
/* Since system PAL-L is pretty much non-existant and
not used by any public broadcast network , force
6.5 MHz carrier to be interpreted as System DK ,
this avoids DK audio detection instability */
cx25840_write ( client , 0x80b , 0x00 ) ;
2006-06-04 12:15:55 -03:00
} else if ( std & V4L2_STD_SECAM ) {
2009-09-25 18:16:21 -03:00
/* Autodetect audio standard and audio system */
2006-06-04 12:15:55 -03:00
cx25840_write ( client , 0x808 , 0xff ) ;
2009-09-25 18:16:21 -03:00
/* If only one of SECAM-DK / SECAM-L is required, then force
6.5 MHz carrier , else autodetect it */
if ( ( std & V4L2_STD_SECAM_DK ) & &
! ( std & ( V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC ) ) ) {
/* 6.5 MHz carrier to be interpreted as System DK */
cx25840_write ( client , 0x80b , 0x00 ) ;
} else if ( ! ( std & V4L2_STD_SECAM_DK ) & &
( std & ( V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC ) ) ) {
/* 6.5 MHz carrier to be interpreted as System L */
cx25840_write ( client , 0x80b , 0x08 ) ;
} else {
/* 6.5 MHz carrier to be autodetected */
cx25840_write ( client , 0x80b , 0x10 ) ;
}
2005-11-13 16:07:56 -08:00
}
2007-08-07 07:16:07 -03:00
cx25840_and_or ( client , 0x810 , ~ 0x01 , 0 ) ;
2005-11-13 16:07:56 -08:00
}
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
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2006-01-09 15:25:42 -02:00
u8 is_composite = ( vid_input > = CX25840_COMPOSITE1 & &
vid_input < = CX25840_COMPOSITE8 ) ;
2009-10-20 12:13:39 -03:00
u8 is_component = ( vid_input & CX25840_COMPONENT_ON ) = =
CX25840_COMPONENT_ON ;
int luma = vid_input & 0xf0 ;
int chroma = vid_input & 0xf00 ;
2006-01-09 15:25:42 -02:00
u8 reg ;
2005-11-13 16:07:56 -08:00
2008-01-10 01:22:39 -03:00
v4l_dbg ( 1 , cx25840_debug , client ,
" decoder set video input %d, audio input %d \n " ,
vid_input , aud_input ) ;
2005-11-13 16:07:56 -08:00
2008-01-10 01:22:39 -03:00
if ( vid_input > = CX25840_VIN1_CH1 ) {
v4l_dbg ( 1 , cx25840_debug , client , " vid_input 0x%x \n " ,
vid_input ) ;
reg = vid_input & 0xff ;
2010-01-22 04:54:46 -03:00
is_composite = ! is_component & &
( ( vid_input & CX25840_SVIDEO_ON ) ! = CX25840_SVIDEO_ON ) ;
2008-01-10 01:22:39 -03:00
v4l_dbg ( 1 , cx25840_debug , client , " mux cfg 0x%x comp=%d \n " ,
reg , is_composite ) ;
2009-10-20 12:13:39 -03:00
} else if ( is_composite ) {
2006-01-09 15:25:42 -02:00
reg = 0xf0 + ( vid_input - CX25840_COMPOSITE1 ) ;
} else {
if ( ( vid_input & ~ 0xff0 ) | |
2008-06-07 11:18:17 -03:00
luma < CX25840_SVIDEO_LUMA1 | | luma > CX25840_SVIDEO_LUMA8 | |
2006-01-09 15:25:42 -02:00
chroma < CX25840_SVIDEO_CHROMA4 | | chroma > CX25840_SVIDEO_CHROMA8 ) {
2008-01-10 01:22:39 -03: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
2008-01-10 01:22:39 -03:00
/* The caller has previously prepared the correct routing
* configuration in reg ( for the cx23885 ) so we have no
* need to attempt to flip bits for earlier av decoders .
*/
2009-09-26 23:47:21 -03:00
if ( ! is_cx2388x ( state ) & & ! is_cx231xx ( state ) ) {
2008-01-10 01:22:39 -03:00
switch ( aud_input ) {
case CX25840_AUDIO_SERIAL :
/* do nothing, use serial audio input */
break ;
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
2008-01-10 01:22:39 -03:00
default :
v4l_err ( client , " 0x%04x is not a valid audio input! \n " ,
aud_input ) ;
return - EINVAL ;
}
2005-11-13 16:07:56 -08:00
}
2006-01-09 15:25:42 -02:00
cx25840_write ( client , 0x103 , reg ) ;
2008-01-10 01:22:39 -03:00
2009-10-20 12:13:39 -03:00
/* Set INPUT_MODE to Composite, S-Video or Component */
if ( is_component )
cx25840_and_or ( client , 0x401 , ~ 0x6 , 0x6 ) ;
else
cx25840_and_or ( client , 0x401 , ~ 0x6 , is_composite ? 0 : 0x02 ) ;
2008-01-10 01:22:39 -03:00
2009-09-26 23:47:21 -03:00
if ( ! is_cx2388x ( state ) & & ! is_cx231xx ( state ) ) {
2008-01-10 01:22:39 -03:00
/* 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&CH3 */
if ( ( reg & 0xc0 ) ! = 0xc0 & & ( reg & 0x30 ) ! = 0x30 )
cx25840_and_or ( client , 0x102 , ~ 0x4 , 4 ) ;
else
cx25840_and_or ( client , 0x102 , ~ 0x4 , 0 ) ;
} else {
2009-10-20 12:13:39 -03:00
/* Set DUAL_MODE_ADC2 to 1 if component*/
cx25840_and_or ( client , 0x102 , ~ 0x4 , is_component ? 0x4 : 0x0 ) ;
if ( is_composite ) {
2008-01-10 01:22:39 -03:00
/* ADC2 input select channel 2 */
cx25840_and_or ( client , 0x102 , ~ 0x2 , 0 ) ;
2009-10-20 12:13:39 -03:00
} else if ( ! is_component ) {
/* S-Video */
if ( chroma > = CX25840_SVIDEO_CHROMA7 ) {
/* ADC2 input select channel 3 */
cx25840_and_or ( client , 0x102 , ~ 0x2 , 2 ) ;
} else {
/* ADC2 input select channel 2 */
cx25840_and_or ( client , 0x102 , ~ 0x2 , 0 ) ;
}
}
2008-01-10 01:22:39 -03:00
}
2006-01-09 15:25:42 -02:00
state - > vid_input = vid_input ;
state - > aud_input = aud_input ;
2009-09-26 23:47:21 -03:00
if ( ! is_cx2583x ( state ) ) {
2006-04-22 10:22:46 -03:00
cx25840_audio_set_path ( client ) ;
input_change ( client ) ;
}
2008-01-10 01:22:39 -03:00
2009-09-26 23:47:21 -03:00
if ( is_cx2388x ( state ) ) {
2008-01-10 01:22:39 -03:00
/* Audio channel 1 src : Parallel 1 */
cx25840_write ( client , 0x124 , 0x03 ) ;
/* Select AFE clock pad output source */
cx25840_write ( client , 0x144 , 0x05 ) ;
/* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
cx25840_write ( client , 0x914 , 0xa0 ) ;
2009-03-03 06:07:42 -03:00
/* I2S_OUT_CTL:
* I2S_IN_SONY_MODE , LEFT SAMPLE on WS = 1
* I2S_OUT_MASTER_MODE = Master
*/
cx25840_write ( client , 0x918 , 0xa0 ) ;
cx25840_write ( client , 0x919 , 0x01 ) ;
2009-09-26 23:47:21 -03:00
} else if ( is_cx231xx ( state ) ) {
2009-03-03 06:07:42 -03:00
/* Audio channel 1 src : Parallel 1 */
cx25840_write ( client , 0x124 , 0x03 ) ;
/* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
cx25840_write ( client , 0x914 , 0xa0 ) ;
2008-01-10 01:22:39 -03:00
/* I2S_OUT_CTL:
* I2S_IN_SONY_MODE , LEFT SAMPLE on WS = 1
* I2S_OUT_MASTER_MODE = Master
*/
cx25840_write ( client , 0x918 , 0xa0 ) ;
cx25840_write ( client , 0x919 , 0x01 ) ;
}
2005-11-13 16:07:56 -08:00
return 0 ;
}
/* ----------------------------------------------------------------------- */
2008-04-22 14:45:51 -03:00
static int set_v4lstd ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2008-04-22 14:45:51 -03:00
u8 fmt = 0 ; /* zero is autodetect */
u8 pal_m = 0 ;
2005-12-19 08:54:11 -02:00
/* First tests should be against specific std */
2008-04-22 14:45:51 -03:00
if ( state - > std = = V4L2_STD_NTSC_M_JP ) {
fmt = 0x2 ;
} else if ( state - > std = = V4L2_STD_NTSC_443 ) {
fmt = 0x3 ;
} else if ( state - > std = = V4L2_STD_PAL_M ) {
pal_m = 1 ;
fmt = 0x5 ;
} else if ( state - > std = = V4L2_STD_PAL_N ) {
fmt = 0x6 ;
} else if ( state - > std = = V4L2_STD_PAL_Nc ) {
fmt = 0x7 ;
} else if ( state - > std = = V4L2_STD_PAL_60 ) {
fmt = 0x8 ;
2005-12-19 08:54:11 -02:00
} else {
/* Then, test against generic ones */
2008-04-22 14:45:51 -03:00
if ( state - > std & V4L2_STD_NTSC )
fmt = 0x1 ;
else if ( state - > std & V4L2_STD_PAL )
fmt = 0x4 ;
else if ( state - > std & V4L2_STD_SECAM )
fmt = 0xc ;
2005-11-13 16:07:56 -08:00
}
2006-06-04 12:15:55 -03:00
v4l_dbg ( 1 , cx25840_debug , client , " changing video std to fmt %i \n " , fmt ) ;
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 ) ;
2008-04-22 14:45:51 -03:00
cx25840_and_or ( client , 0x403 , ~ 0x3 , pal_m ) ;
2008-07-17 11:08:40 -03:00
cx25840_std_setup ( client ) ;
2009-09-26 23:47:21 -03:00
if ( ! is_cx2583x ( state ) )
2008-04-22 14:45:51 -03:00
input_change ( client ) ;
2005-11-13 16:07:56 -08:00
return 0 ;
}
/* ----------------------------------------------------------------------- */
2008-11-29 12:50:06 -03:00
static int cx25840_s_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2005-11-13 16:07:56 -08:00
{
2009-03-03 14:36:55 -03:00
struct cx25840_state * state = to_state ( sd ) ;
2008-11-29 12:50:06 -03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-13 16:07:56 -08:00
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 :
2009-01-29 16:09:13 -03:00
if ( ctrl - > value < - 128 | | 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 :
2009-09-26 23:47:21 -03:00
if ( is_cx2583x ( state ) )
2006-04-22 10:22:46 -03:00
return - EINVAL ;
2009-03-30 06:26:40 -03:00
return cx25840_audio_s_ctrl ( sd , ctrl ) ;
2006-01-09 15:25:44 -02:00
default :
return - EINVAL ;
2005-11-13 16:07:56 -08:00
}
return 0 ;
}
2008-11-29 12:50:06 -03:00
static int cx25840_g_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
2005-11-13 16:07:56 -08:00
{
2009-03-03 14:36:55 -03:00
struct cx25840_state * state = to_state ( sd ) ;
2008-11-29 12:50:06 -03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-13 16:07:56 -08:00
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 :
2009-09-26 23:47:21 -03:00
if ( is_cx2583x ( state ) )
2006-04-22 10:22:46 -03:00
return - EINVAL ;
2009-03-30 06:26:40 -03:00
return cx25840_audio_g_ctrl ( sd , ctrl ) ;
2005-11-13 16:07:56 -08:00
default :
return - EINVAL ;
}
return 0 ;
}
/* ----------------------------------------------------------------------- */
2010-05-09 09:48:50 -03:00
static int cx25840_s_mbus_fmt ( struct v4l2_subdev * sd , struct v4l2_mbus_framefmt * fmt )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-13 16:07:56 -08:00
int HSC , VSC , Vsrc , Hsrc , filter , Vlines ;
2008-04-22 14:45:51 -03:00
int is_50Hz = ! ( state - > std & V4L2_STD_525_60 ) ;
2005-11-13 16:07:56 -08:00
2010-05-09 09:48:50 -03:00
if ( fmt - > code ! = V4L2_MBUS_FMT_FIXED )
return - EINVAL ;
2005-11-13 16:07:56 -08:00
2010-05-09 09:48:50 -03:00
fmt - > field = V4L2_FIELD_INTERLACED ;
fmt - > colorspace = V4L2_COLORSPACE_SMPTE170M ;
2005-11-13 16:07:56 -08:00
2010-05-09 09:48:50 -03:00
Vsrc = ( cx25840_read ( client , 0x476 ) & 0x3f ) < < 4 ;
Vsrc | = ( cx25840_read ( client , 0x475 ) & 0xf0 ) > > 4 ;
2005-11-13 16:07:56 -08:00
2010-05-09 09:48:50 -03:00
Hsrc = ( cx25840_read ( client , 0x472 ) & 0x3f ) < < 4 ;
Hsrc | = ( cx25840_read ( client , 0x471 ) & 0xf0 ) > > 4 ;
2005-11-13 16:07:56 -08:00
2010-05-09 09:48:50 -03:00
Vlines = fmt - > height + ( is_50Hz ? 4 : 7 ) ;
2005-11-13 16:07:56 -08:00
2010-05-09 09:48:50 -03:00
if ( ( fmt - > width * 16 < Hsrc ) | | ( Hsrc < fmt - > width ) | |
( Vlines * 8 < Vsrc ) | | ( Vsrc < Vlines ) ) {
v4l_err ( client , " %dx%d is not a valid size! \n " ,
fmt - > width , fmt - > height ) ;
return - ERANGE ;
}
2005-11-13 16:07:56 -08:00
2010-05-09 09:48:50 -03:00
HSC = ( Hsrc * ( 1 < < 20 ) ) / fmt - > width - ( 1 < < 20 ) ;
VSC = ( 1 < < 16 ) - ( Vsrc * ( 1 < < 9 ) / Vlines - ( 1 < < 9 ) ) ;
VSC & = 0x1fff ;
if ( fmt - > width > = 385 )
filter = 0 ;
else if ( fmt - > width > 192 )
filter = 1 ;
else if ( fmt - > width > 96 )
filter = 2 ;
else
filter = 3 ;
v4l_dbg ( 1 , cx25840_debug , client , " decoder set size %dx%d -> scale %ux%u \n " ,
fmt - > width , fmt - > 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 ) ;
return 0 ;
}
2005-11-13 16:07:56 -08:00
/* ----------------------------------------------------------------------- */
2007-09-13 11:44:47 -03:00
static void log_video_status ( struct i2c_client * client )
{
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 "
} ;
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2007-09-13 11:44:47 -03:00
u8 vidfmt_sel = cx25840_read ( client , 0x400 ) & 0xf ;
u8 gen_stat1 = cx25840_read ( client , 0x40d ) ;
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 )
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2007-09-13 11:44:47 -03: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 ) ;
int aud_input = state - > aud_input ;
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 " ;
}
v4l_info ( client , " Detected audio mode: %s \n " , p ) ;
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 " ;
}
v4l_info ( client , " Detected audio standard: %s \n " , p ) ;
v4l_info ( client , " Audio muted: %s \n " ,
( state - > unmute_volume > = 0 ) ? " yes " : " no " ) ;
v4l_info ( client , " Audio microcontroller: %s \n " ,
( download_ctl & 0x10 ) ?
( ( mute_ctl & 0x2 ) ? " detecting " : " 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 " ;
}
v4l_info ( client , " Configured audio standard: %s \n " , p ) ;
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 " ;
}
v4l_info ( client , " Configured audio mode: %s \n " , p ) ;
} 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 " ;
}
v4l_info ( client , " Configured audio system: %s \n " , p ) ;
}
if ( aud_input ) {
v4l_info ( client , " Specified audio input: Tuner (In%d) \n " , aud_input ) ;
} else {
v4l_info ( client , " Specified audio input: External \n " ) ;
}
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 " ;
}
v4l_info ( client , " Preferred audio mode: %s \n " , p ) ;
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 " ;
}
v4l_info ( client , " Selected 65 MHz format: %s \n " , p ) ;
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 " ;
}
v4l_info ( client , " Selected 45 MHz format: %s \n " , p ) ;
}
}
/* ----------------------------------------------------------------------- */
2009-03-29 19:20:26 -03:00
/* This load_fw operation must be called to load the driver's firmware.
2009-01-15 05:53:18 -03:00
Without this the audio standard detection will fail and you will
only get mono .
Since loading the firmware is often problematic when the driver is
compiled into the kernel I recommend postponing calling this function
until the first open of the video device . Another reason for
postponing it is that loading this firmware takes a long time ( seconds )
due to the slow i2c bus speed . So it will speed up the boot process if
you can avoid loading the fw as long as the video device isn ' t used . */
2009-03-29 19:20:26 -03:00
static int cx25840_load_fw ( struct v4l2_subdev * sd )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2007-07-22 12:52:40 -03:00
if ( ! state - > is_initialized ) {
2009-03-29 19:20:26 -03:00
/* initialize and load firmware */
2007-07-22 12:52:40 -03:00
state - > is_initialized = 1 ;
2009-09-26 23:47:21 -03:00
if ( is_cx2583x ( state ) )
2007-07-22 12:52:40 -03:00
cx25836_initialize ( client ) ;
2009-09-26 23:47:21 -03:00
else if ( is_cx2388x ( state ) )
2008-01-10 01:22:39 -03:00
cx23885_initialize ( client ) ;
2009-09-26 23:47:21 -03:00
else if ( is_cx231xx ( state ) )
2009-03-03 06:07:42 -03:00
cx231xx_initialize ( client ) ;
2007-07-22 12:52:40 -03:00
else
2007-08-04 05:00:07 -03:00
cx25840_initialize ( client ) ;
2007-07-22 12:52:40 -03:00
}
2008-11-29 12:50:06 -03:00
return 0 ;
}
2007-07-22 12:52:40 -03:00
2005-11-13 16:07:56 -08:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
2008-12-30 07:14:19 -03:00
static int cx25840_g_register ( struct v4l2_subdev * sd , struct v4l2_dbg_register * reg )
2008-11-29 12:50:06 -03:00
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2008-01-10 01:22:39 -03:00
2008-12-30 07:14:19 -03:00
if ( ! v4l2_chip_match_i2c_client ( client , & reg - > match ) )
2008-11-29 12:50:06 -03:00
return - EINVAL ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
2008-12-30 07:14:19 -03:00
reg - > size = 1 ;
2008-11-29 12:50:06 -03:00
reg - > val = cx25840_read ( client , reg - > reg & 0x0fff ) ;
return 0 ;
}
2008-12-30 07:14:19 -03:00
static int cx25840_s_register ( struct v4l2_subdev * sd , struct v4l2_dbg_register * reg )
2008-11-29 12:50:06 -03:00
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2008-12-30 07:14:19 -03:00
if ( ! v4l2_chip_match_i2c_client ( client , & reg - > match ) )
2008-11-29 12:50:06 -03:00
return - EINVAL ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
cx25840_write ( client , reg - > reg & 0x0fff , reg - > val & 0xff ) ;
return 0 ;
}
2005-11-13 16:07:56 -08:00
# endif
2009-12-24 13:06:08 -03:00
static int cx25840_s_audio_stream ( struct v4l2_subdev * sd , int enable )
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
u8 v ;
if ( is_cx2583x ( state ) | | is_cx2388x ( state ) | | is_cx231xx ( state ) )
return 0 ;
v4l_dbg ( 1 , cx25840_debug , client , " %s audio output \n " ,
enable ? " enable " : " disable " ) ;
if ( enable ) {
v = cx25840_read ( client , 0x115 ) | 0x80 ;
cx25840_write ( client , 0x115 , v ) ;
v = cx25840_read ( client , 0x116 ) | 0x03 ;
cx25840_write ( client , 0x116 , v ) ;
} else {
v = cx25840_read ( client , 0x115 ) & ~ ( 0x80 ) ;
cx25840_write ( client , 0x115 , v ) ;
v = cx25840_read ( client , 0x116 ) & ~ ( 0x03 ) ;
cx25840_write ( client , 0x116 , v ) ;
}
return 0 ;
}
2008-11-29 12:50:06 -03:00
static int cx25840_s_stream ( struct v4l2_subdev * sd , int enable )
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-12-24 13:06:08 -03:00
u8 v ;
2005-11-13 16:07:56 -08:00
2009-12-24 13:06:08 -03:00
v4l_dbg ( 1 , cx25840_debug , client , " %s video output \n " ,
2008-11-29 12:50:06 -03:00
enable ? " enable " : " disable " ) ;
if ( enable ) {
2009-09-26 23:47:21 -03:00
if ( is_cx2388x ( state ) | | is_cx231xx ( state ) ) {
2009-12-24 13:06:08 -03:00
v = cx25840_read ( client , 0x421 ) | 0x0b ;
2008-01-10 01:22:39 -03:00
cx25840_write ( client , 0x421 , v ) ;
} else {
2009-12-24 13:06:08 -03:00
v = cx25840_read ( client , 0x115 ) | 0x0c ;
cx25840_write ( client , 0x115 , v ) ;
v = cx25840_read ( client , 0x116 ) | 0x04 ;
cx25840_write ( client , 0x116 , v ) ;
2008-01-10 01:22:39 -03:00
}
2008-11-29 12:50:06 -03:00
} else {
2009-09-26 23:47:21 -03:00
if ( is_cx2388x ( state ) | | is_cx231xx ( state ) ) {
2009-12-24 13:06:08 -03:00
v = cx25840_read ( client , 0x421 ) & ~ ( 0x0b ) ;
2008-01-10 01:22:39 -03:00
cx25840_write ( client , 0x421 , v ) ;
} else {
2009-12-24 13:06:08 -03:00
v = cx25840_read ( client , 0x115 ) & ~ ( 0x0c ) ;
cx25840_write ( client , 0x115 , v ) ;
v = cx25840_read ( client , 0x116 ) & ~ ( 0x04 ) ;
cx25840_write ( client , 0x116 , v ) ;
2008-01-10 01:22:39 -03:00
}
2008-11-29 12:50:06 -03:00
}
return 0 ;
}
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
static int cx25840_queryctrl ( struct v4l2_subdev * sd , struct v4l2_queryctrl * qc )
{
struct cx25840_state * state = to_state ( sd ) ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
switch ( qc - > id ) {
case V4L2_CID_BRIGHTNESS :
2009-02-21 18:47:24 -03:00
return v4l2_ctrl_query_fill ( qc , 0 , 255 , 1 , 128 ) ;
2008-11-29 12:50:06 -03:00
case V4L2_CID_CONTRAST :
case V4L2_CID_SATURATION :
2009-02-21 18:47:24 -03:00
return v4l2_ctrl_query_fill ( qc , 0 , 127 , 1 , 64 ) ;
2008-11-29 12:50:06 -03:00
case V4L2_CID_HUE :
2009-02-21 18:47:24 -03:00
return v4l2_ctrl_query_fill ( qc , - 128 , 127 , 1 , 0 ) ;
2008-11-29 12:50:06 -03:00
default :
break ;
}
2009-09-26 23:47:21 -03:00
if ( is_cx2583x ( state ) )
2008-11-29 12:50:06 -03:00
return - EINVAL ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
switch ( qc - > id ) {
case V4L2_CID_AUDIO_VOLUME :
return v4l2_ctrl_query_fill ( qc , 0 , 65535 ,
65535 / 100 , state - > default_volume ) ;
case V4L2_CID_AUDIO_MUTE :
2009-02-21 18:47:24 -03:00
return v4l2_ctrl_query_fill ( qc , 0 , 1 , 1 , 0 ) ;
2008-11-29 12:50:06 -03:00
case V4L2_CID_AUDIO_BALANCE :
case V4L2_CID_AUDIO_BASS :
case V4L2_CID_AUDIO_TREBLE :
2009-02-21 18:47:24 -03:00
return v4l2_ctrl_query_fill ( qc , 0 , 65535 , 65535 / 100 , 32768 ) ;
2008-11-29 12:50:06 -03:00
default :
return - EINVAL ;
}
return - EINVAL ;
}
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
static int cx25840_s_std ( struct v4l2_subdev * sd , v4l2_std_id std )
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 15:32:41 -02:00
2008-11-29 12:50:06 -03:00
if ( state - > radio = = 0 & & state - > std = = std )
return 0 ;
state - > radio = 0 ;
state - > std = std ;
return set_v4lstd ( client ) ;
}
2006-04-22 10:22:46 -03:00
2008-11-29 12:50:06 -03:00
static int cx25840_s_radio ( struct v4l2_subdev * sd )
{
struct cx25840_state * state = to_state ( sd ) ;
2006-01-09 15:32:41 -02:00
2008-11-29 12:50:06 -03:00
state - > radio = 1 ;
return 0 ;
}
2005-11-13 16:07:56 -08:00
2009-04-02 11:26:22 -03:00
static int cx25840_s_video_routing ( struct v4l2_subdev * sd ,
u32 input , u32 output , u32 config )
2008-11-29 12:50:06 -03:00
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 15:25:44 -02:00
2009-04-02 11:26:22 -03:00
return set_input ( client , input , state - > aud_input ) ;
2008-11-29 12:50:06 -03:00
}
2005-11-13 16:07:56 -08:00
2009-04-02 11:26:22 -03:00
static int cx25840_s_audio_routing ( struct v4l2_subdev * sd ,
u32 input , u32 output , u32 config )
2008-11-29 12:50:06 -03:00
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-13 16:07:56 -08:00
2009-09-26 23:47:21 -03:00
if ( is_cx2583x ( state ) )
2008-11-29 12:50:06 -03:00
return - EINVAL ;
2009-04-02 11:26:22 -03:00
return set_input ( client , state - > vid_input , input ) ;
2008-11-29 12:50:06 -03:00
}
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
static int cx25840_s_frequency ( struct v4l2_subdev * sd , struct v4l2_frequency * freq )
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 15:25:42 -02:00
2009-09-26 23:47:21 -03:00
if ( ! is_cx2583x ( state ) )
2008-11-29 12:50:06 -03:00
input_change ( client ) ;
return 0 ;
}
2006-01-09 15:25:42 -02:00
2008-11-29 12:50:06 -03:00
static int cx25840_g_tuner ( struct v4l2_subdev * sd , struct v4l2_tuner * vt )
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
u8 vpres = cx25840_read ( client , 0x40e ) & 0x20 ;
u8 mode ;
int val = 0 ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
if ( state - > radio )
return 0 ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
vt - > signal = vpres ? 0xffff : 0x0 ;
2009-09-26 23:47:21 -03:00
if ( is_cx2583x ( state ) )
2008-11-29 12:50:06 -03:00
return 0 ;
2006-01-09 15:25:44 -02:00
2008-11-29 12:50:06 -03: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
2008-11-29 12:50:06 -03:00
mode = cx25840_read ( client , 0x804 ) ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
/* get rxsubchans and audmode */
if ( ( mode & 0xf ) = = 1 )
val | = V4L2_TUNER_SUB_STEREO ;
else
val | = V4L2_TUNER_SUB_MONO ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
if ( mode = = 2 | | mode = = 4 )
val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2 ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
if ( mode & 0x10 )
val | = V4L2_TUNER_SUB_SAP ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
vt - > rxsubchans = val ;
vt - > audmode = state - > audmode ;
return 0 ;
}
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
static int cx25840_s_tuner ( struct v4l2_subdev * sd , struct v4l2_tuner * vt )
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-13 16:07:56 -08:00
2009-09-26 23:47:21 -03:00
if ( state - > radio | | is_cx2583x ( state ) )
2008-11-29 12:50:06 -03:00
return 0 ;
2006-01-23 17:11:09 -02:00
2008-11-29 12:50:06 -03:00
switch ( vt - > audmode ) {
2005-11-13 16:07:56 -08:00
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 ;
2008-11-29 12:50:06 -03:00
}
state - > audmode = vt - > audmode ;
return 0 ;
}
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
static int cx25840_reset ( struct v4l2_subdev * sd , u32 val )
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-13 16:07:56 -08:00
2009-09-26 23:47:21 -03:00
if ( is_cx2583x ( state ) )
2008-11-29 12:50:06 -03:00
cx25836_initialize ( client ) ;
2009-09-26 23:47:21 -03:00
else if ( is_cx2388x ( state ) )
2008-11-29 12:50:06 -03:00
cx23885_initialize ( client ) ;
2009-09-26 23:47:21 -03:00
else if ( is_cx231xx ( state ) )
2009-03-03 06:07:42 -03:00
cx231xx_initialize ( client ) ;
2008-11-29 12:50:06 -03:00
else
cx25840_initialize ( client ) ;
return 0 ;
}
2005-11-13 16:07:56 -08:00
2008-12-30 07:14:19 -03:00
static int cx25840_g_chip_ident ( struct v4l2_subdev * sd , struct v4l2_dbg_chip_ident * chip )
2008-11-29 12:50:06 -03:00
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
return v4l2_chip_ident_i2c_client ( client , chip , state - > id , state - > rev ) ;
}
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
static int cx25840_log_status ( struct v4l2_subdev * sd )
{
struct cx25840_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-13 16:07:56 -08:00
2008-11-29 12:50:06 -03:00
log_video_status ( client ) ;
2009-09-26 23:47:21 -03:00
if ( ! is_cx2583x ( state ) )
2008-11-29 12:50:06 -03:00
log_audio_status ( client ) ;
2006-01-09 15:25:44 -02:00
return 0 ;
2005-11-13 16:07:56 -08:00
}
2008-11-29 12:50:06 -03:00
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops cx25840_core_ops = {
. log_status = cx25840_log_status ,
. g_chip_ident = cx25840_g_chip_ident ,
. g_ctrl = cx25840_g_ctrl ,
. s_ctrl = cx25840_s_ctrl ,
. queryctrl = cx25840_queryctrl ,
2009-04-01 03:52:39 -03:00
. s_std = cx25840_s_std ,
2008-11-29 12:50:06 -03:00
. reset = cx25840_reset ,
2009-03-29 19:20:26 -03:00
. load_fw = cx25840_load_fw ,
2008-11-29 12:50:06 -03:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
. g_register = cx25840_g_register ,
. s_register = cx25840_s_register ,
# endif
} ;
static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
. s_frequency = cx25840_s_frequency ,
. s_radio = cx25840_s_radio ,
. g_tuner = cx25840_g_tuner ,
. s_tuner = cx25840_s_tuner ,
} ;
static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
. s_clock_freq = cx25840_s_clock_freq ,
. s_routing = cx25840_s_audio_routing ,
2009-12-24 13:06:08 -03:00
. s_stream = cx25840_s_audio_stream ,
2008-11-29 12:50:06 -03:00
} ;
static const struct v4l2_subdev_video_ops cx25840_video_ops = {
. s_routing = cx25840_s_video_routing ,
2010-05-09 09:48:50 -03:00
. s_mbus_fmt = cx25840_s_mbus_fmt ,
2008-11-29 12:50:06 -03:00
. s_stream = cx25840_s_stream ,
} ;
2010-03-14 09:57:30 -03:00
static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = {
. decode_vbi_line = cx25840_decode_vbi_line ,
2010-03-14 10:16:46 -03:00
. s_raw_fmt = cx25840_s_raw_fmt ,
. s_sliced_fmt = cx25840_s_sliced_fmt ,
. g_sliced_fmt = cx25840_g_sliced_fmt ,
2010-03-14 09:57:30 -03:00
} ;
2008-11-29 12:50:06 -03:00
static const struct v4l2_subdev_ops cx25840_ops = {
. core = & cx25840_core_ops ,
. tuner = & cx25840_tuner_ops ,
. audio = & cx25840_audio_ops ,
. video = & cx25840_video_ops ,
2010-03-14 09:57:30 -03:00
. vbi = & cx25840_vbi_ops ,
2008-11-29 12:50:06 -03:00
} ;
2005-11-13 16:07:56 -08:00
/* ----------------------------------------------------------------------- */
2009-09-26 23:32:54 -03:00
static u32 get_cx2388x_ident ( struct i2c_client * client )
{
u32 ret ;
/* Come out of digital power down */
cx25840_write ( client , 0x000 , 0 ) ;
2009-09-27 15:54:47 -03:00
/* Detecting whether the part is cx23885/7/8 is more
* difficult than it needs to be . No ID register . Instead we
* probe certain registers indicated in the datasheets to look
* for specific defaults that differ between the silicon designs . */
/* It's either 885/7 if the IR Tx Clk Divider register exists */
2009-09-26 23:32:54 -03:00
if ( cx25840_read4 ( client , 0x204 ) & 0xffff ) {
2009-09-27 15:54:47 -03:00
/* CX23885 returns bogus repetitive byte values for the DIF,
* which doesn ' t exist for it . ( Ex . 8 a8a8a8a or 31313131 ) */
ret = cx25840_read4 ( client , 0x300 ) ;
if ( ( ( ret & 0xffff0000 ) > > 16 ) = = ( ret & 0xffff ) ) {
/* No DIF */
ret = V4L2_IDENT_CX23885_AV ;
} else {
/* CX23887 has a broken DIF, but the registers
* appear valid ( but unsed ) , good enough to detect . */
ret = V4L2_IDENT_CX23887_AV ;
}
2009-09-26 23:32:54 -03:00
} else if ( cx25840_read4 ( client , 0x300 ) & 0x0fffffff ) {
/* DIF PLL Freq Word reg exists; chip must be a CX23888 */
ret = V4L2_IDENT_CX23888_AV ;
} else {
2009-09-27 15:54:47 -03:00
v4l_err ( client , " Unable to detect h/w, assuming cx23887 \n " ) ;
2009-09-26 23:32:54 -03:00
ret = V4L2_IDENT_CX23887_AV ;
}
/* Back into digital power down */
cx25840_write ( client , 0x000 , 2 ) ;
return ret ;
}
2008-04-29 23:11:39 +02:00
static int cx25840_probe ( struct i2c_client * client ,
const struct i2c_device_id * did )
2005-11-13 16:07:56 -08:00
{
struct cx25840_state * state ;
2008-11-29 12:50:06 -03:00
struct v4l2_subdev * sd ;
2009-09-26 23:32:54 -03:00
u32 id = V4L2_IDENT_NONE ;
2005-11-13 16:07:56 -08:00
u16 device_id ;
2007-09-16 10:47:15 -03:00
/* Check if the adapter supports the needed features */
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
return - EIO ;
2007-08-26 10:53:16 -03:00
v4l_dbg ( 1 , cx25840_debug , client , " detecting cx25840 client on address 0x%x \n " , client - > addr < < 1 ) ;
2005-11-13 16:07:56 -08:00
device_id = cx25840_read ( client , 0x101 ) < < 8 ;
device_id | = cx25840_read ( client , 0x100 ) ;
2008-01-10 01:22:39 -03:00
v4l_dbg ( 1 , cx25840_debug , client , " device_id = 0x%04x \n " , device_id ) ;
2005-11-13 16:07:56 -08:00
/* 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 ;
2009-09-26 23:32:54 -03:00
} else if ( ( device_id & 0xff00 ) = = 0x8400 ) {
2006-04-22 10:22:46 -03:00
id = V4L2_IDENT_CX25840 + ( ( device_id > > 4 ) & 0xf ) ;
2009-03-13 13:36:00 -03:00
} else if ( device_id = = 0x0000 ) {
2009-09-26 23:32:54 -03:00
id = get_cx2388x_ident ( client ) ;
2009-03-03 06:07:42 -03:00
} else if ( ( device_id & 0xfff0 ) = = 0x5A30 ) {
2009-09-26 23:32:54 -03:00
/* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */
id = V4L2_IDENT_CX2310X_AV ;
} else if ( ( device_id & 0xff ) = = ( device_id > > 8 ) ) {
v4l_err ( client ,
" likely a confused/unresponsive cx2388[578] A/V decoder "
" found @ 0x%x (%s) \n " ,
client - > addr < < 1 , client - > adapter - > name ) ;
v4l_err ( client , " A method to reset it from the cx25840 driver "
" software is not known at this time \n " ) ;
return - ENODEV ;
} else {
2006-01-11 22:41:36 -02:00
v4l_dbg ( 1 , cx25840_debug , client , " cx25840 not found \n " ) ;
2007-09-16 10:47:15 -03:00
return - ENODEV ;
2005-11-13 16:07:56 -08:00
}
2007-08-26 10:53:16 -03:00
state = kzalloc ( sizeof ( struct cx25840_state ) , GFP_KERNEL ) ;
2008-11-29 12:50:06 -03:00
if ( state = = NULL )
2007-08-26 10:53:16 -03:00
return - ENOMEM ;
2008-11-29 12:50:06 -03:00
sd = & state - > sd ;
v4l2_i2c_subdev_init ( sd , client , & cx25840_ops ) ;
2009-09-26 23:32:54 -03:00
switch ( id ) {
case V4L2_IDENT_CX23885_AV :
v4l_info ( client , " cx23885 A/V decoder found @ 0x%x (%s) \n " ,
client - > addr < < 1 , client - > adapter - > name ) ;
break ;
case V4L2_IDENT_CX23887_AV :
v4l_info ( client , " cx23887 A/V decoder found @ 0x%x (%s) \n " ,
client - > addr < < 1 , client - > adapter - > name ) ;
break ;
case V4L2_IDENT_CX23888_AV :
v4l_info ( client , " cx23888 A/V decoder found @ 0x%x (%s) \n " ,
client - > addr < < 1 , client - > adapter - > name ) ;
break ;
case V4L2_IDENT_CX2310X_AV :
v4l_info ( client , " cx%d A/V decoder found @ 0x%x (%s) \n " ,
device_id , client - > addr < < 1 , client - > adapter - > name ) ;
break ;
case V4L2_IDENT_CX25840 :
case V4L2_IDENT_CX25841 :
case V4L2_IDENT_CX25842 :
case V4L2_IDENT_CX25843 :
/* Note: revision '(device_id & 0x0f) == 2' was never built. The
marking skips from 0x1 = = 22 to 0x3 = = 23. */
v4l_info ( client , " cx25%3x-2%x found @ 0x%x (%s) \n " ,
( device_id & 0xfff0 ) > > 4 ,
( device_id & 0x0f ) < 3 ? ( device_id & 0x0f ) + 1
: ( device_id & 0x0f ) ,
client - > addr < < 1 , client - > adapter - > name ) ;
break ;
case V4L2_IDENT_CX25836 :
case V4L2_IDENT_CX25837 :
default :
v4l_info ( client , " cx25%3x-%x found @ 0x%x (%s) \n " ,
( device_id & 0xfff0 ) > > 4 , device_id & 0x0f ,
client - > addr < < 1 , client - > adapter - > name ) ;
break ;
}
2005-11-13 16:07:56 -08:00
2007-08-26 10:53:16 -03:00
state - > c = client ;
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 ;
2007-08-05 08:00:36 -03:00
state - > unmute_volume = - 1 ;
2008-07-17 12:26:45 -03:00
state - > default_volume = 228 - cx25840_read ( client , 0x8d4 ) ;
state - > default_volume = ( ( state - > default_volume / 2 ) + 23 ) < < 9 ;
2006-05-24 10:16:45 -03:00
state - > vbi_line_offset = 8 ;
2006-04-22 10:22:46 -03:00
state - > id = id ;
2007-04-27 12:31:08 -03:00
state - > rev = device_id ;
2008-01-10 01:22:39 -03:00
2005-11-13 16:07:56 -08:00
return 0 ;
}
2007-09-13 11:44:47 -03:00
static int cx25840_remove ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
2008-11-29 12:50:06 -03:00
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
v4l2_device_unregister_subdev ( sd ) ;
kfree ( to_state ( sd ) ) ;
2005-11-13 16:07:56 -08:00
return 0 ;
}
2008-05-18 20:49:40 +02:00
static const struct i2c_device_id cx25840_id [ ] = {
{ " cx25840 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , cx25840_id ) ;
2007-09-13 11:44:47 -03:00
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
. name = " cx25840 " ,
. probe = cx25840_probe ,
. remove = cx25840_remove ,
2008-05-18 20:49:40 +02:00
. id_table = cx25840_id ,
2005-11-13 16:07:56 -08:00
} ;