Commit e6cf179b authored by Gerd Knorr's avatar Gerd Knorr Committed by Linus Torvalds

[PATCH] v4l: tuner + tda9887 updates

This is a big update for the tuner and tda9887 modules which are used for TV
card tuning.

The tda9887 module is basically completely rewritten and understands all the
config bits now instead of having just some fixed config presets.  Some of
these config bits can be changed by insmod options now.

The other big change is that both modules allow to use the V4L2 API for
inter-module communication (i.e.  when bttv/saa7134/...  pass through the
tuning ioctls to the modules).  That allows to specify the TV norm more
precisely (not just PAL but PAL-I, PAL-BG, ...), which is needed in some cases
to make TV audio work correctly.  Using the old v4l1 API is still possible so
this shouldn't break any users of these two modules.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 39871f0b
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
Note: OP2 of tda988x must be set to 1, else MT2032 is disabled! Note: OP2 of tda988x must be set to 1, else MT2032 is disabled!
- KNC One TV-Station RDS (saa7134) - KNC One TV-Station RDS (saa7134)
*/ */
/* Addresses to scan */ /* Addresses to scan */
static unsigned short normal_i2c[] = { static unsigned short normal_i2c[] = {
...@@ -33,22 +33,30 @@ static unsigned short normal_i2c_range[] = {I2C_CLIENT_END,I2C_CLIENT_END}; ...@@ -33,22 +33,30 @@ static unsigned short normal_i2c_range[] = {I2C_CLIENT_END,I2C_CLIENT_END};
I2C_CLIENT_INSMOD; I2C_CLIENT_INSMOD;
/* insmod options */ /* insmod options */
static int debug = 0; static unsigned int debug = 0;
static char *pal = "b";
static char *secam = "l";
MODULE_PARM(debug,"i"); MODULE_PARM(debug,"i");
MODULE_PARM(pal,"s");
MODULE_PARM(secam,"s");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
#define UNSET (-1U)
#define PREFIX "tda9885/6/7: "
#define dprintk if (debug) printk #define dprintk if (debug) printk
struct tda9887 { struct tda9887 {
struct i2c_client client; struct i2c_client client;
int radio,tvnorm; v4l2_std_id std;
int pinnacle_id; unsigned int radio;
unsigned int pinnacle_id;
unsigned int using_v4l2;
};
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_driver driver;
...@@ -60,7 +68,7 @@ static struct i2c_client client_template; ...@@ -60,7 +68,7 @@ static struct i2c_client client_template;
// TDA defines // TDA defines
// //
//// first reg //// first reg (b)
#define cVideoTrapBypassOFF 0x00 // bit b0 #define cVideoTrapBypassOFF 0x00 // bit b0
#define cVideoTrapBypassON 0x01 // bit b0 #define cVideoTrapBypassON 0x01 // bit b0
...@@ -85,7 +93,7 @@ static struct i2c_client client_template; ...@@ -85,7 +93,7 @@ static struct i2c_client client_template;
#define cOutputPort2Inactive 0x80 // bit b7 #define cOutputPort2Inactive 0x80 // bit b7
//// second reg //// second reg (c)
#define cDeemphasisOFF 0x00 // bit c5 #define cDeemphasisOFF 0x00 // bit c5
#define cDeemphasisON 0x20 // bit c5 #define cDeemphasisON 0x20 // bit c5
...@@ -96,7 +104,7 @@ static struct i2c_client client_template; ...@@ -96,7 +104,7 @@ static struct i2c_client client_template;
#define cAudioGain6 0x80 // bit c7 #define cAudioGain6 0x80 // bit c7
//// third reg //// third reg (e)
#define cAudioIF_4_5 0x00 // bit e0:1 #define cAudioIF_4_5 0x00 // bit e0:1
#define cAudioIF_5_5 0x01 // bit e0:1 #define cAudioIF_5_5 0x01 // bit e0:1
#define cAudioIF_6_0 0x02 // bit e0:1 #define cAudioIF_6_0 0x02 // bit e0:1
...@@ -122,126 +130,287 @@ static struct i2c_client client_template; ...@@ -122,126 +130,287 @@ static struct i2c_client client_template;
#define cAgcOutON 0x80 // bit e7 #define cAgcOutON 0x80 // bit e7
#define cAgcOutOFF 0x00 // bit e7 #define cAgcOutOFF 0x00 // bit e7
static int tda9887_miro(struct tda9887 *t) /* ---------------------------------------------------------------------- */
static struct tvnorm tvnorms[] = {
{
.std = V4L2_STD_PAL_BG,
.name = "PAL-BG",
.b = ( cNegativeFmTV |
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 ),
.e = ( cAudioIF_5_5 |
cVideoIF_38_90 ),
},{
.std = V4L2_STD_PAL_I,
.name = "PAL-I",
.b = ( cNegativeFmTV |
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 ),
.e = ( cAudioIF_6_0 |
cVideoIF_38_90 ),
},{
.std = V4L2_STD_PAL_DK,
.name = "PAL-DK",
.b = ( cNegativeFmTV |
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 ),
.e = ( cAudioIF_6_5 |
cVideoIF_38_00 ),
},{
.std = V4L2_STD_PAL_M | V4L2_STD_PAL_N,
.name = "PAL-M/N",
.b = ( cNegativeFmTV |
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis75 ),
.e = ( cAudioIF_4_5 |
cVideoIF_45_75 ),
},{
.std = V4L2_STD_SECAM_L,
.name = "SECAM-L",
.b = ( cPositiveAmTV |
cQSS ),
.e = ( cAudioIF_6_5 |
cVideoIF_38_90 ),
},{
.std = V4L2_STD_SECAM_DK,
.name = "SECAM-DK",
.b = ( cNegativeFmTV |
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 ),
.e = ( cAudioIF_6_5 |
cVideoIF_38_00 ),
},{
.std = V4L2_STD_NTSC_M,
.name = "NTSC-M",
.b = ( cNegativeFmTV |
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 ),
.e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_45_75 ),
},{
.std = V4L2_STD_NTSC_M_JP,
.name = "NTSC-JP",
.b = ( cNegativeFmTV |
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 ),
.e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_58_75 ),
}
};
static struct tvnorm radio = {
.name = "radio",
.b = ( cFmRadio |
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 ),
.e = ( cAudioIF_5_5 |
cRadioIF_38_90 ),
};
/* ---------------------------------------------------------------------- */
static void dump_read_message(unsigned char *buf)
{ {
int rc; static char *afc[16] = {
u8 bData[4] = { 0 }; "- 12.5 kHz",
u8 bVideoIF = 0; "- 37.5 kHz",
u8 bAudioIF = 0; "- 62.5 kHz",
u8 bDeEmphasis = 0; "- 87.5 kHz",
u8 bDeEmphVal = 0; "-112.5 kHz",
u8 bModulation = 0; "-137.5 kHz",
u8 bCarrierMode = 0; "-162.5 kHz",
u8 bOutPort1 = cOutputPort1Inactive; "-187.5 kHz [min]",
#if 0 "+187.5 kHz [max]",
u8 bOutPort2 = cOutputPort2Inactive & mbTADState; // store i2c tuner state "+162.5 kHz",
#else "+137.5 kHz",
u8 bOutPort2 = cOutputPort2Inactive; "+112.5 kHz",
#endif "+ 87.5 kHz",
u8 bVideoTrap = cVideoTrapBypassOFF; "+ 62.5 kHz",
#if 1 "+ 37.5 kHz",
u8 bTopAdjust = 0x0e /* -2dB */; "+ 12.5 kHz",
#else };
u8 bTopAdjust = 0; printk(PREFIX "read: 0x%2x\n", buf[0]);
#endif printk(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
printk(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
printk(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
printk(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
}
#if 0 static void dump_write_message(unsigned char *buf)
if (mParams.fVideoTrap) {
bVideoTrap = cVideoTrapBypassON; static char *sound[4] = {
#endif "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",
};
printk(PREFIX "write: byte B 0x%02x\n",buf[1]);
printk(" B0 video mode : %s\n",
(buf[1] & 0x01) ? "video trap" : "sound trap");
printk(" B1 auto mute fm : %s\n",
(buf[1] & 0x02) ? "yes" : "no");
printk(" B2 carrier mode : %s\n",
(buf[1] & 0x04) ? "QSS" : "Intercarrier");
printk(" B3-4 tv sound/radio : %s\n",
sound[(buf[1] & 0x18) >> 3]);
printk(" B5 force mute audio: %s\n",
(buf[1] & 0x20) ? "yes" : "no");
printk(" B6 output port 1 : %s\n",
(buf[1] & 0x40) ? "high" : "low");
printk(" B7 output port 2 : %s\n",
(buf[1] & 0x80) ? "high" : "low");
printk(PREFIX "write: byte C 0x%02x\n",buf[2]);
printk(" C0-4 top adjustment : %s dB\n", adjust[buf[2] & 0x1f]);
printk(" C5-6 de-emphasis : %s\n", deemph[(buf[2] & 0x60) >> 5]);
printk(" C7 audio gain : %s\n",
(buf[2] & 0x80) ? "-6" : "0");
printk(PREFIX "write: byte E 0x%02x\n",buf[3]);
printk(" E0-1 sound carrier : %s\n",
carrier[(buf[3] & 0x03)]);
printk(" E6 l pll ganting : %s\n",
(buf[3] & 0x40) ? "36" : "13");
if (buf[1] & 0x08) {
/* radio */
printk(" E2-4 video if : %s\n",
rif[(buf[3] & 0x0c) >> 2]);
printk(" E7 vif agc output : %s\n",
(buf[3] & 0x80)
? ((buf[3] & 0x10) ? "fm-agc radio" : "sif-agc radio")
: "fm radio carrier afc");
} else {
/* video */
printk(" E2-4 video if : %s\n",
vif[(buf[3] & 0x1c) >> 2]);
printk(" E5 tuner gain : %s\n",
(buf[3] & 0x80)
? ((buf[3] & 0x20) ? "external" : "normal")
: ((buf[3] & 0x20) ? "minimum" : "normal"));
printk(" 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");
}
printk("--\n");
}
/* ---------------------------------------------------------------------- */
static int tda9887_set_tvnorm(struct tda9887 *t, char *buf)
{
struct tvnorm *norm = NULL;
int i;
if (t->radio) { if (t->radio) {
bVideoTrap = cVideoTrapBypassOFF; norm = &radio;
bCarrierMode = cQSS; } else {
bModulation = cFmRadio; for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
bOutPort1 = cOutputPort1Inactive; if (tvnorms[i].std & t->std) {
bDeEmphasis = cDeemphasisON; norm = tvnorms+i;
if (3 == t->pinnacle_id) { break;
/* ntsc */ }
bDeEmphVal = cDeemphasis75;
bAudioIF = cAudioIF_4_5;
bVideoIF = cRadioIF_45_75;
} else {
/* pal */
bAudioIF = cAudioIF_5_5;
bVideoIF = cRadioIF_38_90;
bDeEmphVal = cDeemphasis50;
} }
}
if (NULL == norm) {
dprintk(PREFIX "Oops: no tvnorm entry found\n");
return -1;
}
dprintk(PREFIX "configure for: %s\n",norm->name);
buf[1] = norm->b;
buf[2] = norm->c;
buf[3] = norm->e;
return 0;
}
static unsigned int port1 = 1;
static unsigned int port2 = 1;
static unsigned int qss = UNSET;
static unsigned int adjust = 0x10;
MODULE_PARM(port1,"i");
MODULE_PARM(port2,"i");
MODULE_PARM(qss,"i");
MODULE_PARM(adjust,"i");
static int tda9887_set_insmod(struct tda9887 *t, char *buf)
{
if (port1)
buf[1] |= cOutputPort1Inactive;
if (port2)
buf[1] |= cOutputPort2Inactive;
if (UNSET != qss) {
if (qss)
buf[1] |= cQSS;
else
buf[1] &= ~cQSS;
}
if (adjust >= 0x00 && adjust < 0x20)
buf[2] |= adjust;
return 0;
}
/* ---------------------------------------------------------------------- */
} else if (t->tvnorm == VIDEO_MODE_PAL) { static int tda9887_set_pinnacle(struct tda9887 *t, char *buf)
bDeEmphasis = cDeemphasisON; {
bDeEmphVal = cDeemphasis50; unsigned int bCarrierMode = UNSET;
bModulation = cNegativeFmTV;
bOutPort1 = cOutputPort1Inactive; if (t->std & V4L2_STD_PAL) {
if ((1 == t->pinnacle_id) || (7 == t->pinnacle_id)) { if ((1 == t->pinnacle_id) || (7 == t->pinnacle_id)) {
bCarrierMode = cIntercarrier; bCarrierMode = cIntercarrier;
} else { } else {
// stereo boards
bCarrierMode = cQSS; bCarrierMode = cQSS;
} }
switch (pal[0]) { }
case 'b': if (t->std & V4L2_STD_NTSC) {
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)) { if ((5 == t->pinnacle_id) || (6 == t->pinnacle_id)) {
bCarrierMode = cIntercarrier; bCarrierMode = cIntercarrier;
} else { } else {
...@@ -249,99 +418,102 @@ static int tda9887_miro(struct tda9887 *t) ...@@ -249,99 +418,102 @@ static int tda9887_miro(struct tda9887 *t)
} }
} }
bData[1] = bVideoTrap | // B0: video trap bypass if (bCarrierMode != UNSET) {
cAutoMuteFmInactive | // B1: auto mute buf[1] &= ~0x04;
bCarrierMode | // B2: InterCarrier for PAL else QSS buf[1] |= bCarrierMode;
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; return 0;
} }
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
#if 0 static char *pal = "-";
/* just for reference: old knc-one saa7134 stuff */ MODULE_PARM(pal,"s");
static unsigned char buf_pal_bg[] = { 0x00, 0x16, 0x70, 0x49 }; static char *secam = "-";
static unsigned char buf_pal_i[] = { 0x00, 0x16, 0x70, 0x4a }; MODULE_PARM(secam,"s");
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 int tda9887_fixup_std(struct tda9887 *t)
{ {
unsigned char *buf = NULL; /* get more precise norm info from insmod option */
int rc; if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
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]) { switch (pal[0]) {
case 'b': case 'b':
case 'B':
case 'g': case 'g':
buf = buf_pal_bg; case 'G':
dprintk(PREFIX "insmod fixup: PAL => PAL-BG\n");
t->std = V4L2_STD_PAL_BG;
break; break;
case 'i': case 'i':
buf = buf_pal_i; case 'I':
dprintk(PREFIX "insmod fixup: PAL => PAL-I\n");
t->std = V4L2_STD_PAL_I;
break;
case 'd':
case 'D':
case 'k':
case 'K':
dprintk(PREFIX "insmod fixup: PAL => PAL-DK\n");
t->std = V4L2_STD_PAL_DK;
break; break;
}
}
if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
switch (secam[0]) {
case 'd': case 'd':
case 'D':
case 'k': case 'k':
buf = buf_pal_dk; case 'K':
dprintk(PREFIX "insmod fixup: SECAM => SECAM-DK\n");
t->std = V4L2_STD_SECAM_DK;
break; break;
case 'l': case 'l':
buf = buf_pal_l; case 'L':
dprintk(PREFIX "insmod fixup: SECAM => SECAM-L\n");
t->std = V4L2_STD_SECAM_L;
break; break;
} }
}
return 0;
}
} else if (t->tvnorm == VIDEO_MODE_NTSC) { static int tda9887_status(struct tda9887 *t)
dprintk("tda9885/6/7: NTSC mode\n"); {
buf = buf_ntsc; unsigned char buf[1];
int rc;
} else if (t->tvnorm == VIDEO_MODE_SECAM) { memset(buf,0,sizeof(buf));
dprintk("tda9885/6/7: SECAM mode\n"); if (1 != (rc = i2c_master_recv(&t->client,buf,1)))
buf = buf_pal_l; printk(PREFIX "i2c i/o error: rc == %d (should be 1)\n",rc);
dump_read_message(buf);
return 0;
}
} else if (t->tvnorm == 6 /* BTTV hack */) { static int tda9887_configure(struct tda9887 *t)
dprintk("tda9885/6/7: NTSC-Japan mode\n"); {
buf = buf_ntsc_jp; unsigned char buf[4];
} int rc;
if (NULL == buf) { memset(buf,0,sizeof(buf));
printk("tda9885/6/7 unknown norm=%d\n",t->tvnorm); tda9887_set_tvnorm(t,buf);
return 0; if (UNSET != t->pinnacle_id) {
tda9887_set_pinnacle(t,buf);
} }
tda9887_set_insmod(t,buf);
dprintk("tda9885/6/7: 0x%02x 0x%02x 0x%02x\n", dprintk(PREFIX "writing: b=0x%02x c=0x%02x e=0x%02x\n",
buf[1],buf[2],buf[3]); buf[1],buf[2],buf[3]);
if (debug > 1)
dump_write_message(buf);
if (4 != (rc = i2c_master_send(&t->client,buf,4))) 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); printk(PREFIX "i2c i/o error: rc == %d (should be 4)\n",rc);
if (debug > 2) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
tda9887_status(t);
}
return 0; return 0;
} }
...@@ -354,14 +526,14 @@ static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind) ...@@ -354,14 +526,14 @@ static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind)
client_template.adapter = adap; client_template.adapter = adap;
client_template.addr = addr; client_template.addr = addr;
printk("tda9887: chip found @ 0x%x\n", addr<<1); printk(PREFIX "chip found @ 0x%x\n", addr<<1);
if (NULL == (t = kmalloc(sizeof(*t), GFP_KERNEL))) if (NULL == (t = kmalloc(sizeof(*t), GFP_KERNEL)))
return -ENOMEM; return -ENOMEM;
memset(t,0,sizeof(*t)); memset(t,0,sizeof(*t));
t->client = client_template; t->client = client_template;
t->pinnacle_id = -1; t->std = 0;;
t->tvnorm=VIDEO_MODE_PAL; t->pinnacle_id = UNSET;
i2c_set_clientdata(&t->client, t); i2c_set_clientdata(&t->client, t);
i2c_attach_client(&t->client); i2c_attach_client(&t->client);
...@@ -394,6 +566,13 @@ static int tda9887_detach(struct i2c_client *client) ...@@ -394,6 +566,13 @@ static int tda9887_detach(struct i2c_client *client)
return 0; return 0;
} }
#define SWITCH_V4L2 if (!t->using_v4l2 && debug) \
printk(PREFIX "switching to v4l2\n"); \
t->using_v4l2 = 1;
#define CHECK_V4L2 if (t->using_v4l2) { if (debug) \
printk(PREFIX "ignore v4l1 call\n"); \
return 0; }
static int static int
tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg)
{ {
...@@ -404,10 +583,7 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -404,10 +583,7 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg)
/* --- configuration --- */ /* --- configuration --- */
case AUDC_SET_RADIO: case AUDC_SET_RADIO:
t->radio = 1; t->radio = 1;
if (-1 != t->pinnacle_id) tda9887_configure(t);
tda9887_miro(t);
else
tda9887_configure(t);
break; break;
case AUDC_CONFIG_PINNACLE: case AUDC_CONFIG_PINNACLE:
...@@ -415,7 +591,7 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -415,7 +591,7 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg)
int *i = arg; int *i = arg;
t->pinnacle_id = *i; t->pinnacle_id = *i;
tda9887_miro(t); tda9887_configure(t);
break; break;
} }
/* --- v4l ioctls --- */ /* --- v4l ioctls --- */
...@@ -423,16 +599,52 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -423,16 +599,52 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg)
kernel pointer here... */ kernel pointer here... */
case VIDIOCSCHAN: 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; struct video_channel *vc = arg;
t->radio = 0; CHECK_V4L2;
t->tvnorm = vc->norm; t->radio = 0;
if (-1 != t->pinnacle_id) if (vc->norm < ARRAY_SIZE(map))
tda9887_miro(t); t->std = map[vc->norm];
else tda9887_fixup_std(t);
tda9887_configure(t); tda9887_configure(t);
break;
}
case VIDIOC_S_STD:
{
v4l2_std_id *id = arg;
SWITCH_V4L2;
t->radio = 0;
t->std = *id;
tda9887_fixup_std(t);
tda9887_configure(t);
break; break;
} }
case VIDIOC_S_FREQUENCY:
{
struct v4l2_frequency *f = arg;
SWITCH_V4L2;
if (V4L2_TUNER_ANALOG_TV == f->type) {
if (t->radio == 0)
return 0;
t->radio = 0;
}
if (V4L2_TUNER_RADIO == f->type) {
if (t->radio == 1)
return 0;
t->radio = 1;
}
tda9887_configure(t);
}
default: default:
/* nothing */ /* nothing */
break; break;
......
...@@ -26,15 +26,17 @@ I2C_CLIENT_INSMOD; ...@@ -26,15 +26,17 @@ I2C_CLIENT_INSMOD;
static unsigned int debug = 0; static unsigned int debug = 0;
static unsigned int type = UNSET; static unsigned int type = UNSET;
static unsigned int addr = 0; static unsigned int addr = 0;
static char *pal = "b";
static unsigned int tv_range[2] = { 44, 958 }; static unsigned int tv_range[2] = { 44, 958 };
static unsigned int radio_range[2] = { 65, 108 }; static unsigned int radio_range[2] = { 65, 108 };
static unsigned int tv_antenna = 1;
static unsigned int radio_antenna = 0;
MODULE_PARM(debug,"i"); MODULE_PARM(debug,"i");
MODULE_PARM(type,"i"); MODULE_PARM(type,"i");
MODULE_PARM(addr,"i"); MODULE_PARM(addr,"i");
MODULE_PARM(tv_range,"2i"); MODULE_PARM(tv_range,"2i");
MODULE_PARM(radio_range,"2i"); MODULE_PARM(radio_range,"2i");
MODULE_PARM(pal,"s"); MODULE_PARM(tv_antenna,"i");
MODULE_PARM(radio_antenna,"i");
#define optimize_vco 1 #define optimize_vco 1
...@@ -48,10 +50,10 @@ static int this_adap; ...@@ -48,10 +50,10 @@ static int this_adap;
struct tuner { struct tuner {
unsigned int type; /* chip type */ unsigned int type; /* chip type */
unsigned int freq; /* keep track of the current settings */ unsigned int freq; /* keep track of the current settings */
unsigned int std; v4l2_std_id std;
int using_v4l2;
unsigned int radio; unsigned int radio;
unsigned int mode; /* current norm for multi-norm tuners */
unsigned int input; unsigned int input;
// only for MT2032 // only for MT2032
...@@ -537,15 +539,16 @@ static void mt2032_set_tv_freq(struct i2c_client *c, unsigned int freq) ...@@ -537,15 +539,16 @@ static void mt2032_set_tv_freq(struct i2c_client *c, unsigned int freq)
int if2,from,to; int if2,from,to;
// signal bandwidth and picture carrier // signal bandwidth and picture carrier
if (t->mode == VIDEO_MODE_NTSC) { if (t->std & V4L2_STD_525_60) {
from=40750*1000; // NTSC
to=46750*1000; from = 40750*1000;
if2=45750*1000; to = 46750*1000;
if2 = 45750*1000;
} else { } else {
// Pal // PAL
from=32900*1000; from = 32900*1000;
to=39900*1000; to = 39900*1000;
if2=38900*1000; if2 = 38900*1000;
} }
mt2032_set_if_freq(c, freq*62500 /* freq*1000*1000/16 */, mt2032_set_if_freq(c, freq*62500 /* freq*1000*1000/16 */,
...@@ -619,6 +622,17 @@ static int mt2032_init(struct i2c_client *c) ...@@ -619,6 +622,17 @@ static int mt2032_init(struct i2c_client *c)
return(1); return(1);
} }
static void mt2050_set_antenna(struct i2c_client *c, unsigned char antenna)
{
unsigned char buf[2];
int ret;
buf[0] = 6;
buf[1] = antenna ? 0x11 : 0x10;
ret=i2c_master_send(c,buf,2);
dprintk("mt2050: enabled antenna connector %d\n", antenna);
}
static void mt2050_set_if_freq(struct i2c_client *c,unsigned int freq, unsigned int if2) static void mt2050_set_if_freq(struct i2c_client *c,unsigned int freq, unsigned int if2)
{ {
unsigned int if1=1218*1000*1000; unsigned int if1=1218*1000*1000;
...@@ -683,13 +697,15 @@ static void mt2050_set_tv_freq(struct i2c_client *c, unsigned int freq) ...@@ -683,13 +697,15 @@ static void mt2050_set_tv_freq(struct i2c_client *c, unsigned int freq)
struct tuner *t = i2c_get_clientdata(c); struct tuner *t = i2c_get_clientdata(c);
unsigned int if2; unsigned int if2;
if (t->mode == VIDEO_MODE_NTSC) { if (t->std & V4L2_STD_525_60) {
if2=45750*1000; // NTSC
if2 = 45750*1000;
} else { } else {
// Pal // PAL
if2=38900*1000; if2 = 38900*1000;
} }
mt2050_set_if_freq(c,freq*62500,if2); mt2050_set_if_freq(c, freq*62500, if2);
mt2050_set_antenna(c, tv_antenna);
} }
static void mt2050_set_radio_freq(struct i2c_client *c, unsigned int freq) static void mt2050_set_radio_freq(struct i2c_client *c, unsigned int freq)
...@@ -698,6 +714,7 @@ static void mt2050_set_radio_freq(struct i2c_client *c, unsigned int freq) ...@@ -698,6 +714,7 @@ static void mt2050_set_radio_freq(struct i2c_client *c, unsigned int freq)
int if2 = t->radio_if2; int if2 = t->radio_if2;
mt2050_set_if_freq(c, freq*62500, if2); mt2050_set_if_freq(c, freq*62500, if2);
mt2050_set_antenna(c, radio_antenna);
} }
static int mt2050_init(struct i2c_client *c) static int mt2050_init(struct i2c_client *c)
...@@ -731,8 +748,8 @@ static int microtune_init(struct i2c_client *c) ...@@ -731,8 +748,8 @@ static int microtune_init(struct i2c_client *c)
unsigned char buf[21]; unsigned char buf[21];
int company_code; int company_code;
buf[0] = 0; memset(buf,0,sizeof(buf));
t->tv_freq = NULL; t->tv_freq = NULL;
t->radio_freq = NULL; t->radio_freq = NULL;
name = "unknown"; name = "unknown";
...@@ -751,6 +768,9 @@ static int microtune_init(struct i2c_client *c) ...@@ -751,6 +768,9 @@ static int microtune_init(struct i2c_client *c)
company_code = buf[0x11] << 8 | buf[0x12]; company_code = buf[0x11] << 8 | buf[0x12];
printk("tuner: microtune: companycode=%04x part=%02x rev=%02x\n", printk("tuner: microtune: companycode=%04x part=%02x rev=%02x\n",
company_code,buf[0x13],buf[0x14]); company_code,buf[0x13],buf[0x14]);
#if 0
/* seems to cause more problems than it solves ... */
switch (company_code) { switch (company_code) {
case 0x30bf: case 0x30bf:
case 0x3cbf: case 0x3cbf:
...@@ -764,6 +784,7 @@ static int microtune_init(struct i2c_client *c) ...@@ -764,6 +784,7 @@ static int microtune_init(struct i2c_client *c)
printk("tuner: microtune: unknown companycode\n"); printk("tuner: microtune: unknown companycode\n");
return 0; return 0;
} }
#endif
if (buf[0x13] < ARRAY_SIZE(microtune_part) && if (buf[0x13] < ARRAY_SIZE(microtune_part) &&
NULL != microtune_part[buf[0x13]]) NULL != microtune_part[buf[0x13]])
...@@ -811,54 +832,40 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) ...@@ -811,54 +832,40 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq)
/* 0x02 -> PAL BDGHI / SECAM L */ /* 0x02 -> PAL BDGHI / SECAM L */
/* 0x04 -> ??? PAL others / SECAM others ??? */ /* 0x04 -> ??? PAL others / SECAM others ??? */
config &= ~0x02; config &= ~0x02;
if (t->mode == VIDEO_MODE_SECAM) if (t->std & V4L2_STD_SECAM)
config |= 0x02; config |= 0x02;
break; break;
case TUNER_TEMIC_4046FM5: case TUNER_TEMIC_4046FM5:
config &= ~0x0f; config &= ~0x0f;
switch (pal[0]) {
case 'i': if (t->std & V4L2_STD_PAL_BG) {
case 'I': config |= TEMIC_SET_PAL_BG;
} else if (t->std & V4L2_STD_PAL_I) {
config |= TEMIC_SET_PAL_I; config |= TEMIC_SET_PAL_I;
break;
case 'd': } else if (t->std & V4L2_STD_PAL_DK) {
case 'D':
config |= TEMIC_SET_PAL_DK; config |= TEMIC_SET_PAL_DK;
break;
case 'l': } else if (t->std & V4L2_STD_SECAM_L) {
case 'L':
config |= TEMIC_SET_PAL_L; config |= TEMIC_SET_PAL_L;
break;
case 'b':
case 'B':
case 'g':
case 'G':
default:
config |= TEMIC_SET_PAL_BG;
break;
} }
break; break;
case TUNER_PHILIPS_FQ1216ME: case TUNER_PHILIPS_FQ1216ME:
config &= ~0x0f; config &= ~0x0f;
switch (pal[0]) {
case 'i': if (t->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) {
case 'I': config |= PHILIPS_SET_PAL_BGDK;
} else if (t->std & V4L2_STD_PAL_I) {
config |= PHILIPS_SET_PAL_I; config |= PHILIPS_SET_PAL_I;
break;
case 'l': } else if (t->std & V4L2_STD_SECAM_L) {
case 'L':
config |= PHILIPS_SET_PAL_L; config |= PHILIPS_SET_PAL_L;
break;
case 'd':
case 'D':
case 'b':
case 'B':
case 'g':
case 'G':
config |= PHILIPS_SET_PAL_BGDK;
break;
} }
break; break;
...@@ -867,12 +874,9 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) ...@@ -867,12 +874,9 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq)
/* 0x01 -> ATSC antenna input 2 */ /* 0x01 -> ATSC antenna input 2 */
/* 0x02 -> NTSC antenna input 1 */ /* 0x02 -> NTSC antenna input 1 */
/* 0x03 -> NTSC antenna input 2 */ /* 0x03 -> NTSC antenna input 2 */
config &= ~0x03; config &= ~0x03;
#ifdef VIDEO_MODE_ATSC if (t->std & V4L2_STD_ATSC)
if (VIDEO_MODE_ATSC != t->mode)
config |= 2; config |= 2;
#endif
/* FIXME: input */ /* FIXME: input */
break; break;
} }
...@@ -990,6 +994,22 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) ...@@ -990,6 +994,22 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
t->radio_freq(c,freq); t->radio_freq(c,freq);
} }
static void set_freq(struct i2c_client *c, unsigned long freq)
{
struct tuner *t = i2c_get_clientdata(c);
if (t->radio) {
dprintk("tuner: radio freq set to %lu.%02lu\n",
freq/16,freq%16*100/16);
set_radio_freq(c,freq);
} else {
dprintk("tuner: tv freq set to %lu.%02lu\n",
freq/16,freq%16*100/16);
set_tv_freq(c, freq);
}
t->freq = freq;
}
static void set_type(struct i2c_client *c, unsigned int type, char *source) static void set_type(struct i2c_client *c, unsigned int type, char *source)
{ {
struct tuner *t = i2c_get_clientdata(c); struct tuner *t = i2c_get_clientdata(c);
...@@ -1019,6 +1039,38 @@ static void set_type(struct i2c_client *c, unsigned int type, char *source) ...@@ -1019,6 +1039,38 @@ static void set_type(struct i2c_client *c, unsigned int type, char *source)
} }
} }
static char *pal = "-";
MODULE_PARM(pal,"s");
static int tuner_fixup_std(struct tuner *t)
{
if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
/* get more precise norm info from insmod option */
switch (pal[0]) {
case 'b':
case 'B':
case 'g':
case 'G':
dprintk("insmod fixup: PAL => PAL-BG\n");
t->std = V4L2_STD_PAL_BG;
break;
case 'i':
case 'I':
dprintk("insmod fixup: PAL => PAL-I\n");
t->std = V4L2_STD_PAL_I;
break;
case 'd':
case 'D':
case 'k':
case 'K':
dprintk("insmod fixup: PAL => PAL-DK\n");
t->std = V4L2_STD_PAL_DK;
break;
}
}
return 0;
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
...@@ -1054,7 +1106,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) ...@@ -1054,7 +1106,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
set_type(client, type, "insmod option"); set_type(client, type, "insmod option");
printk("tuner: The type=<n> insmod option will go away soon.\n"); printk("tuner: The type=<n> insmod option will go away soon.\n");
printk("tuner: Please use the tuner=<n> option provided by\n"); printk("tuner: Please use the tuner=<n> option provided by\n");
printk("tuner: tv card core driver (bttv, saa7134, ...) instead.\n"); printk("tuner: tv aard core driver (bttv, saa7134, ...) instead.\n");
} }
return 0; return 0;
} }
...@@ -1094,6 +1146,13 @@ static int tuner_detach(struct i2c_client *client) ...@@ -1094,6 +1146,13 @@ static int tuner_detach(struct i2c_client *client)
return 0; return 0;
} }
#define SWITCH_V4L2 if (!t->using_v4l2 && debug) \
printk("tuner: switching to v4l2\n"); \
t->using_v4l2 = 1;
#define CHECK_V4L2 if (t->using_v4l2) { if (debug) \
printk("tuner: ignore v4l1 call\n"); \
return 0; }
static int static int
tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
{ {
...@@ -1130,10 +1189,21 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -1130,10 +1189,21 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
kernel pointer here... */ kernel pointer here... */
case VIDIOCSCHAN: 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; struct video_channel *vc = arg;
CHECK_V4L2;
t->radio = 0; t->radio = 0;
t->mode = vc->norm; if (vc->norm < ARRAY_SIZE(map))
t->std = map[vc->norm];
tuner_fixup_std(t);
if (t->freq) if (t->freq)
set_tv_freq(client,t->freq); set_tv_freq(client,t->freq);
return 0; return 0;
...@@ -1142,22 +1212,15 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -1142,22 +1212,15 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
{ {
unsigned long *v = arg; unsigned long *v = arg;
if (t->radio) { CHECK_V4L2;
dprintk("tuner: radio freq set to %lu.%02lu\n", set_freq(client,*v);
(*v)/16,(*v)%16*100/16);
set_radio_freq(client,*v);
} else {
dprintk("tuner: tv freq set to %lu.%02lu\n",
(*v)/16,(*v)%16*100/16);
set_tv_freq(client,*v);
}
t->freq = *v;
return 0; return 0;
} }
case VIDIOCGTUNER: case VIDIOCGTUNER:
{ {
struct video_tuner *vt = arg; struct video_tuner *vt = arg;
CHECK_V4L2;
if (t->radio) if (t->radio)
vt->signal = tuner_signal(client); vt->signal = tuner_signal(client);
return 0; return 0;
...@@ -1165,10 +1228,52 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) ...@@ -1165,10 +1228,52 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
case VIDIOCGAUDIO: case VIDIOCGAUDIO:
{ {
struct video_audio *va = arg; struct video_audio *va = arg;
CHECK_V4L2;
if (t->radio) if (t->radio)
va->mode = (tuner_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO); va->mode = (tuner_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO);
return 0; return 0;
} }
case VIDIOC_S_STD:
{
v4l2_std_id *id = arg;
SWITCH_V4L2;
t->radio = 0;
t->std = *id;
tuner_fixup_std(t);
if (t->freq)
set_freq(client,t->freq);
break;
}
case VIDIOC_S_FREQUENCY:
{
struct v4l2_frequency *f = arg;
SWITCH_V4L2;
if (V4L2_TUNER_ANALOG_TV == f->type) {
t->radio = 0;
}
if (V4L2_TUNER_RADIO == f->type) {
if (!t->radio) {
set_tv_freq(client,400*16);
t->radio = 1;
}
}
t->freq = f->frequency;
set_freq(client,t->freq);
break;
}
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *tuner = arg;
SWITCH_V4L2;
if (t->radio)
tuner->signal = tuner_signal(client);
break;
}
default: default:
/* nothing */ /* nothing */
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment