X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Ftda9887.c;h=0d54f6c1982bc9e66adce1d68bc2981b20e09608;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;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..0d54f6c19 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,9 +7,11 @@ #include #include #include +#include + +#include +#include -#include -#include /* Chips: TDA9885 (PAL, NTSC) @@ -20,35 +23,51 @@ TDA9887 (world), TDA9885 (USA) Note: OP2 of tda988x must be set to 1, else MT2032 is disabled! - KNC One TV-Station RDS (saa7134) + - Hauppauge PVR-150/500 (possibly more) */ - + /* Addresses to scan */ static unsigned short normal_i2c[] = { + 0x84 >>1, 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"); +static unsigned int debug = 0; +module_param(debug, int, 0644); MODULE_LICENSE("GPL"); /* ---------------------------------------------------------------------- */ -#define dprintk if (debug) printk +#define UNSET (-1U) +#define tda9887_info(fmt, arg...) do {\ + printk(KERN_INFO "%s %d-%04x: " fmt, t->client.name, \ + i2c_adapter_id(t->client.adapter), t->client.addr , ##arg); } while (0) +#define tda9887_dbg(fmt, arg...) do {\ + if (debug) \ + printk(KERN_INFO "%s %d-%04x: " fmt, t->client.name, \ + i2c_adapter_id(t->client.adapter), t->client.addr , ##arg); } while (0) struct tda9887 { - struct i2c_client client; - int radio,tvnorm; - int pinnacle_id; + struct i2c_client client; + v4l2_std_id std; + enum tuner_mode mode; + unsigned int config; + unsigned int using_v4l2; + unsigned int radio_mode; + unsigned char data[4]; +}; + +struct tvnorm { + v4l2_std_id std; + char *name; + unsigned char b; + unsigned char c; + unsigned char e; }; static struct i2c_driver driver; @@ -60,7 +79,7 @@ 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 +104,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 +114,11 @@ static struct i2c_client client_template; #define cAudioGain0 0x00 // bit c7 #define cAudioGain6 0x80 // bit c7 +#define cTopMask 0x1f // bit c0:4 +#define cTopPalSecamDefault 0x14 // bit c0:4 +#define cTopNtscRadioDefault 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,226 +144,551 @@ static struct i2c_client client_template; #define cAgcOutON 0x80 // bit e7 #define cAgcOutOFF 0x00 // bit e7 -static int tda9887_miro(struct tda9887 *t) +/* ---------------------------------------------------------------------- */ + +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 | + cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_5_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_PAL_I, + .name = "PAL-I", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_6_0 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_PAL_DK, + .name = "PAL-DK", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopPalSecamDefault), + .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 | + cTopNtscRadioDefault), + .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 = ( cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_5_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_SECAM_L, + .name = "SECAM-L", + .b = ( cPositiveAmTV | + cQSS ), + .c = ( cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), + },{ + .std = V4L2_STD_SECAM_LC, + .name = "SECAM-L'", + .b = ( cOutputPort2Inactive | + cPositiveAmTV | + cQSS ), + .c = ( cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_33_90 ), + },{ + .std = V4L2_STD_SECAM_DK, + .name = "SECAM-DK", + .b = ( cNegativeFmTV | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis50 | + cTopPalSecamDefault), + .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 | + cTopNtscRadioDefault), + .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 | + cTopNtscRadioDefault), + .e = ( cGating_36 | + cAudioIF_4_5 | + cVideoIF_58_75 ), + } +}; + +static struct tvnorm radio_stereo = { + .name = "Radio Stereo", + .b = ( cFmRadio | + cQSS ), + .c = ( cDeemphasisOFF | + cAudioGain6 | + cTopNtscRadioDefault), + .e = ( cTunerGainLow | + cAudioIF_5_5 | + cRadioIF_38_90 ), +}; + +static struct tvnorm radio_mono = { + .name = "Radio Mono", + .b = ( cFmRadio | + cQSS ), + .c = ( cDeemphasisON | + cDeemphasis75 | + cTopNtscRadioDefault), + .e = ( cTunerGainLow | + cAudioIF_5_5 | + cRadioIF_38_90 ), +}; + +/* ---------------------------------------------------------------------- */ + +static void dump_read_message(struct tda9887 *t, unsigned char *buf) { - 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; + 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"); +} + +static void dump_write_message(struct tda9887 *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"); +} + +/* ---------------------------------------------------------------------- */ + +static int tda9887_set_tvnorm(struct tda9887 *t, char *buf) +{ + struct tvnorm *norm = NULL; + int i; + + if (t->mode == T_RADIO) { + if (t->radio_mode == 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; + } + + 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_set_insmod(struct tda9887 *t, char *buf) +{ + if (UNSET != port1) { + if (port1) + buf[1] |= cOutputPort1Inactive; + else + buf[1] &= ~cOutputPort1Inactive; + } + if (UNSET != port2) { + if (port2) + buf[1] |= cOutputPort2Inactive; + else + buf[1] &= ~cOutputPort2Inactive; + } - } 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; + 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_set_config(struct tda9887 *t, char *buf) +{ + if (t->config & TDA9887_PORT1_ACTIVE) + buf[1] &= ~cOutputPort1Inactive; + if (t->config & TDA9887_PORT1_INACTIVE) + buf[1] |= cOutputPort1Inactive; + if (t->config & TDA9887_PORT2_ACTIVE) + buf[1] &= ~cOutputPort2Inactive; + if (t->config & TDA9887_PORT2_INACTIVE) + buf[1] |= cOutputPort2Inactive; + + if (t->config & TDA9887_QSS) + buf[1] |= cQSS; + if (t->config & TDA9887_INTERCARRIER) + buf[1] &= ~cQSS; + + if (t->config & TDA9887_AUTOMUTE) + buf[1] |= cAutoMuteFmActive; + if (t->config & TDA9887_DEEMPHASIS_MASK) { + buf[2] &= ~0x60; + switch (t->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->config & TDA9887_TOP_SET) { + buf[2] &= ~cTopMask; + buf[2] |= (t->config >> 8) & cTopMask; + } + if ((t->config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC)) + buf[1] &= ~cQSS; + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static char pal[] = "--"; +static char secam[] = "--"; +static char ntsc[] = "-"; + +module_param_string(pal, pal, sizeof(pal), 0644); +module_param_string(secam, secam, sizeof(secam), 0644); +module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); + +static int tda9887_fixup_std(struct tda9887 *t) +{ + /* get more precise norm info from insmod option */ + if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) { switch (pal[0]) { case 'b': + case 'B': case 'g': + case 'G': case 'h': - bVideoIF = cVideoIF_38_90; - bAudioIF = cAudioIF_5_5; - break; - case 'd': - bVideoIF = cVideoIF_38_00; - bAudioIF = cAudioIF_6_5; + case 'H': + case 'n': + case 'N': + if (pal[1] == 'c' || pal[1] == 'C') { + tda9887_dbg("insmod fixup: PAL => PAL-Nc\n"); + t->std = V4L2_STD_PAL_Nc; + } else { + tda9887_dbg("insmod fixup: PAL => PAL-BGHN\n"); + t->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N; + } break; case 'i': - bVideoIF = cVideoIF_38_90; - bAudioIF = cAudioIF_6_0; + case 'I': + tda9887_dbg("insmod fixup: PAL => PAL-I\n"); + t->std = V4L2_STD_PAL_I; + break; + case 'd': + case 'D': + case 'k': + case 'K': + tda9887_dbg("insmod fixup: PAL => PAL-DK\n"); + t->std = V4L2_STD_PAL_DK; 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; - } + case 'M': + tda9887_dbg("insmod fixup: PAL => PAL-M\n"); + t->std = V4L2_STD_PAL_M; + break; + case '-': + /* default parameter, do nothing */ + break; + default: + tda9887_info("pal= argument not recognised\n"); break; } - - } else if (t->tvnorm == VIDEO_MODE_SECAM) { - bAudioIF = cAudioIF_6_5; - bDeEmphasis = cDeemphasisON; - bDeEmphVal = cDeemphasis50; - bModulation = cNegativeFmTV; - bCarrierMode = cQSS; - bOutPort1 = cOutputPort1Inactive; + } + if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) { switch (secam[0]) { - case 'd': - bVideoIF = cVideoIF_38_00; + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + tda9887_dbg("insmod fixup: SECAM => SECAM-BGH\n"); + t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; break; + case 'd': + case 'D': case 'k': - bVideoIF = cVideoIF_38_90; + case 'K': + tda9887_dbg("insmod fixup: SECAM => SECAM-DK\n"); + t->std = V4L2_STD_SECAM_DK; break; case 'l': - bVideoIF = cVideoIF_38_90; - bDeEmphasis = cDeemphasisOFF; - bDeEmphVal = cDeemphasis75; - bModulation = cPositiveAmTV; + case 'L': + if (secam[1] == 'c' || secam[1] == 'C') { + tda9887_dbg("insmod fixup: SECAM => SECAM-L'\n"); + t->std = V4L2_STD_SECAM_LC; + } else { + tda9887_dbg("insmod fixup: SECAM => SECAM-L\n"); + t->std = V4L2_STD_SECAM_L; + } + break; + case '-': + /* default parameter, do nothing */ break; - case 'L' /* L1 */: - bVideoIF = cVideoIF_33_90; - bDeEmphasis = cDeemphasisOFF; - bDeEmphVal = cDeemphasis75; - bModulation = cPositiveAmTV; + default: + tda9887_info("secam= argument not recognised\n"); + break; + } + } + if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) { + switch (ntsc[0]) { + case 'm': + case 'M': + tda9887_dbg("insmod fixup: NTSC => NTSC-M\n"); + t->std = V4L2_STD_NTSC_M; + break; + case 'j': + case 'J': + tda9887_dbg("insmod fixup: NTSC => NTSC_M_JP\n"); + t->std = V4L2_STD_NTSC_M_JP; + break; + case 'k': + case 'K': + tda9887_dbg("insmod fixup: NTSC => NTSC_M_KR\n"); + t->std = V4L2_STD_NTSC_M_KR; + break; + case '-': + /* default parameter, do nothing */ + break; + default: + tda9887_info("ntsc= argument not recognised\n"); 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; - } } - - 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 int tda9887_status(struct tda9887 *t) +{ + unsigned char buf[1]; + int rc; -#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 }; + memset(buf,0,sizeof(buf)); + if (1 != (rc = i2c_master_recv(&t->client,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_configure(struct tda9887 *t) { - 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; - } - - } else if (t->tvnorm == VIDEO_MODE_NTSC) { - dprintk("tda9885/6/7: NTSC mode\n"); - buf = buf_ntsc; + memset(t->data,0,sizeof(t->data)); + tda9887_set_tvnorm(t,t->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->data[1] |= cOutputPort1Inactive; + t->data[1] |= cOutputPort2Inactive; + + tda9887_set_config(t,t->data); + tda9887_set_insmod(t,t->data); + + if (t->mode == T_STANDBY) { + t->data[1] |= cForcedMuteAudioON; + } - } else if (t->tvnorm == VIDEO_MODE_SECAM) { - dprintk("tda9885/6/7: SECAM mode\n"); - buf = buf_pal_l; + tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", + t->data[1],t->data[2],t->data[3]); + if (debug > 1) + dump_write_message(t, t->data); - } else if (t->tvnorm == 6 /* BTTV hack */) { - dprintk("tda9885/6/7: NTSC-Japan mode\n"); - buf = buf_ntsc_jp; - } + if (4 != (rc = i2c_master_send(&t->client,t->data,4))) + tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc); - if (NULL == buf) { - printk("tda9885/6/7 unknown norm=%d\n",t->tvnorm); - return 0; + if (debug > 2) { + msleep_interruptible(1000); + tda9887_status(t); } - - 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); return 0; } @@ -351,37 +698,28 @@ static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind) { struct tda9887 *t; - client_template.adapter = adap; - client_template.addr = addr; + client_template.adapter = adap; + client_template.addr = addr; + + if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL))) + return -ENOMEM; - printk("tda9887: chip found @ 0x%x\n", addr<<1); + t->client = client_template; + t->std = 0; + t->radio_mode = V4L2_TUNER_MODE_STEREO; + + tda9887_info("chip found @ 0x%x (%s)\n", addr<<1, adap->name); + + i2c_set_clientdata(&t->client, t); + i2c_attach_client(&t->client); - 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); - return 0; } static int tda9887_probe(struct i2c_adapter *adap) { -#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; - } -#endif return 0; } @@ -394,28 +732,39 @@ static int tda9887_detach(struct i2c_client *client) return 0; } +#define SWITCH_V4L2 if (!t->using_v4l2 && debug) \ + tda9887_info("switching to v4l2\n"); \ + t->using_v4l2 = 1; +#define CHECK_V4L2 if (t->using_v4l2) { if (debug) \ + tda9887_info("ignore v4l1 call\n"); \ + return 0; } + static int tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct tda9887 *t = i2c_get_clientdata(client); - switch (cmd) { + switch (cmd) { /* --- configuration --- */ case AUDC_SET_RADIO: - t->radio = 1; - if (-1 != t->pinnacle_id) - tda9887_miro(t); - else - tda9887_configure(t); + { + t->mode = T_RADIO; + tda9887_configure(t); break; - - case AUDC_CONFIG_PINNACLE: + } + case TUNER_SET_STANDBY: + { + t->mode = T_STANDBY; + tda9887_configure(t); + break; + } + case TDA9887_SET_CONFIG: { int *i = arg; - t->pinnacle_id = *i; - tda9887_miro(t); + t->config = *i; + tda9887_configure(t); break; } /* --- v4l ioctls --- */ @@ -423,14 +772,84 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) kernel pointer here... */ case VIDIOCSCHAN: { + static const v4l2_std_id map[] = { + [ VIDEO_MODE_PAL ] = V4L2_STD_PAL, + [ VIDEO_MODE_NTSC ] = V4L2_STD_NTSC_M, + [ VIDEO_MODE_SECAM ] = V4L2_STD_SECAM, + [ 4 /* bttv */ ] = V4L2_STD_PAL_M, + [ 5 /* bttv */ ] = V4L2_STD_PAL_N, + [ 6 /* bttv */ ] = V4L2_STD_NTSC_M_JP, + }; struct video_channel *vc = arg; - t->radio = 0; - t->tvnorm = vc->norm; - if (-1 != t->pinnacle_id) - tda9887_miro(t); - else - tda9887_configure(t); + CHECK_V4L2; + t->mode = T_ANALOG_TV; + if (vc->norm < ARRAY_SIZE(map)) + t->std = map[vc->norm]; + tda9887_fixup_std(t); + tda9887_configure(t); + break; + } + case VIDIOC_S_STD: + { + v4l2_std_id *id = arg; + + SWITCH_V4L2; + t->mode = T_ANALOG_TV; + t->std = *id; + tda9887_fixup_std(t); + tda9887_configure(t); + break; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + SWITCH_V4L2; + if (V4L2_TUNER_ANALOG_TV == f->type) { + if (t->mode == T_ANALOG_TV) + return 0; + t->mode = T_ANALOG_TV; + } + if (V4L2_TUNER_RADIO == f->type) { + if (t->mode == T_RADIO) + return 0; + t->mode = T_RADIO; + } + tda9887_configure(t); + break; + } + case VIDIOC_G_TUNER: + { + static int AFC_BITS_2_kHz[] = { + -12500, -37500, -62500, -97500, + -112500, -137500, -162500, -187500, + 187500, 162500, 137500, 112500, + 97500 , 62500, 37500 , 12500 + }; + struct v4l2_tuner* tuner = arg; + + if (t->mode == T_RADIO) { + __u8 reg = 0; + tuner->afc=0; + if (1 == i2c_master_recv(&t->client,®,1)) + tuner->afc = AFC_BITS_2_kHz[(reg>>1)&0x0f]; + } + break; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner* tuner = arg; + + if (t->mode == T_RADIO) { + t->radio_mode = tuner->audmode; + tda9887_configure (t); + } + break; + } + case VIDIOC_LOG_STATUS: + { + tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", t->data[1], t->data[2], t->data[3]); break; } default: @@ -440,31 +859,50 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } +static int tda9887_suspend(struct device * dev, pm_message_t state) +{ + struct i2c_client *c = container_of(dev, struct i2c_client, dev); + struct tda9887 *t = i2c_get_clientdata(c); + + tda9887_dbg("suspend\n"); + return 0; +} + +static int tda9887_resume(struct device * dev) +{ + struct i2c_client *c = container_of(dev, struct i2c_client, dev); + struct tda9887 *t = i2c_get_clientdata(c); + + tda9887_dbg("resume\n"); + tda9887_configure(t); + 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, + .id = I2C_DRIVERID_TDA9887, + .attach_adapter = tda9887_probe, + .detach_client = tda9887_detach, + .command = tda9887_command, + .driver = { + .name = "tda9887", + .suspend = tda9887_suspend, + .resume = tda9887_resume, + }, }; static struct i2c_client client_template = { - I2C_DEVNAME("tda9887"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + .name = "tda9887", + .driver = &driver, }; -static int tda9887_init_module(void) +static int __init tda9887_init_module(void) { - i2c_add_driver(&driver); - return 0; + return i2c_add_driver(&driver); } -static void tda9887_cleanup_module(void) +static void __exit tda9887_cleanup_module(void) { i2c_del_driver(&driver); }