X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Ftda9887.c;h=fde576f1101cb3f980c9cb616aa3ae7ee1be48c3;hb=refs%2Fheads%2Fvserver;hp=ddc380e1a878365a6a901fca70517c9bab675e20;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index ddc380e1a..fde576f11 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,61 +7,48 @@ #include #include #include +#include + +#include +#include -#include -#include /* Chips: TDA9885 (PAL, NTSC) TDA9886 (PAL, SECAM, NTSC) TDA9887 (PAL, SECAM, NTSC, FM Radio) - found on: - - Pinnacle PCTV (Jul.2002 Version with MT2032, bttv) - TDA9887 (world), TDA9885 (USA) - Note: OP2 of tda988x must be set to 1, else MT2032 is disabled! - - KNC One TV-Station RDS (saa7134) + Used as part of several tuners */ - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { - 0x86 >>1, - 0x96 >>1, - I2C_CLIENT_END, -}; -static unsigned short normal_i2c_range[] = {I2C_CLIENT_END,I2C_CLIENT_END}; -I2C_CLIENT_INSMOD; - -/* insmod options */ -static int debug = 0; -static char *pal = "b"; -static char *secam = "l"; -MODULE_PARM(debug,"i"); -MODULE_PARM(pal,"s"); -MODULE_PARM(secam,"s"); -MODULE_LICENSE("GPL"); +#define tda9887_info(fmt, arg...) do {\ + printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \ + i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) +#define tda9887_dbg(fmt, arg...) do {\ + if (tuner_debug) \ + printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \ + i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) + /* ---------------------------------------------------------------------- */ -#define dprintk if (debug) printk +#define UNSET (-1U) -struct tda9887 { - struct i2c_client client; - int radio,tvnorm; - int pinnacle_id; +struct tvnorm { + v4l2_std_id std; + char *name; + unsigned char b; + unsigned char c; + unsigned char e; }; -static struct i2c_driver driver; -static struct i2c_client client_template; - /* ---------------------------------------------------------------------- */ // // TDA defines // -//// first reg +//// first reg (b) #define cVideoTrapBypassOFF 0x00 // bit b0 #define cVideoTrapBypassON 0x01 // bit b0 @@ -85,7 +73,7 @@ static struct i2c_client client_template; #define cOutputPort2Inactive 0x80 // bit b7 -//// second reg +//// second reg (c) #define cDeemphasisOFF 0x00 // bit c5 #define cDeemphasisON 0x20 // bit c5 @@ -95,8 +83,10 @@ static struct i2c_client client_template; #define cAudioGain0 0x00 // bit c7 #define cAudioGain6 0x80 // bit c7 +#define cTopMask 0x1f // bit c0:4 +#define cTopDefault 0x10 // bit c0:4 -//// third reg +//// 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 @@ -122,355 +112,497 @@ static struct i2c_client client_template; #define cAgcOutON 0x80 // bit e7 #define cAgcOutOFF 0x00 // bit e7 -static int tda9887_miro(struct tda9887 *t) -{ - int rc; - u8 bData[4] = { 0 }; - u8 bVideoIF = 0; - u8 bAudioIF = 0; - u8 bDeEmphasis = 0; - u8 bDeEmphVal = 0; - u8 bModulation = 0; - u8 bCarrierMode = 0; - u8 bOutPort1 = cOutputPort1Inactive; -#if 0 - u8 bOutPort2 = cOutputPort2Inactive & mbTADState; // store i2c tuner state -#else - u8 bOutPort2 = cOutputPort2Inactive; -#endif - u8 bVideoTrap = cVideoTrapBypassOFF; -#if 1 - u8 bTopAdjust = 0x0e /* -2dB */; -#else - u8 bTopAdjust = 0; -#endif - -#if 0 - if (mParams.fVideoTrap) - bVideoTrap = cVideoTrapBypassON; -#endif - - if (t->radio) { - bVideoTrap = cVideoTrapBypassOFF; - bCarrierMode = cQSS; - bModulation = cFmRadio; - bOutPort1 = cOutputPort1Inactive; - bDeEmphasis = cDeemphasisON; - if (3 == t->pinnacle_id) { - /* ntsc */ - bDeEmphVal = cDeemphasis75; - bAudioIF = cAudioIF_4_5; - bVideoIF = cRadioIF_45_75; - } else { - /* pal */ - bAudioIF = cAudioIF_5_5; - bVideoIF = cRadioIF_38_90; - bDeEmphVal = cDeemphasis50; - } - - } else if (t->tvnorm == VIDEO_MODE_PAL) { - bDeEmphasis = cDeemphasisON; - bDeEmphVal = cDeemphasis50; - bModulation = cNegativeFmTV; - bOutPort1 = cOutputPort1Inactive; - if ((1 == t->pinnacle_id) || (7 == t->pinnacle_id)) { - bCarrierMode = cIntercarrier; - } else { - // stereo boards - bCarrierMode = cQSS; - } - switch (pal[0]) { - case 'b': - case 'g': - case 'h': - bVideoIF = cVideoIF_38_90; - bAudioIF = cAudioIF_5_5; - break; - case 'd': - bVideoIF = cVideoIF_38_00; - bAudioIF = cAudioIF_6_5; - break; - case 'i': - bVideoIF = cVideoIF_38_90; - bAudioIF = cAudioIF_6_0; - break; - case 'm': - case 'n': - bVideoIF = cVideoIF_45_75; - bAudioIF = cAudioIF_4_5; - bDeEmphVal = cDeemphasis75; - if ((5 == t->pinnacle_id) || (6 == t->pinnacle_id)) { - bCarrierMode = cIntercarrier; - } else { - bCarrierMode = cQSS; - } - break; - } - - } else if (t->tvnorm == VIDEO_MODE_SECAM) { - bAudioIF = cAudioIF_6_5; - bDeEmphasis = cDeemphasisON; - bDeEmphVal = cDeemphasis50; - bModulation = cNegativeFmTV; - bCarrierMode = cQSS; - bOutPort1 = cOutputPort1Inactive; - switch (secam[0]) { - case 'd': - bVideoIF = cVideoIF_38_00; - break; - case 'k': - bVideoIF = cVideoIF_38_90; - break; - case 'l': - bVideoIF = cVideoIF_38_90; - bDeEmphasis = cDeemphasisOFF; - bDeEmphVal = cDeemphasis75; - bModulation = cPositiveAmTV; - break; - case 'L' /* L1 */: - bVideoIF = cVideoIF_33_90; - bDeEmphasis = cDeemphasisOFF; - bDeEmphVal = cDeemphasis75; - bModulation = cPositiveAmTV; - break; - } +/* ---------------------------------------------------------------------- */ - } else if (t->tvnorm == VIDEO_MODE_NTSC) { - bVideoIF = cVideoIF_45_75; - bAudioIF = cAudioIF_4_5; - bDeEmphasis = cDeemphasisON; - bDeEmphVal = cDeemphasis75; - bModulation = cNegativeFmTV; - bOutPort1 = cOutputPort1Inactive; - if ((5 == t->pinnacle_id) || (6 == t->pinnacle_id)) { - bCarrierMode = cIntercarrier; - } else { - bCarrierMode = cQSS; - } +static struct tvnorm tvnorms[] = { + { + .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N, + .name = "PAL-BGHN", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_5_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_PAL_I, + .name = "PAL-I", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_0 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_PAL_DK, + .name = "PAL-DK", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc, + .name = "PAL-M/Nc", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis75 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_4_5 | + cVideoIF_45_75 ), + },{ + .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, + .name = "SECAM-BGH", + .b = ( cPositiveAmTV | + cQSS ), + .c = ( cTopDefault), + .e = ( cGating_36 | + cAudioIF_5_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_SECAM_L, + .name = "SECAM-L", + .b = ( cPositiveAmTV | + cQSS ), + .c = ( cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_SECAM_LC, + .name = "SECAM-L'", + .b = ( cOutputPort2Inactive | + cPositiveAmTV | + cQSS ), + .c = ( cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_33_90 ), + },{ + .std = V4L2_STD_SECAM_DK, + .name = "SECAM-DK", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, + .name = "NTSC-M", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis75 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_4_5 | + cVideoIF_45_75 ), + },{ + .std = V4L2_STD_NTSC_M_JP, + .name = "NTSC-M-JP", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopDefault), + .e = ( cGating_36 | + cAudioIF_4_5 | + cVideoIF_58_75 ), } +}; - bData[1] = bVideoTrap | // B0: video trap bypass - cAutoMuteFmInactive | // B1: auto mute - bCarrierMode | // B2: InterCarrier for PAL else QSS - bModulation | // B3 - B4: positive AM TV for SECAM only - cForcedMuteAudioOFF | // B5: forced Audio Mute (off) - bOutPort1 | // B6: Out Port 1 - bOutPort2; // B7: Out Port 2 - bData[2] = bTopAdjust | // C0 - C4: Top Adjust 0 == -16dB 31 == 15dB - bDeEmphasis | // C5: De-emphasis on/off - bDeEmphVal | // C6: De-emphasis 50/75 microsec - cAudioGain0; // C7: normal audio gain - bData[3] = bAudioIF | // E0 - E1: Sound IF - bVideoIF | // E2 - E4: Video IF - cTunerGainNormal | // E5: Tuner gain (normal) - cGating_18 | // E6: Gating (18%) - cAgcOutOFF; // E7: VAGC (off) - - dprintk("tda9885/6/7: 0x%02x 0x%02x 0x%02x [pinnacle_id=%d]\n", - bData[1],bData[2],bData[3],t->pinnacle_id); - if (4 != (rc = i2c_master_send(&t->client,bData,4))) - printk("tda9885/6/7: i2c i/o error: rc == %d (should be 4)\n",rc); - return 0; -} +static struct tvnorm radio_stereo = { + .name = "Radio Stereo", + .b = ( cFmRadio | + cQSS ), + .c = ( cDeemphasisOFF | + cAudioGain6 | + cTopDefault), + .e = ( cTunerGainLow | + cAudioIF_5_5 | + cRadioIF_38_90 ), +}; + +static struct tvnorm radio_mono = { + .name = "Radio Mono", + .b = ( cFmRadio | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis75 | + cTopDefault), + .e = ( cTunerGainLow | + cAudioIF_5_5 | + cRadioIF_38_90 ), +}; /* ---------------------------------------------------------------------- */ -#if 0 -/* just for reference: old knc-one saa7134 stuff */ -static unsigned char buf_pal_bg[] = { 0x00, 0x16, 0x70, 0x49 }; -static unsigned char buf_pal_i[] = { 0x00, 0x16, 0x70, 0x4a }; -static unsigned char buf_pal_dk[] = { 0x00, 0x16, 0x70, 0x4b }; -static unsigned char buf_pal_l[] = { 0x00, 0x06, 0x50, 0x4b }; -static unsigned char buf_fm_stereo[] = { 0x00, 0x0e, 0x0d, 0x77 }; -#endif - -static unsigned char buf_pal_bg[] = { 0x00, 0x96, 0x70, 0x49 }; -static unsigned char buf_pal_i[] = { 0x00, 0x96, 0x70, 0x4a }; -static unsigned char buf_pal_dk[] = { 0x00, 0x96, 0x70, 0x4b }; -static unsigned char buf_pal_l[] = { 0x00, 0x86, 0x50, 0x4b }; -static unsigned char buf_fm_stereo[] = { 0x00, 0x8e, 0x0d, 0x77 }; -static unsigned char buf_ntsc[] = { 0x00, 0x96, 0x70, 0x44 }; -static unsigned char buf_ntsc_jp[] = { 0x00, 0x96, 0x70, 0x40 }; - -static int tda9887_configure(struct tda9887 *t) +static void dump_read_message(struct tuner *t, unsigned char *buf) { - unsigned char *buf = NULL; - int rc; - - if (t->radio) { - dprintk("tda9885/6/7: FM Radio mode\n"); - buf = buf_fm_stereo; - - } else if (t->tvnorm == VIDEO_MODE_PAL) { - dprintk("tda9885/6/7: PAL-%c mode\n",pal[0]); - switch (pal[0]) { - case 'b': - case 'g': - buf = buf_pal_bg; - break; - case 'i': - buf = buf_pal_i; - break; - case 'd': - case 'k': - buf = buf_pal_dk; - break; - case 'l': - buf = buf_pal_l; - break; - } + 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", + }; + tda9887_info("read: 0x%2x\n", buf[0]); + tda9887_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no"); + tda9887_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]); + tda9887_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low"); + tda9887_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out"); + tda9887_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low"); +} - } else if (t->tvnorm == VIDEO_MODE_NTSC) { - dprintk("tda9885/6/7: NTSC mode\n"); - buf = buf_ntsc; +static void dump_write_message(struct tuner *t, unsigned char *buf) +{ + 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", + }; + + tda9887_info("write: byte B 0x%02x\n",buf[1]); + tda9887_info(" B0 video mode : %s\n", + (buf[1] & 0x01) ? "video trap" : "sound trap"); + tda9887_info(" B1 auto mute fm : %s\n", + (buf[1] & 0x02) ? "yes" : "no"); + tda9887_info(" B2 carrier mode : %s\n", + (buf[1] & 0x04) ? "QSS" : "Intercarrier"); + tda9887_info(" B3-4 tv sound/radio : %s\n", + sound[(buf[1] & 0x18) >> 3]); + tda9887_info(" B5 force mute audio: %s\n", + (buf[1] & 0x20) ? "yes" : "no"); + tda9887_info(" B6 output port 1 : %s\n", + (buf[1] & 0x40) ? "high (inactive)" : "low (active)"); + tda9887_info(" B7 output port 2 : %s\n", + (buf[1] & 0x80) ? "high (inactive)" : "low (active)"); + + tda9887_info("write: byte C 0x%02x\n",buf[2]); + tda9887_info(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]); + tda9887_info(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]); + tda9887_info(" C7 audio gain : %s\n", + (buf[2] & 0x80) ? "-6" : "0"); + + tda9887_info("write: byte E 0x%02x\n",buf[3]); + tda9887_info(" E0-1 sound carrier : %s\n", + carrier[(buf[3] & 0x03)]); + tda9887_info(" E6 l pll gating : %s\n", + (buf[3] & 0x40) ? "36" : "13"); + + if (buf[1] & 0x08) { + /* radio */ + tda9887_info(" E2-4 video if : %s\n", + rif[(buf[3] & 0x0c) >> 2]); + tda9887_info(" E7 vif agc output : %s\n", + (buf[3] & 0x80) + ? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio") + : "fm radio carrier afc"); + } else { + /* video */ + tda9887_info(" E2-4 video if : %s\n", + vif[(buf[3] & 0x1c) >> 2]); + tda9887_info(" E5 tuner gain : %s\n", + (buf[3] & 0x80) + ? ((buf[3] & 0x20) ? "external" : "normal") + : ((buf[3] & 0x20) ? "minimum" : "normal")); + tda9887_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"); + } + tda9887_info("--\n"); +} - } else if (t->tvnorm == VIDEO_MODE_SECAM) { - dprintk("tda9885/6/7: SECAM mode\n"); - buf = buf_pal_l; +/* ---------------------------------------------------------------------- */ - } else if (t->tvnorm == 6 /* BTTV hack */) { - dprintk("tda9885/6/7: NTSC-Japan mode\n"); - buf = buf_ntsc_jp; - } +static int tda9887_set_tvnorm(struct tuner *t, char *buf) +{ + struct tvnorm *norm = NULL; + int i; - if (NULL == buf) { - printk("tda9885/6/7 unknown norm=%d\n",t->tvnorm); - return 0; + if (t->mode == V4L2_TUNER_RADIO) { + if (t->audmode == V4L2_TUNER_MODE_MONO) + norm = &radio_mono; + else + norm = &radio_stereo; + } else { + for (i = 0; i < ARRAY_SIZE(tvnorms); i++) { + if (tvnorms[i].std & t->std) { + norm = tvnorms+i; + break; + } + } + } + if (NULL == norm) { + tda9887_dbg("Unsupported tvnorm entry - audio muted\n"); + return -1; } - dprintk("tda9885/6/7: 0x%02x 0x%02x 0x%02x\n", - buf[1],buf[2],buf[3]); - if (4 != (rc = i2c_master_send(&t->client,buf,4))) - printk("tda9885/6/7: i2c i/o error: rc == %d (should be 4)\n",rc); + tda9887_dbg("configure for: %s\n",norm->name); + 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; +static unsigned int adjust = UNSET; + +module_param(port1, int, 0644); +module_param(port2, int, 0644); +module_param(qss, int, 0644); +module_param(adjust, int, 0644); -static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind) +static int tda9887_set_insmod(struct tuner *t, char *buf) { - struct tda9887 *t; - - client_template.adapter = adap; - client_template.addr = addr; - - printk("tda9887: chip found @ 0x%x\n", addr<<1); - - if (NULL == (t = kmalloc(sizeof(*t), GFP_KERNEL))) - return -ENOMEM; - memset(t,0,sizeof(*t)); - t->client = client_template; - t->pinnacle_id = -1; - t->tvnorm=VIDEO_MODE_PAL; - i2c_set_clientdata(&t->client, t); - i2c_attach_client(&t->client); - + 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; + } + + if (adjust >= 0x00 && adjust < 0x20) { + buf[2] &= ~cTopMask; + buf[2] |= adjust; + } return 0; } -static int tda9887_probe(struct i2c_adapter *adap) +static int tda9887_set_config(struct tuner *t, char *buf) { -#ifdef I2C_CLASS_TV_ANALOG - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, tda9887_attach); -#else - switch (adap->id) { - case I2C_ALGO_BIT | I2C_HW_B_BT848: - case I2C_ALGO_BIT | I2C_HW_B_RIVA: - case I2C_ALGO_SAA7134: - return i2c_probe(adap, &addr_data, tda9887_attach); - break; + if (t->tda9887_config & TDA9887_PORT1_ACTIVE) + buf[1] &= ~cOutputPort1Inactive; + if (t->tda9887_config & TDA9887_PORT1_INACTIVE) + buf[1] |= cOutputPort1Inactive; + if (t->tda9887_config & TDA9887_PORT2_ACTIVE) + buf[1] &= ~cOutputPort2Inactive; + if (t->tda9887_config & TDA9887_PORT2_INACTIVE) + buf[1] |= cOutputPort2Inactive; + + if (t->tda9887_config & TDA9887_QSS) + buf[1] |= cQSS; + if (t->tda9887_config & TDA9887_INTERCARRIER) + buf[1] &= ~cQSS; + + if (t->tda9887_config & TDA9887_AUTOMUTE) + buf[1] |= cAutoMuteFmActive; + if (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) { + buf[2] &= ~0x60; + switch (t->tda9887_config & TDA9887_DEEMPHASIS_MASK) { + 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; + } + } + if (t->tda9887_config & TDA9887_TOP_SET) { + buf[2] &= ~cTopMask; + buf[2] |= (t->tda9887_config >> 8) & cTopMask; + } + if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC)) + buf[1] &= ~cQSS; + if (t->tda9887_config & TDA9887_GATING_18) + buf[3] &= ~cGating_36; + + if (t->tda9887_config & TDA9887_GAIN_NORMAL) { + radio_stereo.e &= ~cTunerGainLow; + radio_mono.e &= ~cTunerGainLow; } -#endif + return 0; } -static int tda9887_detach(struct i2c_client *client) +/* ---------------------------------------------------------------------- */ + +static int tda9887_status(struct tuner *t) { - struct tda9887 *t = i2c_get_clientdata(client); + unsigned char buf[1]; + int rc; - i2c_detach_client(client); - kfree(t); + memset(buf,0,sizeof(buf)); + if (1 != (rc = i2c_master_recv(&t->i2c,buf,1))) + tda9887_info("i2c i/o error: rc == %d (should be 1)\n",rc); + dump_read_message(t, buf); return 0; } -static int -tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) +static void tda9887_configure(struct i2c_client *client) { - struct tda9887 *t = i2c_get_clientdata(client); + struct tuner *t = i2c_get_clientdata(client); + int rc; - switch (cmd) { + memset(t->tda9887_data,0,sizeof(t->tda9887_data)); + tda9887_set_tvnorm(t,t->tda9887_data); + + /* 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. + */ + t->tda9887_data[1] |= cOutputPort1Inactive; + t->tda9887_data[1] |= cOutputPort2Inactive; + + tda9887_set_config(t,t->tda9887_data); + tda9887_set_insmod(t,t->tda9887_data); + + if (t->mode == T_STANDBY) { + t->tda9887_data[1] |= cForcedMuteAudioON; + } - /* --- configuration --- */ - case AUDC_SET_RADIO: - t->radio = 1; - if (-1 != t->pinnacle_id) - tda9887_miro(t); - else - tda9887_configure(t); - break; - - case AUDC_CONFIG_PINNACLE: - { - int *i = arg; + tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", + t->tda9887_data[1],t->tda9887_data[2],t->tda9887_data[3]); + if (tuner_debug > 1) + dump_write_message(t, t->tda9887_data); - t->pinnacle_id = *i; - tda9887_miro(t); - break; - } - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOCSCHAN: - { - struct video_channel *vc = arg; + if (4 != (rc = i2c_master_send(&t->i2c,t->tda9887_data,4))) + tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc); - t->radio = 0; - t->tvnorm = vc->norm; - if (-1 != t->pinnacle_id) - tda9887_miro(t); - else - tda9887_configure(t); - break; + if (tuner_debug > 2) { + msleep_interruptible(1000); + tda9887_status(t); } - default: - /* nothing */ - break; - } - return 0; } -/* ----------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "i2c tda9887 driver", - .id = -1, /* FIXME */ - .flags = I2C_DF_NOTIFY, - .attach_adapter = tda9887_probe, - .detach_client = tda9887_detach, - .command = tda9887_command, -}; -static struct i2c_client client_template = +static void tda9887_tuner_status(struct i2c_client *client) { - I2C_DEVNAME("tda9887"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; + struct tuner *t = i2c_get_clientdata(client); + tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", t->tda9887_data[1], t->tda9887_data[2], t->tda9887_data[3]); +} -static int tda9887_init_module(void) +static int tda9887_get_afc(struct i2c_client *client) { - i2c_add_driver(&driver); - return 0; + struct tuner *t = i2c_get_clientdata(client); + static int AFC_BITS_2_kHz[] = { + -12500, -37500, -62500, -97500, + -112500, -137500, -162500, -187500, + 187500, 162500, 137500, 112500, + 97500 , 62500, 37500 , 12500 + }; + int afc=0; + __u8 reg = 0; + + if (1 == i2c_master_recv(&t->i2c,®,1)) + afc = AFC_BITS_2_kHz[(reg>>1)&0x0f]; + + return afc; +} + +static void tda9887_standby(struct i2c_client *client) +{ + tda9887_configure(client); } -static void tda9887_cleanup_module(void) +static void tda9887_set_freq(struct i2c_client *client, unsigned int freq) { - i2c_del_driver(&driver); + tda9887_configure(client); } -module_init(tda9887_init_module); -module_exit(tda9887_cleanup_module); +int tda9887_tuner_init(struct i2c_client *c) +{ + struct tuner *t = i2c_get_clientdata(c); + + strlcpy(c->name, "tda9887", sizeof(c->name)); + + tda9887_info("tda988[5/6/7] found @ 0x%x (%s)\n", t->i2c.addr, + t->i2c.driver->driver.name); + + t->set_tv_freq = tda9887_set_freq; + t->set_radio_freq = tda9887_set_freq; + t->standby = tda9887_standby; + t->tuner_status = tda9887_tuner_status; + t->get_afc = tda9887_get_afc; + + return 0; +} /* * Overrides for Emacs so that we follow Linus's tabbing style.