📄 tuner.c
字号:
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))) 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){ struct tunertype *tun; struct tuner *t = i2c_get_clientdata(c); unsigned char buffer[4]; unsigned div; int rc; tun=&tuners[t->type]; div = freq + (int)(16*10.7); 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; default: 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]); if (4 != (rc = i2c_master_send(c,buffer,4))) printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc);}/* ---------------------------------------------------------------------- */// Set tuner frequency, freq in Units of 62.5kHz = 1/16MHzstatic void set_tv_freq(struct i2c_client *c, unsigned int freq){ struct tuner *t = i2c_get_clientdata(c); if (t->type == UNSET) { printk("tuner: tuner type not set\n"); return; } if (NULL == t->tv_freq) { printk("tuner: Huh? tv_set is NULL?\n"); return; } if (freq < tv_range[0]*16 || freq > tv_range[1]*16) { /* FIXME: better do that chip-specific, but right now we don't have that in the config struct and this way is still better than no check at all */ printk("tuner: TV freq (%d.%02d) out of range (%d-%d)\n", freq/16,freq%16*100/16,tv_range[0],tv_range[1]); return; } t->tv_freq(c,freq);}static void set_radio_freq(struct i2c_client *c, unsigned int freq){ struct tuner *t = i2c_get_clientdata(c); if (t->type == UNSET) { printk("tuner: tuner type not set\n"); return; } if (NULL == t->radio_freq) { printk("tuner: no radio tuning for this one, sorry.\n"); return; } if (freq < radio_range[0]*16 || freq > radio_range[1]*16) { printk("tuner: radio freq (%d.%02d) out of range (%d-%d)\n", freq/16,freq%16*100/16, radio_range[0],radio_range[1]); return; } t->radio_freq(c,freq);}static void set_freq(struct i2c_client *c, unsigned long freq){ struct tuner *t = i2c_get_clientdata(c); 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); 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;}static void set_type(struct i2c_client *c, unsigned int type, char *source){ struct tuner *t = i2c_get_clientdata(c); 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); return; } if (type >= TUNERS) return; t->type = type; printk("tuner: type set to %d (%s) by %s\n", t->type,tuners[t->type].name, source); strlcpy(c->name, tuners[t->type].name, sizeof(c->name)); switch (t->type) { case TUNER_MT2032: microtune_init(c); break; default: t->tv_freq = default_set_tv_freq; t->radio_freq = default_set_radio_freq; break; }}static char pal[] = "-";module_param_string(pal, pal, 0644, sizeof(pal));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){ struct tuner *t; struct i2c_client *client; if (this_adap > 0) return -1; this_adap++; client_template.adapter = adap; client_template.addr = addr; printk("tuner: chip found at addr 0x%x i2c-bus %s\n", addr<<1, adap->name); if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) return -ENOMEM; memcpy(client,&client_template,sizeof(struct i2c_client)); t = kmalloc(sizeof(struct tuner),GFP_KERNEL); if (NULL == t) { kfree(client); return -ENOMEM; } memset(t,0,sizeof(struct tuner)); i2c_set_clientdata(client, t); t->type = UNSET; t->radio_if2 = 10700*1000; // 10.7MHz - FM radio i2c_attach_client(client); if (type < TUNERS) { set_type(client, type, "insmod option"); 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: tv aard core driver (bttv, saa7134, ...) instead.\n"); } return 0;}static int tuner_probe(struct i2c_adapter *adap){ if (0 != addr) { normal_i2c_range[0] = addr; normal_i2c_range[1] = addr; } this_adap = 0;#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, tuner_attach);#else switch (adap->id) { case I2C_ALGO_BIT | I2C_HW_SMBUS_VOODOO3: case I2C_ALGO_BIT | I2C_HW_B_BT848: case I2C_ALGO_BIT | I2C_HW_B_RIVA: case I2C_ALGO_SAA7134: case I2C_ALGO_SAA7146: return i2c_probe(adap, &addr_data, tuner_attach); break; }#endif return 0;}static int tuner_detach(struct i2c_client *client){ struct tuner *t = i2c_get_clientdata(client); i2c_detach_client(client); kfree(t); kfree(client); 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 inttuner_command(struct i2c_client *client, unsigned int cmd, void *arg){ struct tuner *t = i2c_get_clientdata(client); unsigned int *iarg = (int*)arg; switch (cmd) { /* --- configuration --- */ case TUNER_SET_TYPE: set_type(client,*iarg,client->adapter->name); break; case AUDC_SET_RADIO: if (V4L2_TUNER_RADIO != t->mode) { set_tv_freq(client,400 * 16); t->mode = V4L2_TUNER_RADIO; } break; case AUDC_CONFIG_PINNACLE: switch (*iarg) { case 2: dprintk("tuner: pinnacle pal\n"); t->radio_if2 = 33300 * 1000; break; case 3: dprintk("tuner: pinnacle ntsc\n"); t->radio_if2 = 41300 * 1000; break; } break; /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a 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; CHECK_V4L2; t->mode = V4L2_TUNER_ANALOG_TV; if (vc->norm < ARRAY_SIZE(map)) t->std = map[vc->norm]; tuner_fixup_std(t); if (t->freq) set_tv_freq(client,t->freq); return 0; } case VIDIOCSFREQ: { unsigned long *v = arg; CHECK_V4L2; set_freq(client,*v); return 0; } case VIDIOCGTUNER: { struct video_tuner *vt = arg; CHECK_V4L2; if (V4L2_TUNER_RADIO == t->mode) vt->signal = tuner_signal(client); return 0; } case VIDIOCGAUDIO: { struct video_audio *va = arg; CHECK_V4L2; if (V4L2_TUNER_RADIO == t->mode) va->mode = (tuner_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO); return 0; } case VIDIOC_S_STD: { v4l2_std_id *id = arg; SWITCH_V4L2; t->mode = V4L2_TUNER_ANALOG_TV; 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_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; } case VIDIOC_G_TUNER: { struct v4l2_tuner *tuner = arg; SWITCH_V4L2; if (V4L2_TUNER_RADIO == t->mode) tuner->signal = tuner_signal(client); break; } default: /* 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;}/* ----------------------------------------------------------------------- */static struct i2c_driver driver = { .owner = THIS_MODULE, .name = "i2c TV tuner driver", .id = I2C_DRIVERID_TUNER, .flags = I2C_DF_NOTIFY, .attach_adapter = tuner_probe, .detach_client = tuner_detach, .command = tuner_command, .driver = { .suspend = tuner_suspend, .resume = tuner_resume, },};static struct i2c_client client_template ={ I2C_DEVNAME("(tuner unset)"), .flags = I2C_CLIENT_ALLOW_USE, .driver = &driver,};static int __init tuner_init_module(void){ return i2c_add_driver(&driver);}static void __exit tuner_cleanup_module(void){ i2c_del_driver(&driver);}module_init(tuner_init_module);module_exit(tuner_cleanup_module);/* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -