patch-2.1.94 linux/drivers/char/tuner.c

Next file: linux/drivers/char/tuner.h
Previous file: linux/drivers/char/pty.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.93/linux/drivers/char/tuner.c linux/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:
+ */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov