2005-06-28 20:45:21 -07:00
/*
* For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview
* I2C address is allways 0xC0 .
*
*
2006-04-03 07:53:40 -03:00
* Copyright ( c ) 2005 Mauro Carvalho Chehab ( mchehab @ infradead . org )
2005-06-28 20:45:21 -07:00
* This code is placed under the terms of the GNU General Public License
*
* tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa
* from their contributions on DScaler .
*/
# include <linux/i2c.h>
2005-07-12 13:58:55 -07:00
# include <linux/delay.h>
2007-08-21 01:14:12 -03:00
# include <linux/videodev.h>
2007-08-27 21:24:27 -03:00
# include "tuner-i2c.h"
# include "tea5767.h"
2005-06-28 20:45:21 -07:00
2007-08-27 21:24:27 -03:00
static int debug = 0 ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " enable verbose debug messages " ) ;
2005-06-28 20:45:21 -07:00
2007-08-27 21:24:27 -03:00
# define PREFIX "tea5767 "
2006-01-09 15:32:40 -02:00
2007-08-21 01:24:42 -03:00
struct tea5767_priv {
struct tuner_i2c_props i2c_props ;
2007-08-27 21:24:27 -03:00
u32 frequency ;
2007-08-21 01:24:42 -03:00
} ;
2005-06-28 20:45:21 -07:00
/*****************************************************************************/
/******************************
* Write mode register values *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* First register */
2005-07-12 13:58:55 -07:00
# define TEA5767_MUTE 0x80 /* Mutes output */
# define TEA5767_SEARCH 0x40 /* Activates station search */
2005-06-28 20:45:21 -07:00
/* Bits 0-5 for divider MSB */
/* Second register */
/* Bits 0-7 for divider LSB */
/* Third register */
/* Station search from botton to up */
# define TEA5767_SEARCH_UP 0x80
/* Searches with ADC output = 10 */
# define TEA5767_SRCH_HIGH_LVL 0x60
/* Searches with ADC output = 10 */
# define TEA5767_SRCH_MID_LVL 0x40
/* Searches with ADC output = 5 */
# define TEA5767_SRCH_LOW_LVL 0x20
/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */
# define TEA5767_HIGH_LO_INJECT 0x10
/* Disable stereo */
# define TEA5767_MONO 0x08
/* Disable right channel and turns to mono */
# define TEA5767_MUTE_RIGHT 0x04
/* Disable left channel and turns to mono */
# define TEA5767_MUTE_LEFT 0x02
# define TEA5767_PORT1_HIGH 0x01
2006-01-15 15:04:52 -02:00
/* Fourth register */
2005-06-28 20:45:21 -07:00
# define TEA5767_PORT2_HIGH 0x80
/* Chips stops working. Only I2C bus remains on */
# define TEA5767_STDBY 0x40
/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */
# define TEA5767_JAPAN_BAND 0x20
/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */
# define TEA5767_XTAL_32768 0x10
/* Cuts weak signals */
# define TEA5767_SOFT_MUTE 0x08
/* Activates high cut control */
# define TEA5767_HIGH_CUT_CTRL 0x04
/* Activates stereo noise control */
# define TEA5767_ST_NOISE_CTL 0x02
/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */
# define TEA5767_SRCH_IND 0x01
2006-01-15 15:04:52 -02:00
/* Fifth register */
2005-06-28 20:45:21 -07:00
/* By activating, it will use Xtal at 13 MHz as reference for divider */
# define TEA5767_PLLREF_ENABLE 0x80
/* By activating, deemphasis=50, or else, deemphasis of 50us */
# define TEA5767_DEEMPH_75 0X40
/*****************************
* Read mode register values *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* First register */
# define TEA5767_READY_FLAG_MASK 0x80
# define TEA5767_BAND_LIMIT_MASK 0X40
/* Bits 0-5 for divider MSB after search or preset */
/* Second register */
/* Bits 0-7 for divider LSB after search or preset */
/* Third register */
# define TEA5767_STEREO_MASK 0x80
# define TEA5767_IF_CNTR_MASK 0x7f
2006-01-15 15:04:52 -02:00
/* Fourth register */
2005-06-28 20:45:21 -07:00
# define TEA5767_ADC_LEVEL_MASK 0xf0
/* should be 0 */
# define TEA5767_CHIP_ID_MASK 0x0f
2006-01-15 15:04:52 -02:00
/* Fifth register */
2005-06-28 20:45:21 -07:00
/* Reserved for future extensions */
# define TEA5767_RESERVED_MASK 0xff
2005-07-12 13:58:55 -07:00
enum tea5767_xtal_freq {
2005-11-08 21:37:43 -08:00
TEA5767_LOW_LO_32768 = 0 ,
TEA5767_HIGH_LO_32768 = 1 ,
TEA5767_LOW_LO_13MHz = 2 ,
TEA5767_HIGH_LO_13MHz = 3 ,
2005-07-12 13:58:55 -07:00
} ;
2005-06-28 20:45:21 -07:00
/*****************************************************************************/
static void tea5767_status_dump ( unsigned char * buffer )
{
unsigned int div , frq ;
if ( TEA5767_READY_FLAG_MASK & buffer [ 0 ] )
printk ( PREFIX " Ready Flag ON \n " ) ;
else
printk ( PREFIX " Ready Flag OFF \n " ) ;
if ( TEA5767_BAND_LIMIT_MASK & buffer [ 0 ] )
printk ( PREFIX " Tuner at band limit \n " ) ;
else
printk ( PREFIX " Tuner not at band limit \n " ) ;
2005-07-12 13:58:55 -07:00
div = ( ( buffer [ 0 ] & 0x3f ) < < 8 ) | buffer [ 1 ] ;
2005-06-28 20:45:21 -07:00
switch ( TEA5767_HIGH_LO_32768 ) {
case TEA5767_HIGH_LO_13MHz :
2005-07-15 03:56:28 -07:00
frq = ( div * 50000 - 700000 - 225000 ) / 4 ; /* Freq in KHz */
2005-06-28 20:45:21 -07:00
break ;
case TEA5767_LOW_LO_13MHz :
2005-07-15 03:56:28 -07:00
frq = ( div * 50000 + 700000 + 225000 ) / 4 ; /* Freq in KHz */
2005-06-28 20:45:21 -07:00
break ;
case TEA5767_LOW_LO_32768 :
2005-07-15 03:56:28 -07:00
frq = ( div * 32768 + 700000 + 225000 ) / 4 ; /* Freq in KHz */
2005-06-28 20:45:21 -07:00
break ;
case TEA5767_HIGH_LO_32768 :
default :
2005-07-15 03:56:28 -07:00
frq = ( div * 32768 - 700000 - 225000 ) / 4 ; /* Freq in KHz */
2005-06-28 20:45:21 -07:00
break ;
}
2005-07-12 13:58:55 -07:00
buffer [ 0 ] = ( div > > 8 ) & 0x3f ;
buffer [ 1 ] = div & 0xff ;
2005-06-28 20:45:21 -07:00
printk ( PREFIX " Frequency %d.%03d KHz (divider = 0x%04x) \n " ,
2005-07-12 13:58:55 -07:00
frq / 1000 , frq % 1000 , div ) ;
2005-06-28 20:45:21 -07:00
if ( TEA5767_STEREO_MASK & buffer [ 2 ] )
printk ( PREFIX " Stereo \n " ) ;
else
printk ( PREFIX " Mono \n " ) ;
2005-07-12 13:58:55 -07:00
printk ( PREFIX " IF Counter = %d \n " , buffer [ 2 ] & TEA5767_IF_CNTR_MASK ) ;
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
printk ( PREFIX " ADC Level = %d \n " ,
( buffer [ 3 ] & TEA5767_ADC_LEVEL_MASK ) > > 4 ) ;
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
printk ( PREFIX " Chip ID = %d \n " , ( buffer [ 3 ] & TEA5767_CHIP_ID_MASK ) ) ;
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
printk ( PREFIX " Reserved = 0x%02x \n " ,
( buffer [ 4 ] & TEA5767_RESERVED_MASK ) ) ;
2005-06-28 20:45:21 -07:00
}
/* Freq should be specifyed at 62.5 Hz */
2007-08-27 21:24:27 -03:00
static int set_radio_freq ( struct dvb_frontend * fe ,
struct analog_parameters * params )
2005-06-28 20:45:21 -07:00
{
2007-08-27 21:24:27 -03:00
struct tea5767_priv * priv = fe - > tuner_priv ;
unsigned int frq = params - > frequency ;
2005-07-12 13:58:55 -07:00
unsigned char buffer [ 5 ] ;
2005-06-28 20:45:21 -07:00
unsigned div ;
int rc ;
2007-08-27 21:24:27 -03:00
tuner_dbg ( " radio freq = %d.%03d MHz \n " , frq / 16000 , ( frq / 16 ) % 1000 ) ;
2005-06-28 20:45:21 -07:00
/* Rounds freq to next decimal value - for 62.5 KHz step */
/* frq = 20*(frq/16)+radio_frq[frq%16]; */
buffer [ 2 ] = TEA5767_PORT1_HIGH ;
2005-07-12 13:58:55 -07:00
buffer [ 3 ] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL |
TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND ;
buffer [ 4 ] = 0 ;
2007-08-27 21:24:27 -03:00
if ( params - > audmode = = V4L2_TUNER_MODE_MONO ) {
2005-06-28 20:45:21 -07:00
tuner_dbg ( " TEA5767 set to mono \n " ) ;
buffer [ 2 ] | = TEA5767_MONO ;
2005-07-12 13:58:55 -07:00
} else {
tuner_dbg ( " TEA5767 set to stereo \n " ) ;
}
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
/* Should be replaced */
switch ( TEA5767_HIGH_LO_32768 ) {
2005-06-28 20:45:21 -07:00
case TEA5767_HIGH_LO_13MHz :
2007-08-27 21:24:27 -03:00
tuner_dbg ( " radio HIGH LO inject xtal @ 13 MHz \n " ) ;
2005-06-28 20:45:21 -07:00
buffer [ 2 ] | = TEA5767_HIGH_LO_INJECT ;
buffer [ 4 ] | = TEA5767_PLLREF_ENABLE ;
2006-01-15 15:04:52 -02:00
div = ( frq * ( 4000 / 16 ) + 700000 + 225000 + 25000 ) / 50000 ;
2005-06-28 20:45:21 -07:00
break ;
case TEA5767_LOW_LO_13MHz :
2007-08-27 21:24:27 -03:00
tuner_dbg ( " radio LOW LO inject xtal @ 13 MHz \n " ) ;
2005-06-28 20:45:21 -07:00
buffer [ 4 ] | = TEA5767_PLLREF_ENABLE ;
2006-01-15 15:04:52 -02:00
div = ( frq * ( 4000 / 16 ) - 700000 - 225000 + 25000 ) / 50000 ;
2005-06-28 20:45:21 -07:00
break ;
case TEA5767_LOW_LO_32768 :
2007-08-27 21:24:27 -03:00
tuner_dbg ( " radio LOW LO inject xtal @ 32,768 MHz \n " ) ;
2005-06-28 20:45:21 -07:00
buffer [ 3 ] | = TEA5767_XTAL_32768 ;
/* const 700=4000*175 Khz - to adjust freq to right value */
2006-01-15 15:04:52 -02:00
div = ( ( frq * ( 4000 / 16 ) - 700000 - 225000 ) + 16384 ) > > 15 ;
2005-06-28 20:45:21 -07:00
break ;
case TEA5767_HIGH_LO_32768 :
default :
2007-08-27 21:24:27 -03:00
tuner_dbg ( " radio HIGH LO inject xtal @ 32,768 MHz \n " ) ;
2005-06-28 20:45:21 -07:00
buffer [ 2 ] | = TEA5767_HIGH_LO_INJECT ;
buffer [ 3 ] | = TEA5767_XTAL_32768 ;
2005-07-15 03:56:28 -07:00
div = ( ( frq * ( 4000 / 16 ) + 700000 + 225000 ) + 16384 ) > > 15 ;
2005-06-28 20:45:21 -07:00
break ;
}
2005-07-12 13:58:55 -07:00
buffer [ 0 ] = ( div > > 8 ) & 0x3f ;
buffer [ 1 ] = div & 0xff ;
2005-06-28 20:45:21 -07:00
2007-08-21 01:24:42 -03:00
if ( 5 ! = ( rc = tuner_i2c_xfer_send ( & priv - > i2c_props , buffer , 5 ) ) )
2005-07-12 13:58:55 -07:00
tuner_warn ( " i2c i/o error: rc == %d (should be 5) \n " , rc ) ;
2005-07-15 03:56:28 -07:00
2007-08-27 21:24:27 -03:00
if ( debug ) {
2007-08-21 01:24:42 -03:00
if ( 5 ! = ( rc = tuner_i2c_xfer_recv ( & priv - > i2c_props , buffer , 5 ) ) )
2005-07-15 03:56:28 -07:00
tuner_warn ( " i2c i/o error: rc == %d (should be 5) \n " , rc ) ;
else
tea5767_status_dump ( buffer ) ;
}
2007-08-27 21:24:27 -03:00
priv - > frequency = frq * 125 / 2 ;
return 0 ;
2005-06-28 20:45:21 -07:00
}
2007-08-27 21:24:27 -03:00
static int tea5767_signal ( struct dvb_frontend * fe )
2005-06-28 20:45:21 -07:00
{
unsigned char buffer [ 5 ] ;
int rc ;
2007-08-27 21:24:27 -03:00
struct tea5767_priv * priv = fe - > tuner_priv ;
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
memset ( buffer , 0 , sizeof ( buffer ) ) ;
2007-08-21 01:24:42 -03:00
if ( 5 ! = ( rc = tuner_i2c_xfer_recv ( & priv - > i2c_props , buffer , 5 ) ) )
2005-07-12 13:58:55 -07:00
tuner_warn ( " i2c i/o error: rc == %d (should be 5) \n " , rc ) ;
2005-06-28 20:45:21 -07:00
2006-01-09 15:25:42 -02:00
return ( ( buffer [ 3 ] & TEA5767_ADC_LEVEL_MASK ) < < 8 ) ;
2005-06-28 20:45:21 -07:00
}
2007-08-27 21:24:27 -03:00
static int tea5767_stereo ( struct dvb_frontend * fe )
2005-06-28 20:45:21 -07:00
{
unsigned char buffer [ 5 ] ;
int rc ;
2007-08-27 21:24:27 -03:00
struct tea5767_priv * priv = fe - > tuner_priv ;
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
memset ( buffer , 0 , sizeof ( buffer ) ) ;
2007-08-21 01:24:42 -03:00
if ( 5 ! = ( rc = tuner_i2c_xfer_recv ( & priv - > i2c_props , buffer , 5 ) ) )
2005-07-12 13:58:55 -07:00
tuner_warn ( " i2c i/o error: rc == %d (should be 5) \n " , rc ) ;
2005-06-28 20:45:21 -07:00
rc = buffer [ 2 ] & TEA5767_STEREO_MASK ;
2007-08-27 21:24:27 -03:00
tuner_dbg ( " radio ST GET = %02x \n " , rc ) ;
2005-06-28 20:45:21 -07:00
2005-07-12 13:58:55 -07:00
return ( ( buffer [ 2 ] & TEA5767_STEREO_MASK ) ? V4L2_TUNER_SUB_STEREO : 0 ) ;
2005-06-28 20:45:21 -07:00
}
2007-08-27 21:24:27 -03:00
static int tea5767_get_status ( struct dvb_frontend * fe , u32 * status )
{
struct tea5767_priv * priv = fe - > tuner_priv ;
int signal = tea5767_signal ( fe ) ;
* status = 0 ;
if ( signal )
* status = TUNER_STATUS_LOCKED ;
if ( tea5767_stereo ( fe ) )
* status | = TUNER_STATUS_STEREO ;
tuner_dbg ( " tea5767: Signal strength: %d \n " , signal ) ;
return 0 ;
}
static int tea5767_standby ( struct dvb_frontend * fe )
2005-09-09 13:03:37 -07:00
{
unsigned char buffer [ 5 ] ;
2007-08-27 21:24:27 -03:00
struct tea5767_priv * priv = fe - > tuner_priv ;
2005-09-09 13:03:37 -07:00
unsigned div , rc ;
div = ( 87500 * 4 + 700 + 225 + 25 ) / 50 ; /* Set frequency to 87.5 MHz */
buffer [ 0 ] = ( div > > 8 ) & 0x3f ;
buffer [ 1 ] = div & 0xff ;
buffer [ 2 ] = TEA5767_PORT1_HIGH ;
buffer [ 3 ] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL |
TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY ;
buffer [ 4 ] = 0 ;
2007-08-21 01:24:42 -03:00
if ( 5 ! = ( rc = tuner_i2c_xfer_send ( & priv - > i2c_props , buffer , 5 ) ) )
2005-09-09 13:03:37 -07:00
tuner_warn ( " i2c i/o error: rc == %d (should be 5) \n " , rc ) ;
2007-08-27 21:24:27 -03:00
return 0 ;
2005-09-09 13:03:37 -07:00
}
2007-08-27 21:24:27 -03:00
int tea5767_autodetection ( struct i2c_adapter * i2c_adap , u8 i2c_addr )
2005-06-28 20:45:21 -07:00
{
2007-08-27 21:24:27 -03:00
struct tuner_i2c_props i2c = { . adap = i2c_adap , . addr = i2c_addr } ;
2005-07-31 22:34:43 -07:00
unsigned char buffer [ 7 ] = { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ;
2005-06-28 20:45:21 -07:00
int rc ;
2007-08-27 21:24:27 -03:00
if ( ( rc = tuner_i2c_xfer_recv ( & i2c , buffer , 7 ) ) < 5 ) {
printk ( KERN_WARNING " It is not a TEA5767. Received %i bytes. \n " , rc ) ;
2005-06-28 20:45:21 -07:00
return EINVAL ;
}
2005-07-31 22:34:43 -07:00
/* If all bytes are the same then it's a TV tuner and not a tea5767 */
2005-07-12 13:58:55 -07:00
if ( buffer [ 0 ] = = buffer [ 1 ] & & buffer [ 0 ] = = buffer [ 2 ] & &
buffer [ 0 ] = = buffer [ 3 ] & & buffer [ 0 ] = = buffer [ 4 ] ) {
2007-08-27 21:24:27 -03:00
printk ( KERN_WARNING " All bytes are equal. It is not a TEA5767 \n " ) ;
2005-06-28 20:45:21 -07:00
return EINVAL ;
}
/* Status bytes:
* Byte 4 : bit 3 : 1 : CI ( Chip Identification ) = = 0
2005-07-12 13:58:55 -07:00
* bit 0 : internally set to 0
2005-06-28 20:45:21 -07:00
* Byte 5 : bit 7 : 0 : = = 0
*/
2005-09-09 13:03:37 -07:00
if ( ( ( buffer [ 3 ] & 0x0f ) ! = 0x00 ) | | ( buffer [ 4 ] ! = 0x00 ) ) {
2007-08-27 21:24:27 -03:00
printk ( KERN_WARNING " Chip ID is not zero. It is not a TEA5767 \n " ) ;
2005-06-28 20:45:21 -07:00
return EINVAL ;
}
2005-07-15 03:56:28 -07:00
2005-08-01 21:11:38 -07:00
/* It seems that tea5767 returns 0xff after the 5th byte */
if ( ( buffer [ 5 ] ! = 0xff ) | | ( buffer [ 6 ] ! = 0xff ) ) {
2007-08-27 21:24:27 -03:00
printk ( KERN_WARNING " Returned more than 5 bytes. It is not a TEA5767 \n " ) ;
2005-08-01 21:11:38 -07:00
return EINVAL ;
}
2007-08-27 21:24:27 -03:00
printk ( KERN_WARNING " TEA5767 detected. \n " ) ;
2005-06-28 20:45:21 -07:00
return 0 ;
}
2007-08-27 21:24:27 -03:00
static int tea5767_release ( struct dvb_frontend * fe )
2007-08-21 01:24:42 -03:00
{
2007-08-27 21:24:27 -03:00
kfree ( fe - > tuner_priv ) ;
fe - > tuner_priv = NULL ;
return 0 ;
2007-08-21 01:24:42 -03:00
}
2007-08-27 21:24:27 -03:00
static int tea5767_get_frequency ( struct dvb_frontend * fe , u32 * frequency )
{
struct tea5767_priv * priv = fe - > tuner_priv ;
* frequency = priv - > frequency ;
return 0 ;
}
static struct dvb_tuner_ops tea5767_tuner_ops = {
. info = {
. name = " tea5767 " , // Philips TEA5767HN FM Radio
} ,
. set_analog_params = set_radio_freq ,
. sleep = tea5767_standby ,
. release = tea5767_release ,
. get_frequency = tea5767_get_frequency ,
. get_status = tea5767_get_status ,
2007-06-06 16:17:17 -03:00
} ;
2007-08-27 21:24:27 -03:00
struct dvb_frontend * tea5767_attach ( struct dvb_frontend * fe ,
struct i2c_adapter * i2c_adap ,
u8 i2c_addr )
2005-06-28 20:45:21 -07:00
{
2007-08-21 01:24:42 -03:00
struct tea5767_priv * priv = NULL ;
priv = kzalloc ( sizeof ( struct tea5767_priv ) , GFP_KERNEL ) ;
if ( priv = = NULL )
2007-08-27 21:24:27 -03:00
return NULL ;
fe - > tuner_priv = priv ;
2007-08-21 01:24:42 -03:00
2007-08-27 21:24:27 -03:00
priv - > i2c_props . addr = i2c_addr ;
priv - > i2c_props . adap = i2c_adap ;
2005-06-28 20:45:21 -07:00
2007-08-27 21:24:27 -03:00
memcpy ( & fe - > ops . tuner_ops , & tea5767_tuner_ops ,
sizeof ( struct dvb_tuner_ops ) ) ;
2005-06-28 20:45:21 -07:00
2007-08-27 21:24:27 -03:00
tuner_info ( " type set to %s \n " , " Philips TEA5767HN FM Radio " ) ;
2005-06-28 20:45:21 -07:00
2007-08-27 21:24:27 -03:00
return fe ;
2005-06-28 20:45:21 -07:00
}
2007-08-27 21:24:27 -03:00
EXPORT_SYMBOL_GPL ( tea5767_attach ) ;
EXPORT_SYMBOL_GPL ( tea5767_autodetection ) ;
MODULE_DESCRIPTION ( " Philips TEA5767 FM tuner driver " ) ;
MODULE_AUTHOR ( " Mauro Carvalho Chehab <mchehab@infradead.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;