X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Ftuner.c;h=67181ea1b9f53ca9ae79c584364351f3f18e8847;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=bac17a3dc12e6abea3812261f850e9133139e4ca;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/media/video/tuner.c b/drivers/media/video/tuner.c index bac17a3dc..67181ea1b 100644 --- a/drivers/media/video/tuner.c +++ b/drivers/media/video/tuner.c @@ -1,4 +1,9 @@ +/* + * $Id: tuner.c,v 1.36 2005/01/14 13:29:40 kraxel Exp $ + */ + #include +#include #include #include #include @@ -15,30 +20,34 @@ #include #include -/* Addresses to scan */ +#define UNSET (-1U) + +/* standard i2c insmod options */ static unsigned short normal_i2c[] = {I2C_CLIENT_END}; static unsigned short normal_i2c_range[] = {0x60,0x6f,I2C_CLIENT_END}; I2C_CLIENT_INSMOD; -#define UNSET (-1U) - -/* insmod options */ -static unsigned int debug = 0; +/* insmod options used at init time => read/only */ static unsigned int type = UNSET; static unsigned int addr = 0; +module_param(type, int, 0444); +module_param(addr, int, 0444); + +/* insmod options used at runtime => read/write */ +static unsigned int debug = 0; +static unsigned int tv_antenna = 1; +static unsigned int radio_antenna = 0; +static unsigned int optimize_vco = 1; +module_param(debug, int, 0644); +module_param(tv_antenna, int, 0644); +module_param(radio_antenna, int, 0644); +module_param(optimize_vco, int, 0644); + static unsigned int tv_range[2] = { 44, 958 }; 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(type,"i"); -MODULE_PARM(addr,"i"); -MODULE_PARM(tv_range,"2i"); -MODULE_PARM(radio_range,"2i"); -MODULE_PARM(tv_antenna,"i"); -MODULE_PARM(radio_antenna,"i"); -#define optimize_vco 1 +module_param_array(tv_range, int, NULL, 0644); +module_param_array(radio_range, int, NULL, 0644); MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners"); MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); @@ -52,10 +61,10 @@ struct tuner { unsigned int freq; /* keep track of the current settings */ v4l2_std_id std; int using_v4l2; - - unsigned int radio; + + enum v4l2_tuner_type mode; unsigned int input; - + // only for MT2032 unsigned int xogc; unsigned int radio_if2; @@ -71,11 +80,11 @@ static struct i2c_client client_template; /* tv standard selection for Temic 4046 FM5 this value takes the low bits of control byte 2 - from datasheet Rev.01, Feb.00 + from datasheet Rev.01, Feb.00 standard BG I L L2 D picture IF 38.9 38.9 38.9 33.95 38.9 sound 1 33.4 32.9 32.4 40.45 32.4 - sound 2 33.16 + sound 2 33.16 NICAM 33.05 32.348 33.05 33.05 */ #define TEMIC_SET_PAL_I 0x05 @@ -97,7 +106,7 @@ static struct i2c_client client_template; #define PHILIPS_SET_PAL_I 0x01 /* Bit 2 always zero !*/ #define PHILIPS_SET_PAL_BGDK 0x09 #define PHILIPS_SET_PAL_L2 0x0a -#define PHILIPS_SET_PAL_L 0x0b +#define PHILIPS_SET_PAL_L 0x0b /* system switching for Philips FI1216MF MK2 from datasheet "1996 Jul 09", @@ -115,21 +124,22 @@ static struct i2c_client client_template; /* ---------------------------------------------------------------------- */ -struct tunertype +struct tunertype { char *name; unsigned char Vendor; unsigned char Type; - + unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */ unsigned short thresh2; /* band switch VHF_HI <=> UHF */ unsigned char VHF_L; unsigned char VHF_H; unsigned char UHF; - unsigned char config; - unsigned short IFPCoff; /* 622.4=16*38.90 MHz PAL, - 732 =16*45.75 NTSCi, - 940 =58.75 NTSC-Japan */ + unsigned char config; + unsigned short IFPCoff; /* 622.4=16*38.90 MHz PAL, + 732 =16*45.75 NTSCi, + 940 =16*58.75 NTSC-Japan + 704 =16*44 ATSC */ }; /* @@ -170,7 +180,7 @@ static struct tunertype tuners[] = { { "Alps TSBC5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */ 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,608}, { "Temic PAL_BG (4006FH5)", TEMIC, PAL, - 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, + 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, { "Alps TSCH6",Alps,NTSC, 16*137.25,16*385.25,0x14,0x12,0x11,0x8e,732}, @@ -244,10 +254,21 @@ static struct tunertype tuners[] = { { "Panasonic VP27s/ENGE4324D", Panasonic, NTSC, 16*160.00,16*454.00,0x01,0x02,0x08,0xce,940}, { "LG NTSC (TAPE series)", LGINNOTEK, NTSC, - 16*170.00, 16*450.00, 0x01,0x02,0x04,0x8e,732 }, + 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 }, { "Tenna TNF 8831 BGFF)", Philips, PAL, 16*161.25,16*463.25,0xa0,0x90,0x30,0x8e,623}, + { "Microtune 4042 FI5 ATSC/NTSC dual in", Microtune, NTSC, + 16*162.00,16*457.00,0xa2,0x94,0x31,0x8e,732}, + { "TCL 2002N", TCL, NTSC, + 16*172.00,16*448.00,0x01,0x02,0x08,0x8e,732}, + { "Philips PAL/SECAM_D (FM 1256 I-H3)", Philips, PAL, + 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,623 }, + + { "Thomson DDT 7610 ATSC/NTSC)", THOMSON, ATSC, + 16*157.25,16*454.00,0x39,0x3a,0x3c,0x8e,732}, + { "Philips FQ1286", Philips, NTSC, + 16*160.00,16*454.00,0x41,0x42,0x04,0x8e,940}, // UHF band untested }; #define TUNERS ARRAY_SIZE(tuners) @@ -416,7 +437,7 @@ static int mt2032_compute_freq(unsigned int rfin, if(rfin >400*1000*1000) buf[6]=0xe4; else - buf[6]=0xf4; // set PKEN per rev 1.2 + buf[6]=0xf4; // set PKEN per rev 1.2 buf[7]=8+xogc; buf[8]=0xc3; //reserved buf[9]=0x4e; //reserved @@ -437,10 +458,10 @@ static int mt2032_check_lo_lock(struct i2c_client *c) i2c_master_recv(c,buf,1); dprintk("mt2032 Reg.E=0x%02x\n",buf[0]); lock=buf[0] &0x06; - + if (lock==6) break; - + dprintk("mt2032: pll wait 1ms for lock (0x%2x)\n",buf[0]); udelay(1000); } @@ -462,7 +483,7 @@ static int mt2032_optimize_vco(struct i2c_client *c,int sel,int lock) if(tad1 ==1) return lock; if(tad1==2) { - if(sel==0) + if(sel==0) return lock; else sel--; } @@ -515,13 +536,13 @@ static void mt2032_set_if_freq(struct i2c_client *c, unsigned int rfin, // wait for PLLs to lock (per manual), retry LINT if not. for(lint_try=0; lint_try<2; lint_try++) { lock=mt2032_check_lo_lock(c); - + if(optimize_vco) lock=mt2032_optimize_vco(c,sel,lock); if(lock==6) break; - - printk("mt2032: re-init PLLs by LINT\n"); - buf[0]=7; + + printk("mt2032: re-init PLLs by LINT\n"); + buf[0]=7; buf[1]=0x80 +8+t->xogc; // set LINT to re-init PLLs i2c_master_send(c,buf,2); mdelay(10); @@ -646,54 +667,56 @@ static void mt2050_set_if_freq(struct i2c_client *c,unsigned int freq, unsigned unsigned int f_lo1,f_lo2,lo1,lo2,f_lo1_modulo,f_lo2_modulo,num1,num2,div1a,div1b,div2a,div2b; int ret; unsigned char buf[6]; - - dprintk("mt2050_set_if_freq freq=%d\n",freq); - + + dprintk("mt2050_set_if_freq freq=%d if1=%d if2=%d\n", + freq,if1,if2); + f_lo1=freq+if1; f_lo1=(f_lo1/1000000)*1000000; - + f_lo2=f_lo1-freq-if2; f_lo2=(f_lo2/50000)*50000; - + lo1=f_lo1/4000000; lo2=f_lo2/4000000; - + f_lo1_modulo= f_lo1-(lo1*4000000); f_lo2_modulo= f_lo2-(lo2*4000000); - + num1=4*f_lo1_modulo/4000000; num2=4096*(f_lo2_modulo/1000)/4000; - + // todo spurchecks - + div1a=(lo1/12)-1; div1b=lo1-(div1a+1)*12; - + div2a=(lo2/8)-1; div2b=lo2-(div2a+1)*8; - - dprintk("lo1 lo2 = %d %d\n", lo1, lo2); - dprintk("num1 num2 div1a div1b div2a div2b= %x %x %x %x %x %x\n",num1,num2,div1a,div1b,div2a,div2b); - - + + if (debug > 1) { + printk("lo1 lo2 = %d %d\n", lo1, lo2); + printk("num1 num2 div1a div1b div2a div2b= %x %x %x %x %x %x\n",num1,num2,div1a,div1b,div2a,div2b); + } + buf[0]=1; buf[1]= 4*div1b + num1; if(freq<275*1000*1000) buf[1] = buf[1]|0x80; - + buf[2]=div1a; buf[3]=32*div2b + num2/256; buf[4]=num2-(num2/256)*256; buf[5]=div2a; if(num2!=0) buf[5]=buf[5]|0x40; - - if(debug) { + + if (debug > 1) { int i; printk("bufs is: "); for(i=0;i<6;i++) printk("%x ",buf[i]); printk("\n"); } - + ret=i2c_master_send(c,buf,6); if (ret!=6) printk("mt2050_set_if_freq failed with %d\n",ret); @@ -703,7 +726,7 @@ static void mt2050_set_tv_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = i2c_get_clientdata(c); unsigned int if2; - + if (t->std & V4L2_STD_525_60) { // NTSC if2 = 45750*1000; @@ -711,6 +734,10 @@ static void mt2050_set_tv_freq(struct i2c_client *c, unsigned int freq) // PAL if2 = 38900*1000; } + if (V4L2_TUNER_DIGITAL_TV == t->mode) { + // testing for DVB ... + if2 = 36150*1000; + } mt2050_set_if_freq(c, freq*62500, if2); mt2050_set_antenna(c, tv_antenna); } @@ -719,7 +746,7 @@ static void mt2050_set_radio_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = i2c_get_clientdata(c); int if2 = t->radio_if2; - + mt2050_set_if_freq(c, freq*62500, if2); mt2050_set_antenna(c, radio_antenna); } @@ -729,19 +756,19 @@ static int mt2050_init(struct i2c_client *c) struct tuner *t = i2c_get_clientdata(c); unsigned char buf[2]; int ret; - + buf[0]=6; buf[1]=0x10; ret=i2c_master_send(c,buf,2); // power - + buf[0]=0x0f; buf[1]=0x0f; ret=i2c_master_send(c,buf,2); // m1lo - + buf[0]=0x0d; ret=i2c_master_send(c,buf,1); i2c_master_recv(c,buf,1); - + dprintk("mt2050: sro is %x\n",buf[0]); t->tv_freq = mt2050_set_tv_freq; t->radio_freq = mt2050_set_radio_freq; @@ -754,7 +781,7 @@ static int microtune_init(struct i2c_client *c) char *name; unsigned char buf[21]; int company_code; - + memset(buf,0,sizeof(buf)); t->tv_freq = NULL; t->radio_freq = NULL; @@ -823,13 +850,17 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) unsigned char buffer[4]; int rc; - tun=&tuners[t->type]; - if (freq < tun->thresh1) + tun = &tuners[t->type]; + if (freq < tun->thresh1) { config = tun->VHF_L; - else if (freq < tun->thresh2) + dprintk("tv: VHF lowrange\n"); + } else if (freq < tun->thresh2) { config = tun->VHF_H; - else + dprintk("tv: VHF high range\n"); + } else { config = tun->UHF; + dprintk("tv: UHF range\n"); + } /* tv norm specific stuff for multi-norm tuners */ @@ -882,13 +913,17 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) /* 0x02 -> NTSC antenna input 1 */ /* 0x03 -> NTSC antenna input 2 */ config &= ~0x03; - if (t->std & V4L2_STD_ATSC) + if (!(t->std & V4L2_STD_ATSC)) config |= 2; /* FIXME: input */ break; + + case TUNER_MICROTUNE_4042FI5: + /* Set the charge pump for fast tuning */ + tun->config |= 0x40; + break; } - /* * Philips FI1216MK2 remark from specification : * for channel selection involving band switching, and to ensure @@ -921,6 +956,37 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) if (4 != (rc = i2c_master_send(c,buffer,4))) printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc); + if (t->type == TUNER_MICROTUNE_4042FI5) { + // FIXME - this may also work for other tuners + unsigned long timeout = jiffies + msecs_to_jiffies(1); + u8 status_byte = 0; + + /* Wait until the PLL locks */ + for (;;) { + if (time_after(jiffies,timeout)) + return; + if (1 != (rc = i2c_master_recv(c,&status_byte,1))) { + dprintk("tuner: i2c i/o read error: rc == %d (should be 1)\n",rc); + break; + } + /* bit 6 is PLL locked indicator */ + if (status_byte & 0x40) + break; + udelay(10); + } + + /* Set the charge pump for optimized phase noise figure */ + tun->config &= ~0x40; + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + buffer[2] = tun->config; + buffer[3] = config; + dprintk("tuner: tv 0x%02x 0x%02x 0x%02x 0x%02x\n", + buffer[0],buffer[1],buffer[2],buffer[3]); + + if (4 != (rc = i2c_master_send(c,buffer,4))) + dprintk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc); + } } static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) @@ -933,14 +999,18 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) tun=&tuners[t->type]; div = freq + (int)(16*10.7); - buffer[0] = (div>>8) & 0x7f; - buffer[1] = div & 0xff; buffer[2] = tun->config; + switch (t->type) { case TUNER_PHILIPS_FM1216ME_MK3: case TUNER_PHILIPS_FM1236_MK3: buffer[3] = 0x19; break; + case TUNER_PHILIPS_FM1256_IH3: + div = (20 * freq)/16 + 333 * 2; + buffer[2] = 0x80; + buffer[3] = 0x19; + break; case TUNER_LG_PAL_FM: buffer[3] = 0xa5; break; @@ -948,6 +1018,8 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) buffer[3] = 0xa4; break; } + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; dprintk("tuner: radio 0x%02x 0x%02x 0x%02x 0x%02x\n", buffer[0],buffer[1],buffer[2],buffer[3]); @@ -1008,14 +1080,18 @@ static void set_freq(struct i2c_client *c, unsigned long freq) { struct tuner *t = i2c_get_clientdata(c); - if (t->radio) { + switch (t->mode) { + case V4L2_TUNER_RADIO: dprintk("tuner: radio freq set to %lu.%02lu\n", freq/16,freq%16*100/16); set_radio_freq(c,freq); - } else { + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: dprintk("tuner: tv freq set to %lu.%02lu\n", freq/16,freq%16*100/16); set_tv_freq(c, freq); + break; } t->freq = freq; } @@ -1024,7 +1100,7 @@ static void set_type(struct i2c_client *c, unsigned int type, char *source) { struct tuner *t = i2c_get_clientdata(c); - if (t->type != UNSET) { + if (t->type != UNSET && t->type != TUNER_ABSENT) { if (t->type != type) printk("tuner: type already set to %d, " "ignoring request for %d\n", t->type, type); @@ -1049,8 +1125,8 @@ static void set_type(struct i2c_client *c, unsigned int type, char *source) } } -static char *pal = "-"; -MODULE_PARM(pal,"s"); +static char pal[] = "-"; +module_param_string(pal, pal, 0644, sizeof(pal)); static int tuner_fixup_std(struct tuner *t) { @@ -1091,7 +1167,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) if (this_adap > 0) return -1; this_adap++; - + client_template.adapter = adap; client_template.addr = addr; @@ -1176,9 +1252,9 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) set_type(client,*iarg,client->adapter->name); break; case AUDC_SET_RADIO: - if (!t->radio) { + if (V4L2_TUNER_RADIO != t->mode) { set_tv_freq(client,400 * 16); - t->radio = 1; + t->mode = V4L2_TUNER_RADIO; } break; case AUDC_CONFIG_PINNACLE: @@ -1193,7 +1269,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) break; } break; - + /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ @@ -1210,7 +1286,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) struct video_channel *vc = arg; CHECK_V4L2; - t->radio = 0; + t->mode = V4L2_TUNER_ANALOG_TV; if (vc->norm < ARRAY_SIZE(map)) t->std = map[vc->norm]; tuner_fixup_std(t); @@ -1231,7 +1307,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) struct video_tuner *vt = arg; CHECK_V4L2; - if (t->radio) + if (V4L2_TUNER_RADIO == t->mode) vt->signal = tuner_signal(client); return 0; } @@ -1240,7 +1316,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) struct video_audio *va = arg; CHECK_V4L2; - if (t->radio) + if (V4L2_TUNER_RADIO == t->mode) va->mode = (tuner_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO); return 0; } @@ -1250,7 +1326,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) v4l2_std_id *id = arg; SWITCH_V4L2; - t->radio = 0; + t->mode = V4L2_TUNER_ANALOG_TV; t->std = *id; tuner_fixup_std(t); if (t->freq) @@ -1262,15 +1338,10 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) 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; - } - } + if (V4L2_TUNER_RADIO == f->type && + V4L2_TUNER_RADIO != t->mode) + set_tv_freq(client,400*16); + t->mode = f->type; t->freq = f->frequency; set_freq(client,t->freq); break; @@ -1280,7 +1351,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) struct v4l2_tuner *tuner = arg; SWITCH_V4L2; - if (t->radio) + if (V4L2_TUNER_RADIO == t->mode) tuner->signal = tuner_signal(client); break; } @@ -1288,7 +1359,25 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) /* nothing */ break; } - + + return 0; +} + +static int tuner_suspend(struct device * dev, u32 state, u32 level) +{ + dprintk("tuner: suspend\n"); + /* FIXME: power down ??? */ + return 0; +} + +static int tuner_resume(struct device * dev, u32 level) +{ + struct i2c_client *c = container_of(dev, struct i2c_client, dev); + struct tuner *t = i2c_get_clientdata(c); + + dprintk("tuner: resume\n"); + if (t->freq) + set_freq(c,t->freq); return 0; } @@ -1302,6 +1391,10 @@ static struct i2c_driver driver = { .attach_adapter = tuner_probe, .detach_client = tuner_detach, .command = tuner_command, + .driver = { + .suspend = tuner_suspend, + .resume = tuner_resume, + }, }; static struct i2c_client client_template = {