2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/i2c.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/delay.h>
2008-07-25 14:39:54 +04:00
# include <linux/videodev2.h>
2006-01-09 20:32:31 +03:00
# include <media/v4l2-common.h>
2005-04-17 02:20:36 +04:00
# include <media/tuner.h>
2007-12-09 08:26:48 +03:00
# include "tuner-i2c.h"
2007-10-22 03:48:48 +04:00
# include "tda9887.h"
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:28 +03:00
2005-04-17 02:20:36 +04:00
/* Chips:
TDA9885 ( PAL , NTSC )
TDA9886 ( PAL , SECAM , NTSC )
TDA9887 ( PAL , SECAM , NTSC , FM Radio )
2006-06-23 23:13:56 +04:00
Used as part of several tuners
2005-04-17 02:20:36 +04:00
*/
2007-12-17 03:20:21 +03:00
static int debug ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " enable verbose debug messages " ) ;
2008-04-22 21:45:51 +04:00
static DEFINE_MUTEX ( tda9887_list_mutex ) ;
static LIST_HEAD ( hybrid_tuner_instance_list ) ;
2007-05-30 05:54:06 +04:00
struct tda9887_priv {
2007-08-21 08:24:42 +04:00
struct tuner_i2c_props i2c_props ;
2008-04-22 21:45:51 +04:00
struct list_head hybrid_tuner_instance_list ;
2007-08-21 08:24:42 +04:00
2018-01-04 21:08:56 +03:00
unsigned char data [ 4 ] ;
2007-12-17 01:53:32 +03:00
unsigned int config ;
2007-12-17 02:05:00 +03:00
unsigned int mode ;
unsigned int audmode ;
v4l2_std_id std ;
2011-02-14 23:53:12 +03:00
bool standby ;
2007-05-30 05:54:06 +04:00
} ;
2005-04-17 02:20:36 +04:00
/* ---------------------------------------------------------------------- */
# define UNSET (-1U)
struct tvnorm {
v4l2_std_id std ;
char * name ;
unsigned char b ;
unsigned char c ;
unsigned char e ;
} ;
/* ---------------------------------------------------------------------- */
//
// TDA defines
//
//// first reg (b)
# define cVideoTrapBypassOFF 0x00 // bit b0
# define cVideoTrapBypassON 0x01 // bit b0
# define cAutoMuteFmInactive 0x00 // bit b1
# define cAutoMuteFmActive 0x02 // bit b1
# define cIntercarrier 0x00 // bit b2
# define cQSS 0x04 // bit b2
# define cPositiveAmTV 0x00 // bit b3:4
# define cFmRadio 0x08 // bit b3:4
# define cNegativeFmTV 0x10 // bit b3:4
# define cForcedMuteAudioON 0x20 // bit b5
# define cForcedMuteAudioOFF 0x00 // bit b5
# define cOutputPort1Active 0x00 // bit b6
# define cOutputPort1Inactive 0x40 // bit b6
# define cOutputPort2Active 0x00 // bit b7
# define cOutputPort2Inactive 0x80 // bit b7
//// second reg (c)
# define cDeemphasisOFF 0x00 // bit c5
# define cDeemphasisON 0x20 // bit c5
# define cDeemphasis75 0x00 // bit c6
# define cDeemphasis50 0x40 // bit c6
# define cAudioGain0 0x00 // bit c7
# define cAudioGain6 0x80 // bit c7
2006-01-09 20:25:18 +03:00
# define cTopMask 0x1f // bit c0:4
2018-01-04 21:08:56 +03:00
# define cTopDefault 0x10 // bit c0:4
2005-04-17 02:20:36 +04:00
//// third reg (e)
# define cAudioIF_4_5 0x00 // bit e0:1
# define cAudioIF_5_5 0x01 // bit e0:1
# define cAudioIF_6_0 0x02 // bit e0:1
# define cAudioIF_6_5 0x03 // bit e0:1
2007-08-04 01:32:38 +04:00
# define cVideoIFMask 0x1c // bit e2:4
/* Video IF selection in TV Mode (bit B3=0) */
2005-04-17 02:20:36 +04:00
# define cVideoIF_58_75 0x00 // bit e2:4
# define cVideoIF_45_75 0x04 // bit e2:4
# define cVideoIF_38_90 0x08 // bit e2:4
# define cVideoIF_38_00 0x0C // bit e2:4
# define cVideoIF_33_90 0x10 // bit e2:4
# define cVideoIF_33_40 0x14 // bit e2:4
# define cRadioIF_45_75 0x18 // bit e2:4
# define cRadioIF_38_90 0x1C // bit e2:4
2007-08-04 01:32:38 +04:00
/* IF1 selection in Radio Mode (bit B3=1) */
# define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14)
# define cRadioIF_41_30 0x04 // bit e2,4
/* Output of AFC pin in radio mode when bit E7=1 */
# define cRadioAGC_SIF 0x00 // bit e3
# define cRadioAGC_FM 0x08 // bit e3
2005-04-17 02:20:36 +04:00
# define cTunerGainNormal 0x00 // bit e5
# define cTunerGainLow 0x20 // bit e5
# define cGating_18 0x00 // bit e6
# define cGating_36 0x40 // bit e6
# define cAgcOutON 0x80 // bit e7
# define cAgcOutOFF 0x00 // bit e7
/* ---------------------------------------------------------------------- */
static struct tvnorm tvnorms [ ] = {
{
2006-01-09 20:25:18 +03:00
. std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N ,
. name = " PAL-BGHN " ,
2005-04-17 02:20:36 +04:00
. b = ( cNegativeFmTV |
cQSS ) ,
. c = ( cDeemphasisON |
2006-01-09 20:25:18 +03:00
cDeemphasis50 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2006-01-09 20:25:18 +03:00
. e = ( cGating_36 |
cAudioIF_5_5 |
2005-04-17 02:20:36 +04:00
cVideoIF_38_90 ) ,
} , {
. std = V4L2_STD_PAL_I ,
. name = " PAL-I " ,
. b = ( cNegativeFmTV |
cQSS ) ,
. c = ( cDeemphasisON |
2006-01-09 20:25:18 +03:00
cDeemphasis50 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2006-01-09 20:25:18 +03:00
. e = ( cGating_36 |
cAudioIF_6_0 |
2005-04-17 02:20:36 +04:00
cVideoIF_38_90 ) ,
} , {
. std = V4L2_STD_PAL_DK ,
. name = " PAL-DK " ,
. b = ( cNegativeFmTV |
cQSS ) ,
. c = ( cDeemphasisON |
2006-01-09 20:25:18 +03:00
cDeemphasis50 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2006-01-09 20:25:18 +03:00
. e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_38_90 ) ,
2005-04-17 02:20:36 +04:00
} , {
2006-01-09 20:25:18 +03:00
. std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc ,
. name = " PAL-M/Nc " ,
2005-04-17 02:20:36 +04:00
. b = ( cNegativeFmTV |
cQSS ) ,
. c = ( cDeemphasisON |
2006-01-09 20:25:18 +03:00
cDeemphasis75 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2006-01-09 20:25:18 +03:00
. e = ( cGating_36 |
cAudioIF_4_5 |
2005-04-17 02:20:36 +04:00
cVideoIF_45_75 ) ,
2006-01-09 20:25:18 +03:00
} , {
. std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H ,
. name = " SECAM-BGH " ,
2008-10-30 10:53:07 +03:00
. b = ( cNegativeFmTV |
2006-01-09 20:25:18 +03:00
cQSS ) ,
2006-06-25 22:37:29 +04:00
. c = ( cTopDefault ) ,
2008-10-30 10:53:07 +03:00
. e = ( cAudioIF_5_5 |
2006-01-09 20:25:18 +03:00
cVideoIF_38_90 ) ,
2005-04-17 02:20:36 +04:00
} , {
. std = V4L2_STD_SECAM_L ,
. name = " SECAM-L " ,
. b = ( cPositiveAmTV |
cQSS ) ,
2006-06-25 22:37:29 +04:00
. c = ( cTopDefault ) ,
2005-11-09 08:37:05 +03:00
. e = ( cGating_36 |
2005-11-09 08:37:04 +03:00
cAudioIF_6_5 |
2005-04-17 02:20:36 +04:00
cVideoIF_38_90 ) ,
2006-01-09 20:25:00 +03:00
} , {
. std = V4L2_STD_SECAM_LC ,
. name = " SECAM-L' " ,
. b = ( cOutputPort2Inactive |
cPositiveAmTV |
cQSS ) ,
2006-06-25 22:37:29 +04:00
. c = ( cTopDefault ) ,
2006-01-09 20:25:00 +03:00
. e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_33_90 ) ,
2005-04-17 02:20:36 +04:00
} , {
. std = V4L2_STD_SECAM_DK ,
. name = " SECAM-DK " ,
. b = ( cNegativeFmTV |
cQSS ) ,
. c = ( cDeemphasisON |
2006-01-09 20:25:18 +03:00
cDeemphasis50 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2006-01-09 20:25:18 +03:00
. e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_38_90 ) ,
2005-04-17 02:20:36 +04:00
} , {
2006-02-07 11:45:34 +03:00
. std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR ,
2005-04-17 02:20:36 +04:00
. name = " NTSC-M " ,
. b = ( cNegativeFmTV |
cQSS ) ,
. c = ( cDeemphasisON |
2006-01-09 20:25:18 +03:00
cDeemphasis75 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2005-04-17 02:20:36 +04:00
. e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_45_75 ) ,
} , {
. std = V4L2_STD_NTSC_M_JP ,
2006-01-09 20:25:18 +03:00
. name = " NTSC-M-JP " ,
2005-04-17 02:20:36 +04:00
. b = ( cNegativeFmTV |
cQSS ) ,
. c = ( cDeemphasisON |
2006-01-09 20:25:18 +03:00
cDeemphasis50 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2005-04-17 02:20:36 +04:00
. e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_58_75 ) ,
}
} ;
2005-06-24 09:05:07 +04:00
static struct tvnorm radio_stereo = {
. name = " Radio Stereo " ,
. b = ( cFmRadio |
cQSS ) ,
. c = ( cDeemphasisOFF |
2006-01-09 20:25:18 +03:00
cAudioGain6 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2006-01-09 20:25:18 +03:00
. e = ( cTunerGainLow |
cAudioIF_5_5 |
2005-06-24 09:05:07 +04:00
cRadioIF_38_90 ) ,
} ;
static struct tvnorm radio_mono = {
. name = " Radio Mono " ,
2005-04-17 02:20:36 +04:00
. b = ( cFmRadio |
cQSS ) ,
. c = ( cDeemphasisON |
2006-01-09 20:25:18 +03:00
cDeemphasis75 |
2006-06-25 22:37:29 +04:00
cTopDefault ) ,
2006-01-09 20:25:18 +03:00
. e = ( cTunerGainLow |
cAudioIF_5_5 |
2005-04-17 02:20:36 +04:00
cRadioIF_38_90 ) ,
} ;
/* ---------------------------------------------------------------------- */
2007-10-22 02:39:50 +04:00
static void dump_read_message ( struct dvb_frontend * fe , unsigned char * buf )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2005-04-17 02:20:36 +04:00
static char * afc [ 16 ] = {
" - 12.5 kHz " ,
" - 37.5 kHz " ,
" - 62.5 kHz " ,
" - 87.5 kHz " ,
" -112.5 kHz " ,
" -137.5 kHz " ,
" -162.5 kHz " ,
" -187.5 kHz [min] " ,
" +187.5 kHz [max] " ,
" +162.5 kHz " ,
" +137.5 kHz " ,
" +112.5 kHz " ,
" + 87.5 kHz " ,
" + 62.5 kHz " ,
" + 37.5 kHz " ,
" + 12.5 kHz " ,
} ;
2007-12-17 03:20:21 +03:00
tuner_info ( " read: 0x%2x \n " , buf [ 0 ] ) ;
tuner_info ( " after power on : %s \n " , ( buf [ 0 ] & 0x01 ) ? " yes " : " no " ) ;
tuner_info ( " afc : %s \n " , afc [ ( buf [ 0 ] > > 1 ) & 0x0f ] ) ;
tuner_info ( " fmif level : %s \n " , ( buf [ 0 ] & 0x20 ) ? " high " : " low " ) ;
tuner_info ( " afc window : %s \n " , ( buf [ 0 ] & 0x40 ) ? " in " : " out " ) ;
tuner_info ( " vfi level : %s \n " , ( buf [ 0 ] & 0x80 ) ? " high " : " low " ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-22 02:39:50 +04:00
static void dump_write_message ( struct dvb_frontend * fe , unsigned char * buf )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2005-04-17 02:20:36 +04:00
static char * sound [ 4 ] = {
" AM/TV " ,
" FM/radio " ,
" FM/TV " ,
" FM/radio "
} ;
static char * adjust [ 32 ] = {
" -16 " , " -15 " , " -14 " , " -13 " , " -12 " , " -11 " , " -10 " , " -9 " ,
" -8 " , " -7 " , " -6 " , " -5 " , " -4 " , " -3 " , " -2 " , " -1 " ,
" 0 " , " +1 " , " +2 " , " +3 " , " +4 " , " +5 " , " +6 " , " +7 " ,
" +8 " , " +9 " , " +10 " , " +11 " , " +12 " , " +13 " , " +14 " , " +15 "
} ;
static char * deemph [ 4 ] = {
" no " , " no " , " 75 " , " 50 "
} ;
static char * carrier [ 4 ] = {
" 4.5 MHz " ,
" 5.5 MHz " ,
" 6.0 MHz " ,
" 6.5 MHz / AM "
} ;
static char * vif [ 8 ] = {
" 58.75 MHz " ,
" 45.75 MHz " ,
" 38.9 MHz " ,
" 38.0 MHz " ,
" 33.9 MHz " ,
" 33.4 MHz " ,
" 45.75 MHz + pin13 " ,
" 38.9 MHz + pin13 " ,
} ;
static char * rif [ 4 ] = {
" 44 MHz " ,
" 52 MHz " ,
" 52 MHz " ,
" 44 MHz " ,
} ;
2007-12-17 03:20:21 +03:00
tuner_info ( " write: byte B 0x%02x \n " , buf [ 1 ] ) ;
tuner_info ( " B0 video mode : %s \n " ,
( buf [ 1 ] & 0x01 ) ? " video trap " : " sound trap " ) ;
tuner_info ( " B1 auto mute fm : %s \n " ,
( buf [ 1 ] & 0x02 ) ? " yes " : " no " ) ;
tuner_info ( " B2 carrier mode : %s \n " ,
( buf [ 1 ] & 0x04 ) ? " QSS " : " Intercarrier " ) ;
tuner_info ( " B3-4 tv sound/radio : %s \n " ,
sound [ ( buf [ 1 ] & 0x18 ) > > 3 ] ) ;
tuner_info ( " B5 force mute audio: %s \n " ,
( buf [ 1 ] & 0x20 ) ? " yes " : " no " ) ;
tuner_info ( " B6 output port 1 : %s \n " ,
( buf [ 1 ] & 0x40 ) ? " high (inactive) " : " low (active) " ) ;
tuner_info ( " B7 output port 2 : %s \n " ,
( buf [ 1 ] & 0x80 ) ? " high (inactive) " : " low (active) " ) ;
tuner_info ( " write: byte C 0x%02x \n " , buf [ 2 ] ) ;
tuner_info ( " C0-4 top adjustment : %s dB \n " ,
adjust [ buf [ 2 ] & 0x1f ] ) ;
tuner_info ( " C5-6 de-emphasis : %s \n " ,
deemph [ ( buf [ 2 ] & 0x60 ) > > 5 ] ) ;
tuner_info ( " C7 audio gain : %s \n " ,
( buf [ 2 ] & 0x80 ) ? " -6 " : " 0 " ) ;
tuner_info ( " write: byte E 0x%02x \n " , buf [ 3 ] ) ;
tuner_info ( " E0-1 sound carrier : %s \n " ,
carrier [ ( buf [ 3 ] & 0x03 ) ] ) ;
tuner_info ( " E6 l pll gating : %s \n " ,
( buf [ 3 ] & 0x40 ) ? " 36 " : " 13 " ) ;
2005-04-17 02:20:36 +04:00
if ( buf [ 1 ] & 0x08 ) {
/* radio */
2007-12-17 03:20:21 +03:00
tuner_info ( " E2-4 video if : %s \n " ,
rif [ ( buf [ 3 ] & 0x0c ) > > 2 ] ) ;
tuner_info ( " E7 vif agc output : %s \n " ,
( buf [ 3 ] & 0x80 )
? ( ( buf [ 3 ] & 0x10 ) ? " fm-agc radio " :
" sif-agc radio " )
: " fm radio carrier afc " ) ;
2005-04-17 02:20:36 +04:00
} else {
/* video */
2007-12-17 03:20:21 +03:00
tuner_info ( " E2-4 video if : %s \n " ,
vif [ ( buf [ 3 ] & 0x1c ) > > 2 ] ) ;
tuner_info ( " E5 tuner gain : %s \n " ,
( buf [ 3 ] & 0x80 )
? ( ( buf [ 3 ] & 0x20 ) ? " external " : " normal " )
: ( ( buf [ 3 ] & 0x20 ) ? " minimum " : " normal " ) ) ;
tuner_info ( " E7 vif agc output : %s \n " ,
( buf [ 3 ] & 0x80 ) ? ( ( buf [ 3 ] & 0x20 )
? " pin3 port, pin22 vif agc out "
: " pin22 port, pin3 vif acg ext in " )
: " pin3+pin22 port " ) ;
2005-04-17 02:20:36 +04:00
}
2007-12-17 03:20:21 +03:00
tuner_info ( " -- \n " ) ;
2005-04-17 02:20:36 +04:00
}
/* ---------------------------------------------------------------------- */
2007-10-22 02:39:50 +04:00
static int tda9887_set_tvnorm ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2005-04-17 02:20:36 +04:00
struct tvnorm * norm = NULL ;
2007-10-22 02:39:50 +04:00
char * buf = priv - > data ;
2005-04-17 02:20:36 +04:00
int i ;
2007-12-17 02:05:00 +03:00
if ( priv - > mode = = V4L2_TUNER_RADIO ) {
if ( priv - > audmode = = V4L2_TUNER_MODE_MONO )
2005-06-24 09:05:07 +04:00
norm = & radio_mono ;
else
2005-06-29 07:45:21 +04:00
norm = & radio_stereo ;
2005-04-17 02:20:36 +04:00
} else {
for ( i = 0 ; i < ARRAY_SIZE ( tvnorms ) ; i + + ) {
2007-12-17 02:05:00 +03:00
if ( tvnorms [ i ] . std & priv - > std ) {
2005-04-17 02:20:36 +04:00
norm = tvnorms + i ;
break ;
}
}
}
if ( NULL = = norm ) {
2007-12-17 03:20:21 +03:00
tuner_dbg ( " Unsupported tvnorm entry - audio muted \n " ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2007-12-17 03:20:21 +03:00
tuner_dbg ( " configure for: %s \n " , norm - > name ) ;
2005-04-17 02:20:36 +04:00
buf [ 1 ] = norm - > b ;
buf [ 2 ] = norm - > c ;
buf [ 3 ] = norm - > e ;
return 0 ;
}
static unsigned int port1 = UNSET ;
static unsigned int port2 = UNSET ;
static unsigned int qss = UNSET ;
2006-01-09 20:25:18 +03:00
static unsigned int adjust = UNSET ;
2005-04-17 02:20:36 +04:00
module_param ( port1 , int , 0644 ) ;
module_param ( port2 , int , 0644 ) ;
module_param ( qss , int , 0644 ) ;
module_param ( adjust , int , 0644 ) ;
2007-10-22 02:39:50 +04:00
static int tda9887_set_insmod ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
char * buf = priv - > data ;
2005-04-17 02:20:36 +04:00
if ( UNSET ! = port1 ) {
if ( port1 )
buf [ 1 ] | = cOutputPort1Inactive ;
else
buf [ 1 ] & = ~ cOutputPort1Inactive ;
}
if ( UNSET ! = port2 ) {
if ( port2 )
buf [ 1 ] | = cOutputPort2Inactive ;
else
buf [ 1 ] & = ~ cOutputPort2Inactive ;
}
if ( UNSET ! = qss ) {
if ( qss )
buf [ 1 ] | = cQSS ;
else
buf [ 1 ] & = ~ cQSS ;
}
2009-10-23 14:59:42 +04:00
if ( adjust < 0x20 ) {
2006-01-09 20:25:18 +03:00
buf [ 2 ] & = ~ cTopMask ;
2005-04-17 02:20:36 +04:00
buf [ 2 ] | = adjust ;
2006-01-09 20:25:18 +03:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-12-17 01:53:32 +03:00
static int tda9887_do_config ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
char * buf = priv - > data ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_PORT1_ACTIVE )
2005-04-17 02:20:36 +04:00
buf [ 1 ] & = ~ cOutputPort1Inactive ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_PORT1_INACTIVE )
2005-04-17 02:20:36 +04:00
buf [ 1 ] | = cOutputPort1Inactive ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_PORT2_ACTIVE )
2005-04-17 02:20:36 +04:00
buf [ 1 ] & = ~ cOutputPort2Inactive ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_PORT2_INACTIVE )
2005-04-17 02:20:36 +04:00
buf [ 1 ] | = cOutputPort2Inactive ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_QSS )
2005-04-17 02:20:36 +04:00
buf [ 1 ] | = cQSS ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_INTERCARRIER )
2005-04-17 02:20:36 +04:00
buf [ 1 ] & = ~ cQSS ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_AUTOMUTE )
2005-04-17 02:20:36 +04:00
buf [ 1 ] | = cAutoMuteFmActive ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_DEEMPHASIS_MASK ) {
2005-04-17 02:20:36 +04:00
buf [ 2 ] & = ~ 0x60 ;
2007-12-17 01:53:32 +03:00
switch ( priv - > config & TDA9887_DEEMPHASIS_MASK ) {
2005-04-17 02:20:36 +04:00
case TDA9887_DEEMPHASIS_NONE :
buf [ 2 ] | = cDeemphasisOFF ;
break ;
case TDA9887_DEEMPHASIS_50 :
buf [ 2 ] | = cDeemphasisON | cDeemphasis50 ;
break ;
case TDA9887_DEEMPHASIS_75 :
buf [ 2 ] | = cDeemphasisON | cDeemphasis75 ;
break ;
}
}
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_TOP_SET ) {
2006-01-09 20:25:18 +03:00
buf [ 2 ] & = ~ cTopMask ;
2007-12-17 01:53:32 +03:00
buf [ 2 ] | = ( priv - > config > > 8 ) & cTopMask ;
2006-01-09 20:25:18 +03:00
}
2007-12-17 01:53:32 +03:00
if ( ( priv - > config & TDA9887_INTERCARRIER_NTSC ) & &
2007-12-17 02:05:00 +03:00
( priv - > std & V4L2_STD_NTSC ) )
2005-11-09 08:37:39 +03:00
buf [ 1 ] & = ~ cQSS ;
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_GATING_18 )
2006-08-25 05:43:45 +04:00
buf [ 3 ] & = ~ cGating_36 ;
2006-12-04 14:31:35 +03:00
2007-12-17 02:05:00 +03:00
if ( priv - > mode = = V4L2_TUNER_RADIO ) {
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_RIF_41_3 ) {
2007-08-04 01:32:38 +04:00
buf [ 3 ] & = ~ cVideoIFMask ;
buf [ 3 ] | = cRadioIF_41_30 ;
}
2007-12-17 01:53:32 +03:00
if ( priv - > config & TDA9887_GAIN_NORMAL )
2007-08-04 01:32:38 +04:00
buf [ 3 ] & = ~ cTunerGainLow ;
2006-12-04 14:31:35 +03:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* ---------------------------------------------------------------------- */
2007-10-22 02:39:50 +04:00
static int tda9887_status ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2005-04-17 02:20:36 +04:00
unsigned char buf [ 1 ] ;
int rc ;
2013-11-01 19:40:40 +04:00
rc = tuner_i2c_xfer_recv ( & priv - > i2c_props , buf , 1 ) ;
if ( rc ! = 1 )
2007-12-17 03:20:21 +03:00
tuner_info ( " i2c i/o error: rc == %d (should be 1) \n " , rc ) ;
2007-10-22 02:39:50 +04:00
dump_read_message ( fe , buf ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-10-22 02:39:50 +04:00
static void tda9887_configure ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2005-04-17 02:20:36 +04:00
int rc ;
2007-05-30 05:54:06 +04:00
memset ( priv - > data , 0 , sizeof ( priv - > data ) ) ;
2007-10-22 02:39:50 +04:00
tda9887_set_tvnorm ( fe ) ;
2005-06-24 09:05:07 +04:00
2006-01-09 20:25:18 +03:00
/* A note on the port settings:
These settings tend to depend on the specifics of the board .
By default they are set to inactive ( bit value 1 ) by this driver ,
overwriting any changes made by the tvnorm . This means that it
is the responsibility of the module using the tda9887 to set
these values in case of changes in the tvnorm .
In many cases port 2 should be made active ( 0 ) when selecting
SECAM - L , and port 2 should remain inactive ( 1 ) for SECAM - L ' .
For the other standards the tda9887 application note says that
the ports should be set to active ( 0 ) , but , again , that may
differ depending on the precise hardware configuration .
*/
2007-05-30 05:54:06 +04:00
priv - > data [ 1 ] | = cOutputPort1Inactive ;
priv - > data [ 1 ] | = cOutputPort2Inactive ;
2005-06-24 09:05:07 +04:00
2007-12-17 01:53:32 +03:00
tda9887_do_config ( fe ) ;
2007-10-22 02:39:50 +04:00
tda9887_set_insmod ( fe ) ;
2005-04-17 02:20:36 +04:00
2011-02-14 23:53:12 +03:00
if ( priv - > standby )
2007-05-30 05:54:06 +04:00
priv - > data [ 1 ] | = cForcedMuteAudioON ;
2005-09-10 00:03:37 +04:00
2007-12-17 03:20:21 +03:00
tuner_dbg ( " writing: b=0x%02x c=0x%02x e=0x%02x \n " ,
priv - > data [ 1 ] , priv - > data [ 2 ] , priv - > data [ 3 ] ) ;
if ( debug > 1 )
2007-10-22 02:39:50 +04:00
dump_write_message ( fe , priv - > data ) ;
2005-04-17 02:20:36 +04:00
2007-08-21 08:24:42 +04:00
if ( 4 ! = ( rc = tuner_i2c_xfer_send ( & priv - > i2c_props , priv - > data , 4 ) ) )
2007-12-17 03:20:21 +03:00
tuner_info ( " i2c i/o error: rc == %d (should be 4) \n " , rc ) ;
2005-04-17 02:20:36 +04:00
2007-12-17 03:20:21 +03:00
if ( debug > 2 ) {
2005-04-17 02:20:36 +04:00
msleep_interruptible ( 1000 ) ;
2007-10-22 02:39:50 +04:00
tda9887_status ( fe ) ;
2005-04-17 02:20:36 +04:00
}
}
/* ---------------------------------------------------------------------- */
2007-10-22 02:39:50 +04:00
static void tda9887_tuner_status ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2007-12-17 03:20:21 +03:00
tuner_info ( " Data bytes: b=0x%02x c=0x%02x e=0x%02x \n " ,
priv - > data [ 1 ] , priv - > data [ 2 ] , priv - > data [ 3 ] ) ;
2005-04-17 02:20:36 +04:00
}
2013-04-06 11:35:27 +04:00
static int tda9887_get_afc ( struct dvb_frontend * fe , s32 * afc )
2005-04-17 02:20:36 +04:00
{
2007-10-22 02:39:50 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2013-04-06 11:35:27 +04:00
static const int AFC_BITS_2_kHz [ ] = {
2006-06-23 23:13:56 +04:00
- 12500 , - 37500 , - 62500 , - 97500 ,
- 112500 , - 137500 , - 162500 , - 187500 ,
187500 , 162500 , 137500 , 112500 ,
97500 , 62500 , 37500 , 12500
} ;
__u8 reg = 0 ;
2005-04-17 02:20:36 +04:00
2013-04-06 11:35:27 +04:00
if ( priv - > mode ! = V4L2_TUNER_RADIO )
return 0 ;
if ( 1 = = tuner_i2c_xfer_recv ( & priv - > i2c_props , & reg , 1 ) )
* afc = AFC_BITS_2_kHz [ ( reg > > 1 ) & 0x0f ] ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-10-22 02:39:50 +04:00
static void tda9887_standby ( struct dvb_frontend * fe )
2005-04-17 02:20:36 +04:00
{
2007-12-17 02:05:00 +03:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2011-02-14 23:53:12 +03:00
priv - > standby = true ;
2007-12-17 02:05:00 +03:00
2007-10-22 02:39:50 +04:00
tda9887_configure ( fe ) ;
2005-04-17 02:20:36 +04:00
}
2007-12-08 23:06:30 +03:00
static void tda9887_set_params ( struct dvb_frontend * fe ,
struct analog_parameters * params )
2005-04-17 02:20:36 +04:00
{
2007-12-17 02:05:00 +03:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
2011-02-14 23:53:12 +03:00
priv - > standby = false ;
2007-12-17 02:05:00 +03:00
priv - > mode = params - > mode ;
priv - > audmode = params - > audmode ;
priv - > std = params - > std ;
2007-10-22 02:39:50 +04:00
tda9887_configure ( fe ) ;
2005-04-17 02:20:36 +04:00
}
2007-12-17 01:53:32 +03:00
static int tda9887_set_config ( struct dvb_frontend * fe , void * priv_cfg )
{
struct tda9887_priv * priv = fe - > analog_demod_priv ;
priv - > config = * ( unsigned int * ) priv_cfg ;
tda9887_configure ( fe ) ;
return 0 ;
}
2007-10-22 02:39:50 +04:00
static void tda9887_release ( struct dvb_frontend * fe )
2007-06-04 22:20:11 +04:00
{
2008-04-22 21:45:51 +04:00
struct tda9887_priv * priv = fe - > analog_demod_priv ;
mutex_lock ( & tda9887_list_mutex ) ;
if ( priv )
hybrid_tuner_release_state ( priv ) ;
mutex_unlock ( & tda9887_list_mutex ) ;
2007-10-22 02:39:50 +04:00
fe - > analog_demod_priv = NULL ;
2007-06-04 22:20:11 +04:00
}
2016-08-10 00:32:21 +03:00
static const struct analog_demod_ops tda9887_ops = {
2007-12-09 19:52:51 +03:00
. info = {
2008-01-21 16:55:37 +03:00
. name = " tda9887 " ,
2007-12-09 19:52:51 +03:00
} ,
2007-12-08 23:06:30 +03:00
. set_params = tda9887_set_params ,
2007-06-06 23:15:48 +04:00
. standby = tda9887_standby ,
. tuner_status = tda9887_tuner_status ,
. get_afc = tda9887_get_afc ,
. release = tda9887_release ,
2007-12-17 01:53:32 +03:00
. set_config = tda9887_set_config ,
2007-06-06 23:15:48 +04:00
} ;
2007-12-17 02:11:46 +03:00
struct dvb_frontend * tda9887_attach ( struct dvb_frontend * fe ,
struct i2c_adapter * i2c_adap ,
u8 i2c_addr )
2005-04-17 02:20:36 +04:00
{
2007-05-30 05:54:06 +04:00
struct tda9887_priv * priv = NULL ;
2008-04-22 21:45:51 +04:00
int instance ;
2005-04-17 02:20:36 +04:00
2008-04-22 21:45:51 +04:00
mutex_lock ( & tda9887_list_mutex ) ;
2007-05-30 05:54:06 +04:00
2008-04-22 21:45:51 +04:00
instance = hybrid_tuner_request_state ( struct tda9887_priv , priv ,
hybrid_tuner_instance_list ,
i2c_adap , i2c_addr , " tda9887 " ) ;
switch ( instance ) {
case 0 :
mutex_unlock ( & tda9887_list_mutex ) ;
return NULL ;
case 1 :
fe - > analog_demod_priv = priv ;
2011-02-14 23:53:12 +03:00
priv - > standby = true ;
2008-04-22 21:45:51 +04:00
tuner_info ( " tda988[5/6/7] found \n " ) ;
break ;
default :
fe - > analog_demod_priv = priv ;
break ;
}
2007-08-21 08:24:42 +04:00
2008-04-22 21:45:51 +04:00
mutex_unlock ( & tda9887_list_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-12-21 17:18:32 +03:00
memcpy ( & fe - > ops . analog_ops , & tda9887_ops ,
sizeof ( struct analog_demod_ops ) ) ;
2005-04-17 02:20:36 +04:00
2007-12-17 02:11:46 +03:00
return fe ;
2005-04-17 02:20:36 +04:00
}
2007-10-22 03:48:48 +04:00
EXPORT_SYMBOL_GPL ( tda9887_attach ) ;
2005-04-17 02:20:36 +04:00
2007-10-27 09:17:19 +04:00
MODULE_LICENSE ( " GPL " ) ;