162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci#include <linux/module.h> 362306a36Sopenharmony_ci#include <linux/kernel.h> 462306a36Sopenharmony_ci#include <linux/i2c.h> 562306a36Sopenharmony_ci#include <linux/types.h> 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/errno.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/videodev2.h> 1062306a36Sopenharmony_ci#include <media/v4l2-common.h> 1162306a36Sopenharmony_ci#include <media/tuner.h> 1262306a36Sopenharmony_ci#include "tuner-i2c.h" 1362306a36Sopenharmony_ci#include "tda9887.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* Chips: 1762306a36Sopenharmony_ci TDA9885 (PAL, NTSC) 1862306a36Sopenharmony_ci TDA9886 (PAL, SECAM, NTSC) 1962306a36Sopenharmony_ci TDA9887 (PAL, SECAM, NTSC, FM Radio) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci Used as part of several tuners 2262306a36Sopenharmony_ci*/ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int debug; 2562306a36Sopenharmony_cimodule_param(debug, int, 0644); 2662306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages"); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic DEFINE_MUTEX(tda9887_list_mutex); 2962306a36Sopenharmony_cistatic LIST_HEAD(hybrid_tuner_instance_list); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct tda9887_priv { 3262306a36Sopenharmony_ci struct tuner_i2c_props i2c_props; 3362306a36Sopenharmony_ci struct list_head hybrid_tuner_instance_list; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci unsigned char data[4]; 3662306a36Sopenharmony_ci unsigned int config; 3762306a36Sopenharmony_ci unsigned int mode; 3862306a36Sopenharmony_ci unsigned int audmode; 3962306a36Sopenharmony_ci v4l2_std_id std; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci bool standby; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define UNSET (-1U) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct tvnorm { 4962306a36Sopenharmony_ci v4l2_std_id std; 5062306a36Sopenharmony_ci char *name; 5162306a36Sopenharmony_ci unsigned char b; 5262306a36Sopenharmony_ci unsigned char c; 5362306a36Sopenharmony_ci unsigned char e; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci// 5962306a36Sopenharmony_ci// TDA defines 6062306a36Sopenharmony_ci// 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci//// first reg (b) 6362306a36Sopenharmony_ci#define cVideoTrapBypassOFF 0x00 // bit b0 6462306a36Sopenharmony_ci#define cVideoTrapBypassON 0x01 // bit b0 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define cAutoMuteFmInactive 0x00 // bit b1 6762306a36Sopenharmony_ci#define cAutoMuteFmActive 0x02 // bit b1 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define cIntercarrier 0x00 // bit b2 7062306a36Sopenharmony_ci#define cQSS 0x04 // bit b2 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define cPositiveAmTV 0x00 // bit b3:4 7362306a36Sopenharmony_ci#define cFmRadio 0x08 // bit b3:4 7462306a36Sopenharmony_ci#define cNegativeFmTV 0x10 // bit b3:4 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define cForcedMuteAudioON 0x20 // bit b5 7862306a36Sopenharmony_ci#define cForcedMuteAudioOFF 0x00 // bit b5 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define cOutputPort1Active 0x00 // bit b6 8162306a36Sopenharmony_ci#define cOutputPort1Inactive 0x40 // bit b6 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define cOutputPort2Active 0x00 // bit b7 8462306a36Sopenharmony_ci#define cOutputPort2Inactive 0x80 // bit b7 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci//// second reg (c) 8862306a36Sopenharmony_ci#define cDeemphasisOFF 0x00 // bit c5 8962306a36Sopenharmony_ci#define cDeemphasisON 0x20 // bit c5 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define cDeemphasis75 0x00 // bit c6 9262306a36Sopenharmony_ci#define cDeemphasis50 0x40 // bit c6 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define cAudioGain0 0x00 // bit c7 9562306a36Sopenharmony_ci#define cAudioGain6 0x80 // bit c7 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define cTopMask 0x1f // bit c0:4 9862306a36Sopenharmony_ci#define cTopDefault 0x10 // bit c0:4 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci//// third reg (e) 10162306a36Sopenharmony_ci#define cAudioIF_4_5 0x00 // bit e0:1 10262306a36Sopenharmony_ci#define cAudioIF_5_5 0x01 // bit e0:1 10362306a36Sopenharmony_ci#define cAudioIF_6_0 0x02 // bit e0:1 10462306a36Sopenharmony_ci#define cAudioIF_6_5 0x03 // bit e0:1 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define cVideoIFMask 0x1c // bit e2:4 10862306a36Sopenharmony_ci/* Video IF selection in TV Mode (bit B3=0) */ 10962306a36Sopenharmony_ci#define cVideoIF_58_75 0x00 // bit e2:4 11062306a36Sopenharmony_ci#define cVideoIF_45_75 0x04 // bit e2:4 11162306a36Sopenharmony_ci#define cVideoIF_38_90 0x08 // bit e2:4 11262306a36Sopenharmony_ci#define cVideoIF_38_00 0x0C // bit e2:4 11362306a36Sopenharmony_ci#define cVideoIF_33_90 0x10 // bit e2:4 11462306a36Sopenharmony_ci#define cVideoIF_33_40 0x14 // bit e2:4 11562306a36Sopenharmony_ci#define cRadioIF_45_75 0x18 // bit e2:4 11662306a36Sopenharmony_ci#define cRadioIF_38_90 0x1C // bit e2:4 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* IF1 selection in Radio Mode (bit B3=1) */ 11962306a36Sopenharmony_ci#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14) 12062306a36Sopenharmony_ci#define cRadioIF_41_30 0x04 // bit e2,4 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* Output of AFC pin in radio mode when bit E7=1 */ 12362306a36Sopenharmony_ci#define cRadioAGC_SIF 0x00 // bit e3 12462306a36Sopenharmony_ci#define cRadioAGC_FM 0x08 // bit e3 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define cTunerGainNormal 0x00 // bit e5 12762306a36Sopenharmony_ci#define cTunerGainLow 0x20 // bit e5 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define cGating_18 0x00 // bit e6 13062306a36Sopenharmony_ci#define cGating_36 0x40 // bit e6 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define cAgcOutON 0x80 // bit e7 13362306a36Sopenharmony_ci#define cAgcOutOFF 0x00 // bit e7 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic struct tvnorm tvnorms[] = { 13862306a36Sopenharmony_ci { 13962306a36Sopenharmony_ci .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N, 14062306a36Sopenharmony_ci .name = "PAL-BGHN", 14162306a36Sopenharmony_ci .b = ( cNegativeFmTV | 14262306a36Sopenharmony_ci cQSS ), 14362306a36Sopenharmony_ci .c = ( cDeemphasisON | 14462306a36Sopenharmony_ci cDeemphasis50 | 14562306a36Sopenharmony_ci cTopDefault), 14662306a36Sopenharmony_ci .e = ( cGating_36 | 14762306a36Sopenharmony_ci cAudioIF_5_5 | 14862306a36Sopenharmony_ci cVideoIF_38_90 ), 14962306a36Sopenharmony_ci },{ 15062306a36Sopenharmony_ci .std = V4L2_STD_PAL_I, 15162306a36Sopenharmony_ci .name = "PAL-I", 15262306a36Sopenharmony_ci .b = ( cNegativeFmTV | 15362306a36Sopenharmony_ci cQSS ), 15462306a36Sopenharmony_ci .c = ( cDeemphasisON | 15562306a36Sopenharmony_ci cDeemphasis50 | 15662306a36Sopenharmony_ci cTopDefault), 15762306a36Sopenharmony_ci .e = ( cGating_36 | 15862306a36Sopenharmony_ci cAudioIF_6_0 | 15962306a36Sopenharmony_ci cVideoIF_38_90 ), 16062306a36Sopenharmony_ci },{ 16162306a36Sopenharmony_ci .std = V4L2_STD_PAL_DK, 16262306a36Sopenharmony_ci .name = "PAL-DK", 16362306a36Sopenharmony_ci .b = ( cNegativeFmTV | 16462306a36Sopenharmony_ci cQSS ), 16562306a36Sopenharmony_ci .c = ( cDeemphasisON | 16662306a36Sopenharmony_ci cDeemphasis50 | 16762306a36Sopenharmony_ci cTopDefault), 16862306a36Sopenharmony_ci .e = ( cGating_36 | 16962306a36Sopenharmony_ci cAudioIF_6_5 | 17062306a36Sopenharmony_ci cVideoIF_38_90 ), 17162306a36Sopenharmony_ci },{ 17262306a36Sopenharmony_ci .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc, 17362306a36Sopenharmony_ci .name = "PAL-M/Nc", 17462306a36Sopenharmony_ci .b = ( cNegativeFmTV | 17562306a36Sopenharmony_ci cQSS ), 17662306a36Sopenharmony_ci .c = ( cDeemphasisON | 17762306a36Sopenharmony_ci cDeemphasis75 | 17862306a36Sopenharmony_ci cTopDefault), 17962306a36Sopenharmony_ci .e = ( cGating_36 | 18062306a36Sopenharmony_ci cAudioIF_4_5 | 18162306a36Sopenharmony_ci cVideoIF_45_75 ), 18262306a36Sopenharmony_ci },{ 18362306a36Sopenharmony_ci .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, 18462306a36Sopenharmony_ci .name = "SECAM-BGH", 18562306a36Sopenharmony_ci .b = ( cNegativeFmTV | 18662306a36Sopenharmony_ci cQSS ), 18762306a36Sopenharmony_ci .c = ( cTopDefault), 18862306a36Sopenharmony_ci .e = ( cAudioIF_5_5 | 18962306a36Sopenharmony_ci cVideoIF_38_90 ), 19062306a36Sopenharmony_ci },{ 19162306a36Sopenharmony_ci .std = V4L2_STD_SECAM_L, 19262306a36Sopenharmony_ci .name = "SECAM-L", 19362306a36Sopenharmony_ci .b = ( cPositiveAmTV | 19462306a36Sopenharmony_ci cQSS ), 19562306a36Sopenharmony_ci .c = ( cTopDefault), 19662306a36Sopenharmony_ci .e = ( cGating_36 | 19762306a36Sopenharmony_ci cAudioIF_6_5 | 19862306a36Sopenharmony_ci cVideoIF_38_90 ), 19962306a36Sopenharmony_ci },{ 20062306a36Sopenharmony_ci .std = V4L2_STD_SECAM_LC, 20162306a36Sopenharmony_ci .name = "SECAM-L'", 20262306a36Sopenharmony_ci .b = ( cOutputPort2Inactive | 20362306a36Sopenharmony_ci cPositiveAmTV | 20462306a36Sopenharmony_ci cQSS ), 20562306a36Sopenharmony_ci .c = ( cTopDefault), 20662306a36Sopenharmony_ci .e = ( cGating_36 | 20762306a36Sopenharmony_ci cAudioIF_6_5 | 20862306a36Sopenharmony_ci cVideoIF_33_90 ), 20962306a36Sopenharmony_ci },{ 21062306a36Sopenharmony_ci .std = V4L2_STD_SECAM_DK, 21162306a36Sopenharmony_ci .name = "SECAM-DK", 21262306a36Sopenharmony_ci .b = ( cNegativeFmTV | 21362306a36Sopenharmony_ci cQSS ), 21462306a36Sopenharmony_ci .c = ( cDeemphasisON | 21562306a36Sopenharmony_ci cDeemphasis50 | 21662306a36Sopenharmony_ci cTopDefault), 21762306a36Sopenharmony_ci .e = ( cGating_36 | 21862306a36Sopenharmony_ci cAudioIF_6_5 | 21962306a36Sopenharmony_ci cVideoIF_38_90 ), 22062306a36Sopenharmony_ci },{ 22162306a36Sopenharmony_ci .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, 22262306a36Sopenharmony_ci .name = "NTSC-M", 22362306a36Sopenharmony_ci .b = ( cNegativeFmTV | 22462306a36Sopenharmony_ci cQSS ), 22562306a36Sopenharmony_ci .c = ( cDeemphasisON | 22662306a36Sopenharmony_ci cDeemphasis75 | 22762306a36Sopenharmony_ci cTopDefault), 22862306a36Sopenharmony_ci .e = ( cGating_36 | 22962306a36Sopenharmony_ci cAudioIF_4_5 | 23062306a36Sopenharmony_ci cVideoIF_45_75 ), 23162306a36Sopenharmony_ci },{ 23262306a36Sopenharmony_ci .std = V4L2_STD_NTSC_M_JP, 23362306a36Sopenharmony_ci .name = "NTSC-M-JP", 23462306a36Sopenharmony_ci .b = ( cNegativeFmTV | 23562306a36Sopenharmony_ci cQSS ), 23662306a36Sopenharmony_ci .c = ( cDeemphasisON | 23762306a36Sopenharmony_ci cDeemphasis50 | 23862306a36Sopenharmony_ci cTopDefault), 23962306a36Sopenharmony_ci .e = ( cGating_36 | 24062306a36Sopenharmony_ci cAudioIF_4_5 | 24162306a36Sopenharmony_ci cVideoIF_58_75 ), 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci}; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic struct tvnorm radio_stereo = { 24662306a36Sopenharmony_ci .name = "Radio Stereo", 24762306a36Sopenharmony_ci .b = ( cFmRadio | 24862306a36Sopenharmony_ci cQSS ), 24962306a36Sopenharmony_ci .c = ( cDeemphasisOFF | 25062306a36Sopenharmony_ci cAudioGain6 | 25162306a36Sopenharmony_ci cTopDefault), 25262306a36Sopenharmony_ci .e = ( cTunerGainLow | 25362306a36Sopenharmony_ci cAudioIF_5_5 | 25462306a36Sopenharmony_ci cRadioIF_38_90 ), 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic struct tvnorm radio_mono = { 25862306a36Sopenharmony_ci .name = "Radio Mono", 25962306a36Sopenharmony_ci .b = ( cFmRadio | 26062306a36Sopenharmony_ci cQSS ), 26162306a36Sopenharmony_ci .c = ( cDeemphasisON | 26262306a36Sopenharmony_ci cDeemphasis75 | 26362306a36Sopenharmony_ci cTopDefault), 26462306a36Sopenharmony_ci .e = ( cTunerGainLow | 26562306a36Sopenharmony_ci cAudioIF_5_5 | 26662306a36Sopenharmony_ci cRadioIF_38_90 ), 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void dump_read_message(struct dvb_frontend *fe, unsigned char *buf) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci static char *afc[16] = { 27662306a36Sopenharmony_ci "- 12.5 kHz", 27762306a36Sopenharmony_ci "- 37.5 kHz", 27862306a36Sopenharmony_ci "- 62.5 kHz", 27962306a36Sopenharmony_ci "- 87.5 kHz", 28062306a36Sopenharmony_ci "-112.5 kHz", 28162306a36Sopenharmony_ci "-137.5 kHz", 28262306a36Sopenharmony_ci "-162.5 kHz", 28362306a36Sopenharmony_ci "-187.5 kHz [min]", 28462306a36Sopenharmony_ci "+187.5 kHz [max]", 28562306a36Sopenharmony_ci "+162.5 kHz", 28662306a36Sopenharmony_ci "+137.5 kHz", 28762306a36Sopenharmony_ci "+112.5 kHz", 28862306a36Sopenharmony_ci "+ 87.5 kHz", 28962306a36Sopenharmony_ci "+ 62.5 kHz", 29062306a36Sopenharmony_ci "+ 37.5 kHz", 29162306a36Sopenharmony_ci "+ 12.5 kHz", 29262306a36Sopenharmony_ci }; 29362306a36Sopenharmony_ci tuner_info("read: 0x%2x\n", buf[0]); 29462306a36Sopenharmony_ci tuner_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no"); 29562306a36Sopenharmony_ci tuner_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]); 29662306a36Sopenharmony_ci tuner_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low"); 29762306a36Sopenharmony_ci tuner_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out"); 29862306a36Sopenharmony_ci tuner_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low"); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic void dump_write_message(struct dvb_frontend *fe, unsigned char *buf) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci static char *sound[4] = { 30662306a36Sopenharmony_ci "AM/TV", 30762306a36Sopenharmony_ci "FM/radio", 30862306a36Sopenharmony_ci "FM/TV", 30962306a36Sopenharmony_ci "FM/radio" 31062306a36Sopenharmony_ci }; 31162306a36Sopenharmony_ci static char *adjust[32] = { 31262306a36Sopenharmony_ci "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", 31362306a36Sopenharmony_ci "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", 31462306a36Sopenharmony_ci "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", 31562306a36Sopenharmony_ci "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15" 31662306a36Sopenharmony_ci }; 31762306a36Sopenharmony_ci static char *deemph[4] = { 31862306a36Sopenharmony_ci "no", "no", "75", "50" 31962306a36Sopenharmony_ci }; 32062306a36Sopenharmony_ci static char *carrier[4] = { 32162306a36Sopenharmony_ci "4.5 MHz", 32262306a36Sopenharmony_ci "5.5 MHz", 32362306a36Sopenharmony_ci "6.0 MHz", 32462306a36Sopenharmony_ci "6.5 MHz / AM" 32562306a36Sopenharmony_ci }; 32662306a36Sopenharmony_ci static char *vif[8] = { 32762306a36Sopenharmony_ci "58.75 MHz", 32862306a36Sopenharmony_ci "45.75 MHz", 32962306a36Sopenharmony_ci "38.9 MHz", 33062306a36Sopenharmony_ci "38.0 MHz", 33162306a36Sopenharmony_ci "33.9 MHz", 33262306a36Sopenharmony_ci "33.4 MHz", 33362306a36Sopenharmony_ci "45.75 MHz + pin13", 33462306a36Sopenharmony_ci "38.9 MHz + pin13", 33562306a36Sopenharmony_ci }; 33662306a36Sopenharmony_ci static char *rif[4] = { 33762306a36Sopenharmony_ci "44 MHz", 33862306a36Sopenharmony_ci "52 MHz", 33962306a36Sopenharmony_ci "52 MHz", 34062306a36Sopenharmony_ci "44 MHz", 34162306a36Sopenharmony_ci }; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci tuner_info("write: byte B 0x%02x\n", buf[1]); 34462306a36Sopenharmony_ci tuner_info(" B0 video mode : %s\n", 34562306a36Sopenharmony_ci (buf[1] & 0x01) ? "video trap" : "sound trap"); 34662306a36Sopenharmony_ci tuner_info(" B1 auto mute fm : %s\n", 34762306a36Sopenharmony_ci (buf[1] & 0x02) ? "yes" : "no"); 34862306a36Sopenharmony_ci tuner_info(" B2 carrier mode : %s\n", 34962306a36Sopenharmony_ci (buf[1] & 0x04) ? "QSS" : "Intercarrier"); 35062306a36Sopenharmony_ci tuner_info(" B3-4 tv sound/radio : %s\n", 35162306a36Sopenharmony_ci sound[(buf[1] & 0x18) >> 3]); 35262306a36Sopenharmony_ci tuner_info(" B5 force mute audio: %s\n", 35362306a36Sopenharmony_ci (buf[1] & 0x20) ? "yes" : "no"); 35462306a36Sopenharmony_ci tuner_info(" B6 output port 1 : %s\n", 35562306a36Sopenharmony_ci (buf[1] & 0x40) ? "high (inactive)" : "low (active)"); 35662306a36Sopenharmony_ci tuner_info(" B7 output port 2 : %s\n", 35762306a36Sopenharmony_ci (buf[1] & 0x80) ? "high (inactive)" : "low (active)"); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci tuner_info("write: byte C 0x%02x\n", buf[2]); 36062306a36Sopenharmony_ci tuner_info(" C0-4 top adjustment : %s dB\n", 36162306a36Sopenharmony_ci adjust[buf[2] & 0x1f]); 36262306a36Sopenharmony_ci tuner_info(" C5-6 de-emphasis : %s\n", 36362306a36Sopenharmony_ci deemph[(buf[2] & 0x60) >> 5]); 36462306a36Sopenharmony_ci tuner_info(" C7 audio gain : %s\n", 36562306a36Sopenharmony_ci (buf[2] & 0x80) ? "-6" : "0"); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci tuner_info("write: byte E 0x%02x\n", buf[3]); 36862306a36Sopenharmony_ci tuner_info(" E0-1 sound carrier : %s\n", 36962306a36Sopenharmony_ci carrier[(buf[3] & 0x03)]); 37062306a36Sopenharmony_ci tuner_info(" E6 l pll gating : %s\n", 37162306a36Sopenharmony_ci (buf[3] & 0x40) ? "36" : "13"); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (buf[1] & 0x08) { 37462306a36Sopenharmony_ci /* radio */ 37562306a36Sopenharmony_ci tuner_info(" E2-4 video if : %s\n", 37662306a36Sopenharmony_ci rif[(buf[3] & 0x0c) >> 2]); 37762306a36Sopenharmony_ci tuner_info(" E7 vif agc output : %s\n", 37862306a36Sopenharmony_ci (buf[3] & 0x80) 37962306a36Sopenharmony_ci ? ((buf[3] & 0x10) ? "fm-agc radio" : 38062306a36Sopenharmony_ci "sif-agc radio") 38162306a36Sopenharmony_ci : "fm radio carrier afc"); 38262306a36Sopenharmony_ci } else { 38362306a36Sopenharmony_ci /* video */ 38462306a36Sopenharmony_ci tuner_info(" E2-4 video if : %s\n", 38562306a36Sopenharmony_ci vif[(buf[3] & 0x1c) >> 2]); 38662306a36Sopenharmony_ci tuner_info(" E5 tuner gain : %s\n", 38762306a36Sopenharmony_ci (buf[3] & 0x80) 38862306a36Sopenharmony_ci ? ((buf[3] & 0x20) ? "external" : "normal") 38962306a36Sopenharmony_ci : ((buf[3] & 0x20) ? "minimum" : "normal")); 39062306a36Sopenharmony_ci tuner_info(" E7 vif agc output : %s\n", 39162306a36Sopenharmony_ci (buf[3] & 0x80) ? ((buf[3] & 0x20) 39262306a36Sopenharmony_ci ? "pin3 port, pin22 vif agc out" 39362306a36Sopenharmony_ci : "pin22 port, pin3 vif acg ext in") 39462306a36Sopenharmony_ci : "pin3+pin22 port"); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci tuner_info("--\n"); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int tda9887_set_tvnorm(struct dvb_frontend *fe) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 40462306a36Sopenharmony_ci struct tvnorm *norm = NULL; 40562306a36Sopenharmony_ci char *buf = priv->data; 40662306a36Sopenharmony_ci int i; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (priv->mode == V4L2_TUNER_RADIO) { 40962306a36Sopenharmony_ci if (priv->audmode == V4L2_TUNER_MODE_MONO) 41062306a36Sopenharmony_ci norm = &radio_mono; 41162306a36Sopenharmony_ci else 41262306a36Sopenharmony_ci norm = &radio_stereo; 41362306a36Sopenharmony_ci } else { 41462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tvnorms); i++) { 41562306a36Sopenharmony_ci if (tvnorms[i].std & priv->std) { 41662306a36Sopenharmony_ci norm = tvnorms+i; 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci if (NULL == norm) { 42262306a36Sopenharmony_ci tuner_dbg("Unsupported tvnorm entry - audio muted\n"); 42362306a36Sopenharmony_ci return -1; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci tuner_dbg("configure for: %s\n", norm->name); 42762306a36Sopenharmony_ci buf[1] = norm->b; 42862306a36Sopenharmony_ci buf[2] = norm->c; 42962306a36Sopenharmony_ci buf[3] = norm->e; 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic unsigned int port1 = UNSET; 43462306a36Sopenharmony_cistatic unsigned int port2 = UNSET; 43562306a36Sopenharmony_cistatic unsigned int qss = UNSET; 43662306a36Sopenharmony_cistatic unsigned int adjust = UNSET; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cimodule_param(port1, int, 0644); 43962306a36Sopenharmony_cimodule_param(port2, int, 0644); 44062306a36Sopenharmony_cimodule_param(qss, int, 0644); 44162306a36Sopenharmony_cimodule_param(adjust, int, 0644); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int tda9887_set_insmod(struct dvb_frontend *fe) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 44662306a36Sopenharmony_ci char *buf = priv->data; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (UNSET != port1) { 44962306a36Sopenharmony_ci if (port1) 45062306a36Sopenharmony_ci buf[1] |= cOutputPort1Inactive; 45162306a36Sopenharmony_ci else 45262306a36Sopenharmony_ci buf[1] &= ~cOutputPort1Inactive; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci if (UNSET != port2) { 45562306a36Sopenharmony_ci if (port2) 45662306a36Sopenharmony_ci buf[1] |= cOutputPort2Inactive; 45762306a36Sopenharmony_ci else 45862306a36Sopenharmony_ci buf[1] &= ~cOutputPort2Inactive; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (UNSET != qss) { 46262306a36Sopenharmony_ci if (qss) 46362306a36Sopenharmony_ci buf[1] |= cQSS; 46462306a36Sopenharmony_ci else 46562306a36Sopenharmony_ci buf[1] &= ~cQSS; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (adjust < 0x20) { 46962306a36Sopenharmony_ci buf[2] &= ~cTopMask; 47062306a36Sopenharmony_ci buf[2] |= adjust; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int tda9887_do_config(struct dvb_frontend *fe) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 47862306a36Sopenharmony_ci char *buf = priv->data; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (priv->config & TDA9887_PORT1_ACTIVE) 48162306a36Sopenharmony_ci buf[1] &= ~cOutputPort1Inactive; 48262306a36Sopenharmony_ci if (priv->config & TDA9887_PORT1_INACTIVE) 48362306a36Sopenharmony_ci buf[1] |= cOutputPort1Inactive; 48462306a36Sopenharmony_ci if (priv->config & TDA9887_PORT2_ACTIVE) 48562306a36Sopenharmony_ci buf[1] &= ~cOutputPort2Inactive; 48662306a36Sopenharmony_ci if (priv->config & TDA9887_PORT2_INACTIVE) 48762306a36Sopenharmony_ci buf[1] |= cOutputPort2Inactive; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (priv->config & TDA9887_QSS) 49062306a36Sopenharmony_ci buf[1] |= cQSS; 49162306a36Sopenharmony_ci if (priv->config & TDA9887_INTERCARRIER) 49262306a36Sopenharmony_ci buf[1] &= ~cQSS; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (priv->config & TDA9887_AUTOMUTE) 49562306a36Sopenharmony_ci buf[1] |= cAutoMuteFmActive; 49662306a36Sopenharmony_ci if (priv->config & TDA9887_DEEMPHASIS_MASK) { 49762306a36Sopenharmony_ci buf[2] &= ~0x60; 49862306a36Sopenharmony_ci switch (priv->config & TDA9887_DEEMPHASIS_MASK) { 49962306a36Sopenharmony_ci case TDA9887_DEEMPHASIS_NONE: 50062306a36Sopenharmony_ci buf[2] |= cDeemphasisOFF; 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case TDA9887_DEEMPHASIS_50: 50362306a36Sopenharmony_ci buf[2] |= cDeemphasisON | cDeemphasis50; 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci case TDA9887_DEEMPHASIS_75: 50662306a36Sopenharmony_ci buf[2] |= cDeemphasisON | cDeemphasis75; 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci if (priv->config & TDA9887_TOP_SET) { 51162306a36Sopenharmony_ci buf[2] &= ~cTopMask; 51262306a36Sopenharmony_ci buf[2] |= (priv->config >> 8) & cTopMask; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci if ((priv->config & TDA9887_INTERCARRIER_NTSC) && 51562306a36Sopenharmony_ci (priv->std & V4L2_STD_NTSC)) 51662306a36Sopenharmony_ci buf[1] &= ~cQSS; 51762306a36Sopenharmony_ci if (priv->config & TDA9887_GATING_18) 51862306a36Sopenharmony_ci buf[3] &= ~cGating_36; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (priv->mode == V4L2_TUNER_RADIO) { 52162306a36Sopenharmony_ci if (priv->config & TDA9887_RIF_41_3) { 52262306a36Sopenharmony_ci buf[3] &= ~cVideoIFMask; 52362306a36Sopenharmony_ci buf[3] |= cRadioIF_41_30; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci if (priv->config & TDA9887_GAIN_NORMAL) 52662306a36Sopenharmony_ci buf[3] &= ~cTunerGainLow; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int tda9887_status(struct dvb_frontend *fe) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 53762306a36Sopenharmony_ci unsigned char buf[1]; 53862306a36Sopenharmony_ci int rc; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1); 54162306a36Sopenharmony_ci if (rc != 1) 54262306a36Sopenharmony_ci tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc); 54362306a36Sopenharmony_ci dump_read_message(fe, buf); 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic void tda9887_configure(struct dvb_frontend *fe) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 55062306a36Sopenharmony_ci int rc; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci memset(priv->data,0,sizeof(priv->data)); 55362306a36Sopenharmony_ci tda9887_set_tvnorm(fe); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* A note on the port settings: 55662306a36Sopenharmony_ci These settings tend to depend on the specifics of the board. 55762306a36Sopenharmony_ci By default they are set to inactive (bit value 1) by this driver, 55862306a36Sopenharmony_ci overwriting any changes made by the tvnorm. This means that it 55962306a36Sopenharmony_ci is the responsibility of the module using the tda9887 to set 56062306a36Sopenharmony_ci these values in case of changes in the tvnorm. 56162306a36Sopenharmony_ci In many cases port 2 should be made active (0) when selecting 56262306a36Sopenharmony_ci SECAM-L, and port 2 should remain inactive (1) for SECAM-L'. 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci For the other standards the tda9887 application note says that 56562306a36Sopenharmony_ci the ports should be set to active (0), but, again, that may 56662306a36Sopenharmony_ci differ depending on the precise hardware configuration. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci priv->data[1] |= cOutputPort1Inactive; 56962306a36Sopenharmony_ci priv->data[1] |= cOutputPort2Inactive; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci tda9887_do_config(fe); 57262306a36Sopenharmony_ci tda9887_set_insmod(fe); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (priv->standby) 57562306a36Sopenharmony_ci priv->data[1] |= cForcedMuteAudioON; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", 57862306a36Sopenharmony_ci priv->data[1], priv->data[2], priv->data[3]); 57962306a36Sopenharmony_ci if (debug > 1) 58062306a36Sopenharmony_ci dump_write_message(fe, priv->data); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4))) 58362306a36Sopenharmony_ci tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (debug > 2) { 58662306a36Sopenharmony_ci msleep_interruptible(1000); 58762306a36Sopenharmony_ci tda9887_status(fe); 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic void tda9887_tuner_status(struct dvb_frontend *fe) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 59662306a36Sopenharmony_ci tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", 59762306a36Sopenharmony_ci priv->data[1], priv->data[2], priv->data[3]); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int tda9887_get_afc(struct dvb_frontend *fe, s32 *afc) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 60362306a36Sopenharmony_ci static const int AFC_BITS_2_kHz[] = { 60462306a36Sopenharmony_ci -12500, -37500, -62500, -97500, 60562306a36Sopenharmony_ci -112500, -137500, -162500, -187500, 60662306a36Sopenharmony_ci 187500, 162500, 137500, 112500, 60762306a36Sopenharmony_ci 97500 , 62500, 37500 , 12500 60862306a36Sopenharmony_ci }; 60962306a36Sopenharmony_ci __u8 reg = 0; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (priv->mode != V4L2_TUNER_RADIO) 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci if (1 == tuner_i2c_xfer_recv(&priv->i2c_props, ®, 1)) 61462306a36Sopenharmony_ci *afc = AFC_BITS_2_kHz[(reg >> 1) & 0x0f]; 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic void tda9887_standby(struct dvb_frontend *fe) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci priv->standby = true; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci tda9887_configure(fe); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic void tda9887_set_params(struct dvb_frontend *fe, 62862306a36Sopenharmony_ci struct analog_parameters *params) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci priv->standby = false; 63362306a36Sopenharmony_ci priv->mode = params->mode; 63462306a36Sopenharmony_ci priv->audmode = params->audmode; 63562306a36Sopenharmony_ci priv->std = params->std; 63662306a36Sopenharmony_ci tda9887_configure(fe); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci priv->config = *(unsigned int *)priv_cfg; 64462306a36Sopenharmony_ci tda9887_configure(fe); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci return 0; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void tda9887_release(struct dvb_frontend *fe) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct tda9887_priv *priv = fe->analog_demod_priv; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci mutex_lock(&tda9887_list_mutex); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (priv) 65662306a36Sopenharmony_ci hybrid_tuner_release_state(priv); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci mutex_unlock(&tda9887_list_mutex); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci fe->analog_demod_priv = NULL; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic const struct analog_demod_ops tda9887_ops = { 66462306a36Sopenharmony_ci .info = { 66562306a36Sopenharmony_ci .name = "tda9887", 66662306a36Sopenharmony_ci }, 66762306a36Sopenharmony_ci .set_params = tda9887_set_params, 66862306a36Sopenharmony_ci .standby = tda9887_standby, 66962306a36Sopenharmony_ci .tuner_status = tda9887_tuner_status, 67062306a36Sopenharmony_ci .get_afc = tda9887_get_afc, 67162306a36Sopenharmony_ci .release = tda9887_release, 67262306a36Sopenharmony_ci .set_config = tda9887_set_config, 67362306a36Sopenharmony_ci}; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistruct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, 67662306a36Sopenharmony_ci struct i2c_adapter *i2c_adap, 67762306a36Sopenharmony_ci u8 i2c_addr) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct tda9887_priv *priv = NULL; 68062306a36Sopenharmony_ci int instance; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci mutex_lock(&tda9887_list_mutex); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci instance = hybrid_tuner_request_state(struct tda9887_priv, priv, 68562306a36Sopenharmony_ci hybrid_tuner_instance_list, 68662306a36Sopenharmony_ci i2c_adap, i2c_addr, "tda9887"); 68762306a36Sopenharmony_ci switch (instance) { 68862306a36Sopenharmony_ci case 0: 68962306a36Sopenharmony_ci mutex_unlock(&tda9887_list_mutex); 69062306a36Sopenharmony_ci return NULL; 69162306a36Sopenharmony_ci case 1: 69262306a36Sopenharmony_ci fe->analog_demod_priv = priv; 69362306a36Sopenharmony_ci priv->standby = true; 69462306a36Sopenharmony_ci tuner_info("tda988[5/6/7] found\n"); 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci default: 69762306a36Sopenharmony_ci fe->analog_demod_priv = priv; 69862306a36Sopenharmony_ci break; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci mutex_unlock(&tda9887_list_mutex); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci memcpy(&fe->ops.analog_ops, &tda9887_ops, 70462306a36Sopenharmony_ci sizeof(struct analog_demod_ops)); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return fe; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tda9887_attach); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 711