vserver 1.9.5.x5
[linux-2.6.git] / drivers / media / video / tuner.c
index d1150b7..67181ea 100644 (file)
@@ -1,4 +1,9 @@
+/*
+ * $Id: tuner.c,v 1.36 2005/01/14 13:29:40 kraxel Exp $
+ */
+
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <media/tuner.h>
 #include <media/audiochip.h>
 
-/* 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;
-static char *pal =  "b";
+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 };
-MODULE_PARM(debug,"i");
-MODULE_PARM(type,"i");
-MODULE_PARM(addr,"i");
-MODULE_PARM(tv_range,"2i");
-MODULE_PARM(radio_range,"2i");
-MODULE_PARM(pal,"s");
 
-#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");
@@ -48,12 +59,12 @@ static int this_adap;
 struct tuner {
        unsigned int type;            /* chip type */
        unsigned int freq;            /* keep track of the current settings */
-       unsigned int std;
-       
-       unsigned int radio;
-       unsigned int mode;            /* current norm for multi-norm tuners */
+       v4l2_std_id  std;
+       int          using_v4l2;
+
+       enum v4l2_tuner_type mode;
        unsigned int input;
-       
+
        // only for MT2032
        unsigned int xogc;
        unsigned int radio_if2;
@@ -69,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
@@ -95,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",
@@ -113,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 */
 };
 
 /*
@@ -168,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},
 
@@ -206,7 +218,7 @@ static struct tunertype tuners[] = {
        { "Temic PAL* auto + FM (4009 FN5)", TEMIC, PAL,
          16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623},
        { "SHARP NTSC_JP (2U5JF5540)", SHARP, NTSC, /* 940=16*58.75 NTSC@Japan */
-         16*137.25,16*317.25,0x01,0x02,0x08,0x8e,732 }, // Corrected to NTSC=732 (was:940)
+         16*137.25,16*317.25,0x01,0x02,0x08,0x8e,940 },
 
        { "Samsung PAL TCPM9091PD27", Samsung, PAL,  /* from sourceforge v3tv */
           16*169,16*464,0xA0,0x90,0x30,0x8e,623},
@@ -227,7 +239,7 @@ static struct tunertype tuners[] = {
           16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,732},
 
        { "HITACHI V7-J180AT", HITACHI, NTSC,
-         16*170.00, 16*450.00, 0x01,0x02,0x00,0x8e,940 },
+         16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,940 },
        { "Philips PAL_MK (FI1216 MK)", Philips, PAL,
          16*140.25,16*463.25,0x01,0xc2,0xcf,0x8e,623},
        { "Philips 1236D ATSC/NTSC daul in",Philips,ATSC,
@@ -239,6 +251,24 @@ static struct tunertype tuners[] = {
           16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732},
        { "Microtune 4049 FM5",Microtune,PAL,
          16*141.00,16*464.00,0xa0,0x90,0x30,0x8e,623},
+       { "Panasonic VP27s/ENGE4324D", Panasonic, NTSC,
+         16*160.00,16*454.00,0x01,0x02,0x08,0xce,940},
+        { "LG NTSC (TAPE series)", LGINNOTEK, NTSC,
+          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)
@@ -407,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
@@ -428,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);
        }
@@ -453,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--;
        }
@@ -506,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);
@@ -537,15 +567,16 @@ static void mt2032_set_tv_freq(struct i2c_client *c, unsigned int freq)
        int if2,from,to;
 
        // signal bandwidth and picture carrier
-       if (t->mode == VIDEO_MODE_NTSC) {
-               from=40750*1000;
-               to=46750*1000;
-               if2=45750*1000; 
+       if (t->std & V4L2_STD_525_60) {
+               // NTSC
+               from = 40750*1000;
+               to   = 46750*1000;
+               if2  = 45750*1000;
        } else {
-               // Pal 
-               from=32900*1000;
-               to=39900*1000;
-               if2=38900*1000;
+               // PAL
+               from = 32900*1000;
+               to   = 39900*1000;
+               if2  = 38900*1000;
        }
 
         mt2032_set_if_freq(c, freq*62500 /* freq*1000*1000/16 */,
@@ -619,60 +650,73 @@ static int mt2032_init(struct i2c_client *c)
         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)
 {
        unsigned int if1=1218*1000*1000;
        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);
@@ -682,22 +726,29 @@ 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->mode == VIDEO_MODE_NTSC) {
-                if2=45750*1000;
+
+       if (t->std & V4L2_STD_525_60) {
+               // NTSC
+                if2 = 45750*1000;
         } else {
-                // Pal
-                if2=38900*1000;
+                // PAL
+                if2 = 38900*1000;
         }
-       mt2050_set_if_freq(c,freq*62500,if2);
+       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);
 }
 
 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);
 }
 
 static int mt2050_init(struct i2c_client *c)
@@ -705,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;
@@ -730,9 +781,9 @@ static int microtune_init(struct i2c_client *c)
        char *name;
         unsigned char buf[21];
        int company_code;
-       
-        buf[0] = 0;
-       t->tv_freq = NULL;
+
+       memset(buf,0,sizeof(buf));
+       t->tv_freq    = NULL;
        t->radio_freq = NULL;
        name = "unknown";
 
@@ -751,6 +802,9 @@ static int microtune_init(struct i2c_client *c)
        company_code = buf[0x11] << 8 | buf[0x12];
         printk("tuner: microtune: companycode=%04x part=%02x rev=%02x\n",
               company_code,buf[0x13],buf[0x14]);
+
+#if 0
+       /* seems to cause more problems than it solves ... */
        switch (company_code) {
        case 0x30bf:
        case 0x3cbf:
@@ -764,6 +818,7 @@ static int microtune_init(struct i2c_client *c)
                printk("tuner: microtune: unknown companycode\n");
                return 0;
        }
+#endif
 
        if (buf[0x13] < ARRAY_SIZE(microtune_part) &&
            NULL != microtune_part[buf[0x13]])
@@ -795,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 */
@@ -811,54 +870,40 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq)
                /* 0x02 -> PAL BDGHI / SECAM L */
                /* 0x04 -> ??? PAL others / SECAM others ??? */
                config &= ~0x02;
-               if (t->mode == VIDEO_MODE_SECAM)
+               if (t->std & V4L2_STD_SECAM)
                        config |= 0x02;
                break;
 
        case TUNER_TEMIC_4046FM5:
                config &= ~0x0f;
-               switch (pal[0]) {
-               case 'i':
-               case 'I':
+
+               if (t->std & V4L2_STD_PAL_BG) {
+                       config |= TEMIC_SET_PAL_BG;
+
+               } else if (t->std & V4L2_STD_PAL_I) {
                        config |= TEMIC_SET_PAL_I;
-                       break;
-               case 'd':
-               case 'D':
+
+               } else if (t->std & V4L2_STD_PAL_DK) {
                        config |= TEMIC_SET_PAL_DK;
-                       break;
-               case 'l':
-               case 'L':
+
+               } else if (t->std & V4L2_STD_SECAM_L) {
                        config |= TEMIC_SET_PAL_L;
-                       break;
-               case 'b':
-               case 'B':
-               case 'g':
-               case 'G':
-               default:
-                       config |= TEMIC_SET_PAL_BG;
-                       break;
+
                }
                break;
 
        case TUNER_PHILIPS_FQ1216ME:
                config &= ~0x0f;
-               switch (pal[0]) {
-               case 'i':
-               case 'I':
+
+               if (t->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) {
+                       config |= PHILIPS_SET_PAL_BGDK;
+
+               } else if (t->std & V4L2_STD_PAL_I) {
                        config |= PHILIPS_SET_PAL_I;
-                       break;
-               case 'l':
-               case 'L':
+
+               } else if (t->std & V4L2_STD_SECAM_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;
 
@@ -867,17 +912,18 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq)
                /* 0x01 -> ATSC antenna input 2 */
                /* 0x02 -> NTSC antenna input 1 */
                /* 0x03 -> NTSC antenna input 2 */
-               
                config &= ~0x03;
-#ifdef VIDEO_MODE_ATSC
-               if (VIDEO_MODE_ATSC != t->mode)
+               if (!(t->std & V4L2_STD_ATSC))
                        config |= 2;
-#endif
                /* 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
@@ -910,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)
@@ -922,18 +999,27 @@ 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;
        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]);
@@ -990,11 +1076,31 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
        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) {
+       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);
@@ -1019,6 +1125,38 @@ static void set_type(struct i2c_client *c, unsigned int type, char *source)
        }
 }
 
+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)
@@ -1029,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;
 
@@ -1054,7 +1192,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
                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 card core driver (bttv, saa7134, ...) instead.\n");
+               printk("tuner: tv aard core driver (bttv, saa7134, ...) instead.\n");
        }
        return 0;
 }
@@ -1067,8 +1205,8 @@ static int tuner_probe(struct i2c_adapter *adap)
        }
        this_adap = 0;
 
-#ifdef I2C_ADAP_CLASS_TV_ANALOG
-       if (adap->class & I2C_ADAP_CLASS_TV_ANALOG)
+#ifdef I2C_CLASS_TV_ANALOG
+       if (adap->class & I2C_CLASS_TV_ANALOG)
                return i2c_probe(adap, &addr_data, tuner_attach);
 #else
        switch (adap->id) {
@@ -1094,6 +1232,13 @@ static int tuner_detach(struct i2c_client *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 int
 tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
 {
@@ -1107,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:
@@ -1124,16 +1269,27 @@ 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... */
        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;
 
-               t->radio = 0;
-               t->mode = vc->norm;
+               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;
@@ -1142,38 +1298,86 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                unsigned long *v = arg;
 
-               if (t->radio) {
-                       dprintk("tuner: radio freq set to %lu.%02lu\n",
-                               (*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;
+               CHECK_V4L2;
+               set_freq(client,*v);
                return 0;
        }
        case VIDIOCGTUNER:
        {
                struct video_tuner *vt = arg;
 
-               if (t->radio)
+               CHECK_V4L2;
+               if (V4L2_TUNER_RADIO == t->mode)
                        vt->signal = tuner_signal(client);
                return 0;
        }
        case VIDIOCGAUDIO:
        {
                struct video_audio *va = arg;
-               if (t->radio)
+
+               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;
 }
 
@@ -1187,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 =
 {
@@ -1195,13 +1403,12 @@ static struct i2c_client client_template =
         .driver     = &driver,
 };
 
-static int tuner_init_module(void)
+static int __init tuner_init_module(void)
 {
-       i2c_add_driver(&driver);
-       return 0;
+       return i2c_add_driver(&driver);
 }
 
-static void tuner_cleanup_module(void)
+static void __exit tuner_cleanup_module(void)
 {
        i2c_del_driver(&driver);
 }