diff options
Diffstat (limited to 'drivers/char/tuner.c')
-rw-r--r-- | drivers/char/tuner.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/drivers/char/tuner.c b/drivers/char/tuner.c new file mode 100644 index 000000000..a942985c2 --- /dev/null +++ b/drivers/char/tuner.c @@ -0,0 +1,269 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/malloc.h> + +#include "i2c.h" +#include <linux/videodev.h> + +#include "tuner.h" + +int debug = 0; /* insmod parameter */ +int type = 0; /* tuner type */ + +#define dprintk if (debug) printk + +MODULE_PARM(debug,"i"); +MODULE_PARM(type,"i"); + +struct tuner +{ + struct i2c_bus *bus; /* where is our chip */ + int addr; + + int type; /* chip type */ + int freq; /* keep track of the current settings */ + int radio; +}; + +/* ---------------------------------------------------------------------- */ + +struct tunertype +{ + char *name; + unsigned char Vendor; + unsigned char Type; + + unsigned short thresh1; /* frequency Range for UHF,VHF-L, VHF_H */ + unsigned short thresh2; + unsigned char VHF_L; + unsigned char VHF_H; + unsigned char UHF; + unsigned char config; + unsigned char I2C; + unsigned short IFPCoff; +}; + +/* + * The floats in the tuner struct are computed at compile time + * by gcc and cast back to integers. Thus we don't violate the + * "no float in kernel" rule. + */ +static struct tunertype tuners[] = { + {"Temic PAL", TEMIC, PAL, + 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,623}, + {"Philips PAL_I", Philips, PAL_I, + 16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc0,623}, + {"Philips NTSC", Philips, NTSC, + 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0,732}, + {"Philips SECAM", Philips, SECAM, + 16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,0xc0,623}, + {"NoTuner", NoTuner, NOTUNER, + 0 ,0 ,0x00,0x00,0x00,0x00,0x00,000}, + {"Philips PAL", Philips, PAL, + 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0,623}, + {"Temic NTSC", TEMIC, NTSC, + 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,732}, + {"TEMIC PAL_I", TEMIC, PAL_I, + 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623}, +}; + +/* ---------------------------------------------------------------------- */ + +static int tuner_getstatus (struct tuner *t) +{ + return i2c_read(t->bus,t->addr+1); +} + +#define TUNER_POR 0x80 +#define TUNER_FL 0x40 +#define TUNER_AFC 0x07 + +static int tuner_islocked (struct tuner *t) +{ + return (tuner_getstatus (t) & TUNER_FL); +} + +static int tuner_afcstatus (struct tuner *t) +{ + return (tuner_getstatus (t) & TUNER_AFC) - 2; +} + + +static void set_tv_freq(struct tuner *t, int freq) +{ + unsigned long flags; + u8 config; + u16 div; + struct tunertype *tun=&tuners[t->type]; + + if (freq < tun->thresh1) + config = tun->VHF_L; + else if (freq < tun->thresh2) + config = tun->VHF_H; + else + config = tun->UHF; + + div=freq + (int)(16*38.9); + div&=0x7fff; + + LOCK_I2C_BUS(t->bus); + if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) { + printk("tuner: i2c i/o error #1\n"); + } else { + if (i2c_write(t->bus, t->addr, tun->config, config, 1)) + printk("tuner: i2c i/o error #2\n"); + } + UNLOCK_I2C_BUS(t->bus); +} + +static void set_radio_freq(struct tuner *t, int freq) +{ + unsigned long flags; + u8 config; + u16 div; + struct tunertype *tun=&tuners[type]; + + config = 0xa5; + div=freq + (int)(16*10.7); + div&=0x7fff; + + LOCK_I2C_BUS(t->bus); + if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) { + printk("tuner: i2c i/o error #1\n"); + } else { + if (i2c_write(t->bus, t->addr, tun->config, config, 1)) + printk("tuner: i2c i/o error #2\n"); + } + if (debug) { + UNLOCK_I2C_BUS(t->bus); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ/10; + schedule(); + LOCK_I2C_BUS(t->bus); + + if (tuner_islocked (t)) + printk ("tuner: PLL locked\n"); + else + printk ("tuner: PLL not locked\n"); + + printk ("tuner: AFC: %d\n", tuner_afcstatus (t)); + } + UNLOCK_I2C_BUS(t->bus); +} + +/* ---------------------------------------------------------------------- */ + +static int tuner_attach(struct i2c_device *device) +{ + struct tuner *t; + + /* + * For now we only try and attach these tuners to the BT848 + * bus. This same module will however work different species + * of card using these chips. Just change the constraints + * (i2c doesn't have a totally clash free 'address' space) + */ + + if(device->bus->id!=I2C_BUSID_BT848) + return -EINVAL; + + device->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL); + if (NULL == t) + return -ENOMEM; + memset(t,0,sizeof(struct tuner)); + strcpy(device->name,"tuner"); + t->bus = device->bus; + t->addr = device->addr; + t->type = type; + dprintk("tuner: type is %d (%s)\n",t->type,tuners[t->type].name); + + MOD_INC_USE_COUNT; + return 0; +} + +static int tuner_detach(struct i2c_device *device) +{ + struct tuner *t = (struct tuner*)device->data; + kfree(t); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tuner_command(struct i2c_device *device, + unsigned int cmd, void *arg) +{ + struct tuner *t = (struct tuner*)device->data; + int *iarg = (int*)arg; + + switch (cmd) + { + case TUNER_SET_TYPE: + t->type = *iarg; + dprintk("tuner: type set to %d (%s)\n", + t->type,tuners[t->type].name); + break; + + case TUNER_SET_TVFREQ: + dprintk("tuner: tv freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_tv_freq(t,*iarg); + t->radio = 0; + t->freq = *iarg; + break; + + case TUNER_SET_RADIOFREQ: + dprintk("tuner: radio freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_radio_freq(t,*iarg); + t->radio = 1; + t->freq = *iarg; + break; + + default: + return -EINVAL; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +struct i2c_driver i2c_driver_tuner = +{ + "tuner", /* name */ + I2C_DRIVERID_TUNER, /* ID */ + 0xc0, 0xce, /* addr range */ + + tuner_attach, + tuner_detach, + tuner_command +}; + +#ifdef MODULE +int init_module(void) +#else +int msp3400c_init(void) +#endif +{ + i2c_register_driver(&i2c_driver_tuner); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_unregister_driver(&i2c_driver_tuner); +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ |